Skip to content

Commit f827a01

Browse files
feat(zkgm): evm: introduce fungible asset order v2 (#4617)
## Features FungibleAssetOrderV2 introduces a flexible metadata architecture replacing fixed metadata fields with a dynamic type system. Key improvements include three-tier metadata types (IMAGE, PREIMAGE, IMAGE_UNWRAP), enhanced token addressing with metadata images, dual balance tracking for backward compatibility. ## Data Structure Evolution V1 uses explicit metadata fields (baseTokenSymbol, baseTokenName, baseTokenDecimals, baseTokenPath) while V2 replaces these with metadataType and metadata bytes for flexible handling. The evolution enables dynamic metadata strategies rather than fixed validation patterns. Example: create my own wrapper with a custom erc20 implementation/admin on the destination chain. ## Metadata Type System **FUNGIBLE_ASSET_METADATA_TYPE_IMAGE (0x00)**: Contains 32-byte metadata hash for existing tokens, enabling deterministic prediction without full metadata transmission. Triggers escrow-and-mint flow. **FUNGIBLE_ASSET_METADATA_TYPE_PREIMAGE (0x01)**: Contains full FungibleAssetMetadata struct for new token deployment. Triggers escrow-and-mint flow. **FUNGIBLE_ASSET_METADATA_TYPE_IMAGE_UNWRAP (0x02)**: Signals unwrapping operations with metadata hash validation, triggering burn-and-unescrow flow. ## Token Addressing Changes V1 uses simple deterministic addressing based on path, channel, and token hash. V2 enhances this by incorporating metadata images into salt calculation while maintaining V1 compatibility through FUNGIBLE_ASSET_METADATA_IMAGE_PREDICT_V1 magic constant detection. ## Balance Tracking V1 implements single-tier tracking with channelBalance mapping. V2 introduces dual-tier tracking maintaining both legacy V1 balance tracking and enhanced V2 tracking that includes metadata image in key structure, enabling precise balance management for tokens with identical addresses but different metadata. ## Compatibility ### Backward Compatibility Mechanisms Magic image support via FUNGIBLE_ASSET_METADATA_IMAGE_PREDICT_V1 enables seamless V1 handling. Dual balance systems maintain both V1 and V2 tracking. Legacy acknowledgments preserve V1-style market maker payments. Fallback prediction enables V1 logic for magic metadata images.
2 parents f24add3 + 86d8042 commit f827a01

File tree

22 files changed

+3734
-1311
lines changed

22 files changed

+3734
-1311
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cosmwasm/cw20-token-minter/src/contract.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,43 @@ pub fn execute(
9191

9292
let response = match msg {
9393
ExecuteMsg::Wrapped(msg) => match msg {
94+
WrappedTokenMsg::CreateDenomV2 {
95+
subdenom,
96+
path,
97+
channel_id,
98+
token,
99+
implementation,
100+
initializer,
101+
} => {
102+
let (admin, code_id) =
103+
<(String, u64)>::abi_decode_params_validate(implementation.as_ref()).unwrap();
104+
let metadata_image = calculate_metadata_image(implementation, initializer.clone());
105+
Response::new()
106+
.add_message(WasmMsg::Instantiate2 {
107+
admin: Some(env.contract.address.to_string()),
108+
code_id: config.dummy_code_id,
109+
label: subdenom.clone(),
110+
msg: to_json_binary(&cosmwasm_std::Empty {})?,
111+
funds: vec![],
112+
salt: Binary::new(calculate_salt_v2(
113+
U256::from_be_bytes::<{ U256::BYTES }>(
114+
path.as_slice().try_into().expect("correctly encoded; qed"),
115+
),
116+
channel_id,
117+
token.to_vec(),
118+
metadata_image,
119+
)),
120+
})
121+
.add_message(WasmMsg::Migrate {
122+
contract_addr: subdenom.clone(),
123+
new_code_id: code_id,
124+
msg: initializer,
125+
})
126+
.add_message(WasmMsg::UpdateAdmin {
127+
contract_addr: subdenom,
128+
admin,
129+
})
130+
}
94131
WrappedTokenMsg::CreateDenom {
95132
metadata,
96133
subdenom,
@@ -283,6 +320,29 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result<Binary, Error> {
283320
wrapped_token: deps.api.addr_humanize(&token_addr)?.to_string(),
284321
})?)
285322
}
323+
QueryMsg::PredictWrappedTokenV2 {
324+
path,
325+
channel_id,
326+
token,
327+
metadata_image,
328+
} => {
329+
let Config { dummy_code_id, .. } = CONFIG.load(deps.storage)?;
330+
let code_hash = get_code_hash(deps, dummy_code_id)?;
331+
let token_addr = instantiate2_address(
332+
&code_hash.into_bytes(),
333+
&deps.api.addr_canonicalize(env.contract.address.as_str())?,
334+
&calculate_salt_v2(
335+
path.parse::<U256>().map_err(Error::U256Parse)?,
336+
channel_id,
337+
token.to_vec(),
338+
metadata_image,
339+
),
340+
)?;
341+
342+
Ok(to_json_binary(&PredictWrappedTokenResponse {
343+
wrapped_token: deps.api.addr_humanize(&token_addr)?.to_string(),
344+
})?)
345+
}
286346
QueryMsg::Metadata { denom } => match query_token_info(deps, &denom) {
287347
Ok(TokenInfoResponse {
288348
name,
@@ -348,6 +408,29 @@ fn calculate_salt(path: U256, channel_id: ChannelId, token: Vec<u8>) -> Vec<u8>
348408
.to_vec()
349409
}
350410

411+
fn calculate_salt_v2(
412+
path: U256,
413+
channel_id: ChannelId,
414+
token: Vec<u8>,
415+
metadata_image: H256,
416+
) -> Vec<u8> {
417+
keccak256(
418+
(
419+
path,
420+
channel_id.raw(),
421+
token.to_vec(),
422+
U256::from_be_bytes(*metadata_image.get()),
423+
)
424+
.abi_encode_params(),
425+
)
426+
.into_bytes()
427+
.to_vec()
428+
}
429+
430+
fn calculate_metadata_image(implementation: Binary, initializer: Binary) -> H256 {
431+
keccak256((implementation.to_vec(), initializer.to_vec()).abi_encode_params())
432+
}
433+
351434
fn restrict_name(name: String) -> String {
352435
if name.len() > 50 {
353436
let name = &name[(name.len() - 50)..];

cosmwasm/ibc-union/app/ucs03-zkgm/src/com.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use alloy_primitives::U256;
2+
use unionlabs::primitives::H256;
23

34
pub const INSTR_VERSION_0: u8 = 0x00;
45
pub const INSTR_VERSION_1: u8 = 0x01;
6+
pub const INSTR_VERSION_2: u8 = 0x02;
57

68
pub const OP_FORWARD: u8 = 0x00;
79
pub const OP_MULTIPLEX: u8 = 0x01;
@@ -21,6 +23,15 @@ pub const TAG_ACK_SUCCESS: U256 = U256::from_be_slice(&[1]);
2123
pub const FILL_TYPE_PROTOCOL: U256 = U256::from_be_slice(&[0xB0, 0xCA, 0xD0]);
2224
pub const FILL_TYPE_MARKETMAKER: U256 = U256::from_be_slice(&[0xD1, 0xCE, 0xC4, 0x5E]);
2325

26+
pub const FUNGIBLE_ASSET_METADATA_TYPE_IMAGE: u8 = 0x00;
27+
pub const FUNGIBLE_ASSET_METADATA_TYPE_PREIMAGE: u8 = 0x01;
28+
pub const FUNGIBLE_ASSET_METADATA_TYPE_IMAGE_UNWRAP: u8 = 0x02;
29+
30+
pub const FUNGIBLE_ASSET_METADATA_IMAGE_PREDICT_V1: H256 = H256::new([
31+
0xC0, 0xDE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
32+
0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
33+
]);
34+
2435
pub const FORWARD_SALT_MAGIC: U256 = U256::from_be_slice(&[
2536
0xC0, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2637
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBA, 0xBE,
@@ -73,6 +84,24 @@ alloy_sol_types::sol! {
7384
uint256 quote_amount;
7485
}
7586

87+
#[derive(Debug, PartialEq)]
88+
struct FungibleAssetOrderV2 {
89+
bytes sender;
90+
bytes receiver;
91+
bytes base_token;
92+
uint256 base_amount;
93+
uint8 metadata_type;
94+
bytes metadata;
95+
bytes quote_token;
96+
uint256 quote_amount;
97+
}
98+
99+
#[derive(Debug, PartialEq)]
100+
struct FungibleAssetMetadata {
101+
bytes implementation;
102+
bytes initializer;
103+
}
104+
76105
#[derive(Debug, PartialEq)]
77106
struct Stake {
78107
uint256 token_id;

0 commit comments

Comments
 (0)