diff --git a/code/Cargo.lock b/code/Cargo.lock index 0d09d554612..5aa202017da 100644 --- a/code/Cargo.lock +++ b/code/Cargo.lock @@ -19230,6 +19230,7 @@ dependencies = [ "sha2 0.10.7", "strum 0.25.0", "thiserror-core", + "xcm", ] [[package]] diff --git a/code/xcvm/README.md b/code/xcvm/README.md index 8462fc886e8..e346a211faf 100644 --- a/code/xcvm/README.md +++ b/code/xcvm/README.md @@ -57,6 +57,22 @@ Can be considered as 3 layers, For each chain and protocol it makes pragmatics hacks to use existing liquidity and execution primitives. +### Examples + +#### Stake on Strides + +Program to `Stake` on Stride and transfer staked token to Osmosis +is detected as pattern expressed in XCVM. + +That part of program is translated to IBC calls to Stride without contracts deployed. + +So this program is possible +``` +Osmosis ATOM -> +Spawn(Stride, ATOM) -> Stake(ATOM) + Spawn(Osmosis, stATOM) +-> Spawn(Centauri, stATOM) +``` + ### ICS-20 Memo as `Spawn` carrier `Spawn` forms `ICS-20` packet with `memo`. @@ -69,4 +85,14 @@ For each chain and protocol it makes pragmatics hacks to use existing liquidity `xc-master` contract verifies amount sent and proceed with move of assets up to amount in message via delegation from `xc-account`. -Approach is needed because there is no `amount` information can be securely transferred in `memo`. \ No newline at end of file +Approach is needed because there is no `amount` information can be securely transferred in `memo`. + +### Asset id encoding + +Each asset id is 128 bit number with 4 first bytes are network id, means that numbers never overlap. + +So it will not be the case that on one chain 123213 means PEPA and on other chain 123213 means SHIB. + +Prefix allows to find network to look at for asset info. + +For security reasons when assets sent from chain to chain, asset id changes. \ No newline at end of file diff --git a/code/xcvm/cosmwasm/contracts/gateway/README.md b/code/xcvm/cosmwasm/contracts/gateway/README.md index cab61fc2698..a95b1aa5cc6 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/README.md +++ b/code/xcvm/cosmwasm/contracts/gateway/README.md @@ -6,4 +6,4 @@ Hides custom entry points and messaging under unified security, routing, assets Is Entrypoint and exit for Programs. Also handles interactions with [OTP](otp.md). -Book keeps per-chain state. Holds the address mappings for cross-chain accounts and instantiates new interpreters. +Book keeps per-chain state. Holds the address mappings for cross-chain accounts and instantiates new interpreters. \ No newline at end of file diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/assets.rs b/code/xcvm/cosmwasm/contracts/gateway/src/assets.rs index db284ba6a37..3beaee4c90f 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/assets.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/assets.rs @@ -38,5 +38,5 @@ pub(crate) fn force_remove_asset( ) -> std::result::Result { ASSETS.remove(deps.storage, asset_id); Ok(Response::new() - .add_event(make_event("unregister").add_attribute("asset_id", asset_id.to_string()))) + .add_event(make_event("assets.removed").add_attribute("asset_id", asset_id.to_string()))) } diff --git a/code/xcvm/cosmwasm/contracts/gateway/src/contract/execute.rs b/code/xcvm/cosmwasm/contracts/gateway/src/contract/execute.rs index 42fe925437d..a093bb43569 100644 --- a/code/xcvm/cosmwasm/contracts/gateway/src/contract/execute.rs +++ b/code/xcvm/cosmwasm/contracts/gateway/src/contract/execute.rs @@ -97,8 +97,8 @@ fn transfer_from_user( return Err(ContractError::InsufficientFunds)? } }, - msg::AssetReference::Virtual { cw20_address } => - transfers.push(Cw20Contract(cw20_address).call(Cw20ExecuteMsg::TransferFrom { + msg::AssetReference::Cw20 { contract } => + transfers.push(Cw20Contract(contract).call(Cw20ExecuteMsg::TransferFrom { owner: user.to_string(), recipient: self_address.to_string(), amount: (*amount).into(), @@ -232,8 +232,8 @@ fn send_funds_to_interpreter( amount: vec![Coin::new(amount, denom)], } .into(), - msg::AssetReference::Virtual { cw20_address } => { - let contract = Cw20Contract(cw20_address); + msg::AssetReference::Cw20 { contract } => { + let contract = Cw20Contract(contract); contract.call(Cw20ExecuteMsg::Transfer { recipient: interpreter_address.clone(), amount: amount.into(), diff --git a/code/xcvm/cosmwasm/contracts/interpreter/src/contract.rs b/code/xcvm/cosmwasm/contracts/interpreter/src/contract.rs index ab78b8009a7..c9cf07a9a55 100644 --- a/code/xcvm/cosmwasm/contracts/interpreter/src/contract.rs +++ b/code/xcvm/cosmwasm/contracts/interpreter/src/contract.rs @@ -230,8 +230,8 @@ pub fn interpret_call( asset_id, )?; match reference.local { - AssetReference::Virtual { cw20_address } => - Cow::Owned(cw20_address.into_string().into()), + AssetReference::Cw20 { contract } => + Cow::Owned(contract.into_string().into()), AssetReference::Native { denom } => Cow::Owned(denom.into()), } }, @@ -242,10 +242,10 @@ pub fn interpret_call( asset_id, )?; let amount = match reference.local { - AssetReference::Virtual { cw20_address } => apply_amount_to_cw20_balance( + AssetReference::Cw20 { contract } => apply_amount_to_cw20_balance( deps, &balance, - &cw20_address, + &contract, &env.contract.address, ), AssetReference::Native { denom } => @@ -308,10 +308,10 @@ pub fn interpret_spawn( .apply(coin.amount.into()) .map_err(|_| ContractError::ArithmeticError) }, - AssetReference::Virtual { cw20_address } => apply_amount_to_cw20_balance( + AssetReference::Cw20 { contract } => apply_amount_to_cw20_balance( deps.as_ref(), &balance, - cw20_address, + contract, &env.contract.address, ), }?; @@ -324,12 +324,11 @@ pub fn interpret_spawn( to_address: gateway_address.clone().into(), amount: vec![Coin { denom, amount: transfer_amount.into() }], }), - AssetReference::Virtual { cw20_address } => response.add_message( - Cw20Contract(cw20_address).call(Cw20ExecuteMsg::Transfer { + AssetReference::Cw20 { contract } => + response.add_message(Cw20Contract(contract).call(Cw20ExecuteMsg::Transfer { recipient: gateway_address.clone().into(), amount: transfer_amount.into(), - })?, - ), + })?), }; } } @@ -396,12 +395,12 @@ pub fn interpret_transfer( amount: vec![coin], }) }, - AssetReference::Virtual { cw20_address } => { - let contract = Cw20Contract(cw20_address.clone()); + AssetReference::Cw20 { contract } => { + let contract = Cw20Contract(contract.clone()); let transfer_amount = apply_amount_to_cw20_balance( deps.as_ref(), &balance, - &cw20_address, + &contract.0, &env.contract.address, )?; response.add_message(contract.call(Cw20ExecuteMsg::Transfer { @@ -463,17 +462,17 @@ fn handle_call_result(deps: DepsMut, msg: Reply) -> StdResult { /// Calculates and returns the actual balance to process /// /// * `balance`: Balance to be transformed into the actual balance -/// * `cw20_address`: Address of the corresponding cw20 contract +/// * `contract`: Address of the corresponding cw20 contract /// * `self_address`: This interpreter's address fn apply_amount_to_cw20_balance + Clone>( deps: Deps, balance: &Balance, - cw20_address: A, + contract: A, self_address: A, ) -> Result { let balance_response = deps.querier.query::(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: cw20_address.clone().into(), + contract_addr: contract.clone().into(), msg: to_binary(&Cw20QueryMsg::Balance { address: self_address.into() })?, }))?; @@ -481,7 +480,7 @@ fn apply_amount_to_cw20_balance + Clone>( // If the balance is unit, we need to take `decimals` into account. let token_info = deps.querier.query::(&QueryRequest::Wasm(WasmQuery::Smart { - contract_addr: cw20_address.into(), + contract_addr: contract.into(), msg: to_binary(&Cw20QueryMsg::TokenInfo {})?, }))?; balance diff --git a/code/xcvm/cosmwasm/tests/src/tests/framework.rs b/code/xcvm/cosmwasm/tests/src/tests/framework.rs index bad8e47a259..53115ce1610 100644 --- a/code/xcvm/cosmwasm/tests/src/tests/framework.rs +++ b/code/xcvm/cosmwasm/tests/src/tests/framework.rs @@ -276,8 +276,8 @@ impl TestVM> { (ConfigSubMsg::ForceAsset(AssetItem { asset_id, from_network_id: todo!("restore"), - local: xc_core::gateway::AssetReference::Virtual { - cw20_address: asset_address.clone().into(), + local: xc_core::gateway::AssetReference::Cw20 { + contract: asset_address.clone().into(), }, bridged: None, })), diff --git a/code/xcvm/lib/core/Cargo.toml b/code/xcvm/lib/core/Cargo.toml index 7997b8df8db..1cb18713125 100644 --- a/code/xcvm/lib/core/Cargo.toml +++ b/code/xcvm/lib/core/Cargo.toml @@ -27,6 +27,7 @@ strum.workspace = true thiserror = { workspace = true } ibc-proto = { workspace = true, default-features = false, features = ["serde", "parity-scale-codec"]} serde-cw-value = { workspace = true, default-features = false} +xcm = { workspace = true, default-features = false, optional = true} [build-dependencies] prost-build = { workspace = true } @@ -34,8 +35,10 @@ prost-build = { workspace = true } [features] default = ["std"] cosmwasm = ["cw-storage-plus", "cw20"] +substrate = ["xcm"] std = [ "cosmwasm-std/std", + "xcm/std", "cw-storage-plus/std", "dep:cosmwasm-schema", "dep:schemars", diff --git a/code/xcvm/lib/core/schema/raw/execute.json b/code/xcvm/lib/core/schema/raw/execute.json index d15b720a220..ef7825e9195 100644 --- a/code/xcvm/lib/core/schema/raw/execute.json +++ b/code/xcvm/lib/core/schema/raw/execute.json @@ -117,7 +117,7 @@ ], "properties": { "message_hook": { - "$ref": "#/definitions/MessageHookMsg" + "$ref": "#/definitions/XcMessageData" } }, "additionalProperties": false @@ -155,11 +155,14 @@ "AssetItem": { "type": "object", "required": [ + "asset_id", "from_network_id", - "id", "local" ], "properties": { + "asset_id": { + "$ref": "#/definitions/AssetId" + }, "bridged": { "anyOf": [ { @@ -173,9 +176,6 @@ "from_network_id": { "$ref": "#/definitions/NetworkId" }, - "id": { - "$ref": "#/definitions/AssetId" - }, "local": { "$ref": "#/definitions/AssetReference" } @@ -207,16 +207,36 @@ { "type": "object", "required": [ - "virtual" + "a_d_r001" + ], + "properties": { + "a_d_r001": { + "type": "object", + "required": [ + "sha256" + ], + "properties": { + "sha256": { + "$ref": "#/definitions/HexBinary" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" ], "properties": { - "virtual": { + "cw20": { "type": "object", "required": [ - "cw20_address" + "contract" ], "properties": { - "cw20_address": { + "contract": { "$ref": "#/definitions/Addr" } } @@ -423,33 +443,36 @@ } }, "ConfigSubMsg": { + "description": "cross cross chain routing requires a lot of configuration, about chain executing this contract, about connectivity to and of other chains (even if not connected directly) and about assets and services on these chains (in future block hooks and some set of host extensions/precompiles would help to get some info automatically) `Force` message sets the data unconditionally.", "oneOf": [ { + "description": "Permissioned message (gov or admin) to force set information about network contract is executed. Network can be any network or this network (so it overrides some this network parameters too)", "type": "object", "required": [ - "force_network_to_network" + "force_network" ], "properties": { - "force_network_to_network": { - "$ref": "#/definitions/ForceNetworkToNetworkMsg" + "force_network": { + "$ref": "#/definitions/NetworkItem" } }, "additionalProperties": false }, { + "description": "Sets network to network connectivity/routing information", "type": "object", "required": [ - "force_network" + "force_network_to_network" ], "properties": { - "force_network": { - "$ref": "#/definitions/NetworkItem" + "force_network_to_network": { + "$ref": "#/definitions/ForceNetworkToNetworkMsg" } }, "additionalProperties": false }, { - "description": "Message sent by an admin to register a new asset.", + "description": "Permissioned message (gov or admin) to force set asset information.", "type": "object", "required": [ "force_asset" @@ -510,7 +533,7 @@ ] }, "Displayed_for_uint128": { - "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", + "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json_wasm::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json_wasm::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", "type": "integer", "format": "uint128", "minimum": 0.0 @@ -580,10 +603,10 @@ { "type": "object", "required": [ - "IbcIcs20" + "ibc_ics20" ], "properties": { - "IbcIcs20": { + "ibc_ics20": { "$ref": "#/definitions/PrefixedDenom" } }, @@ -635,31 +658,63 @@ ], "properties": { "cosm_wasm": { - "$ref": "#/definitions/Addr" + "type": "object", + "required": [ + "admin", + "contract", + "interpreter_code_id" + ], + "properties": { + "admin": { + "description": "admin of everything", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "contract": { + "$ref": "#/definitions/Addr" + }, + "interpreter_code_id": { + "description": "Address of the XCVM interpreter contract code", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } } }, "additionalProperties": false } ] }, - "IbcEnabled": { + "HexBinary": { + "description": "This is a wrapper around Vec to add hex de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is similar to `cosmwasm_std::Binary` but uses hex. See also .", + "type": "string" + }, + "IbcChannels": { "type": "object", "properties": { - "ics_20_features": { + "ics20": { "anyOf": [ { - "$ref": "#/definitions/Ics20Features" + "$ref": "#/definitions/Ics20Channel" }, { "type": "null" } ] - }, - "ics_20_sender": { - "description": "specific per chain way to send IBC ICS 20 assets", + } + } + }, + "IbcEnabled": { + "type": "object", + "properties": { + "channels": { "anyOf": [ { - "$ref": "#/definitions/IbcIcs20Sender" + "$ref": "#/definitions/IbcChannels" }, { "type": "null" @@ -688,8 +743,8 @@ { "type": "string", "enum": [ - "OsmosisModule", - "CosmWasmStd" + "CosmosStargateIbcApplicationsTransferV1MsgTransfer", + "CosmWasmStd1_3" ] }, { @@ -754,7 +809,34 @@ } } }, + "Ics20Channel": { + "type": "object", + "required": [ + "sender" + ], + "properties": { + "features": { + "anyOf": [ + { + "$ref": "#/definitions/Ics20Features" + }, + { + "type": "null" + } + ] + }, + "sender": { + "description": "specific per chain way to send IBC ICS 20 assets", + "allOf": [ + { + "$ref": "#/definitions/IbcIcs20Sender" + } + ] + } + } + }, "Ics20Features": { + "description": "what features/modules/version enabled/installed/configured", "type": "object", "properties": { "pfm": { @@ -780,6 +862,22 @@ } } }, + "IcsPair": { + "description": "we need both, so we can unwrap", + "type": "object", + "required": [ + "sink", + "source" + ], + "properties": { + "sink": { + "$ref": "#/definitions/ChannelId" + }, + "source": { + "$ref": "#/definitions/ChannelId" + } + } + }, "Instruction_for_Array_of_uint8_and_CanonicalAddr_and_Funds_for_Balance": { "description": "Base XCVM instructions. This set will remain as small as possible, expressiveness must come on `top` of the base instructions.", "oneOf": [ @@ -939,22 +1037,6 @@ } } }, - "MessageHookMsg": { - "description": "This message should be send as part of wasm termination memo. So that can match it to sender hash and know what channel and origin was used to send message. All information here is not secured until compared with existing secured data.", - "type": "object", - "required": [ - "data", - "from_network_id" - ], - "properties": { - "data": { - "$ref": "#/definitions/Binary" - }, - "from_network_id": { - "$ref": "#/definitions/NetworkId" - } - } - }, "NetworkId": { "description": "Newtype for XCVM networks ID. Must be unique for each network and must never change. This ID is an opaque, arbitrary type from the XCVM protocol and no assumption must be made on how it is computed.", "type": "integer", @@ -964,20 +1046,21 @@ "NetworkItem": { "type": "object", "required": [ - "admin", - "id", - "interpreter_code_id" + "network_id" ], "properties": { - "admin": { - "description": "The admin which is allowed to update the bridge list.", - "allOf": [ + "accounts": { + "description": "Account encoding type", + "anyOf": [ { - "$ref": "#/definitions/Addr" + "$ref": "#/definitions/Prefix" + }, + { + "type": "null" } ] }, - "gateway_to_send_to": { + "gateway": { "description": "something which will be receiver on other side case of network has XCVM deployed as contract, account address is stored here", "anyOf": [ { @@ -998,25 +1081,8 @@ } ] }, - "id": { + "network_id": { "$ref": "#/definitions/NetworkId" - }, - "interpreter_code_id": { - "description": "Address of the XCVM interpreter contract code", - "type": "integer", - "format": "uint64", - "minimum": 0.0 - }, - "prefix": { - "description": "Cosmos bech32 prefix per network, if there is prefix chain accounts are Cosmos SDK compatible chain", - "anyOf": [ - { - "$ref": "#/definitions/Prefix" - }, - { - "type": "null" - } - ] } } }, @@ -1034,11 +1100,10 @@ } ] }, - "ics_20_channel": { - "description": "channel to use to send ics 20 tokens", + "ics_20": { "anyOf": [ { - "$ref": "#/definitions/ChannelId" + "$ref": "#/definitions/IcsPair" }, { "type": "null" @@ -1260,6 +1325,22 @@ "type": "boolean" } } + }, + "XcMessageData": { + "description": "This message should be send as part of wasm termination memo. So that can match it to sender hash and know what channel and origin was used to send message. All information here is not secured until compared with existing secured data.", + "type": "object", + "required": [ + "data", + "from_network_id" + ], + "properties": { + "data": { + "$ref": "#/definitions/Binary" + }, + "from_network_id": { + "$ref": "#/definitions/NetworkId" + } + } } } } diff --git a/code/xcvm/lib/core/schema/raw/instantiate.json b/code/xcvm/lib/core/schema/raw/instantiate.json index 9eeadd4d936..75f93e86788 100644 --- a/code/xcvm/lib/core/schema/raw/instantiate.json +++ b/code/xcvm/lib/core/schema/raw/instantiate.json @@ -15,7 +15,7 @@ "type": "object", "required": [ "admin", - "id" + "here_id" ], "properties": { "admin": { @@ -26,7 +26,7 @@ } ] }, - "id": { + "here_id": { "description": "Network ID of this network", "allOf": [ { diff --git a/code/xcvm/lib/core/schema/raw/query.json b/code/xcvm/lib/core/schema/raw/query.json index 852574a5d4a..ac578079268 100644 --- a/code/xcvm/lib/core/schema/raw/query.json +++ b/code/xcvm/lib/core/schema/raw/query.json @@ -34,7 +34,7 @@ ] }, "Displayed_for_uint128": { - "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", + "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json_wasm::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json_wasm::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", "type": "integer", "format": "uint128", "minimum": 0.0 diff --git a/code/xcvm/lib/core/schema/raw/response_to_lookup_asset.json b/code/xcvm/lib/core/schema/raw/response_to_lookup_asset.json index f7e4e1a5042..5d4823edf73 100644 --- a/code/xcvm/lib/core/schema/raw/response_to_lookup_asset.json +++ b/code/xcvm/lib/core/schema/raw/response_to_lookup_asset.json @@ -26,11 +26,14 @@ "AssetItem": { "type": "object", "required": [ + "asset_id", "from_network_id", - "id", "local" ], "properties": { + "asset_id": { + "$ref": "#/definitions/AssetId" + }, "bridged": { "anyOf": [ { @@ -44,9 +47,6 @@ "from_network_id": { "$ref": "#/definitions/NetworkId" }, - "id": { - "$ref": "#/definitions/AssetId" - }, "local": { "$ref": "#/definitions/AssetReference" } @@ -78,16 +78,36 @@ { "type": "object", "required": [ - "virtual" + "a_d_r001" ], "properties": { - "virtual": { + "a_d_r001": { "type": "object", "required": [ - "cw20_address" + "sha256" ], "properties": { - "cw20_address": { + "sha256": { + "$ref": "#/definitions/HexBinary" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "type": "object", + "required": [ + "contract" + ], + "properties": { + "contract": { "$ref": "#/definitions/Addr" } } @@ -109,7 +129,7 @@ } }, "Displayed_for_uint128": { - "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", + "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json_wasm::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json_wasm::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", "type": "integer", "format": "uint128", "minimum": 0.0 @@ -119,10 +139,10 @@ { "type": "object", "required": [ - "IbcIcs20" + "ibc_ics20" ], "properties": { - "IbcIcs20": { + "ibc_ics20": { "$ref": "#/definitions/PrefixedDenom" } }, @@ -130,6 +150,10 @@ } ] }, + "HexBinary": { + "description": "This is a wrapper around Vec to add hex de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is similar to `cosmwasm_std::Binary` but uses hex. See also .", + "type": "string" + }, "NetworkId": { "description": "Newtype for XCVM networks ID. Must be unique for each network and must never change. This ID is an opaque, arbitrary type from the XCVM protocol and no assumption must be made on how it is computed.", "type": "integer", diff --git a/code/xcvm/lib/core/schema/xc-core.json b/code/xcvm/lib/core/schema/xc-core.json index ea8ee7769c2..d9772db6cb9 100644 --- a/code/xcvm/lib/core/schema/xc-core.json +++ b/code/xcvm/lib/core/schema/xc-core.json @@ -19,7 +19,7 @@ "type": "object", "required": [ "admin", - "id" + "here_id" ], "properties": { "admin": { @@ -30,7 +30,7 @@ } ] }, - "id": { + "here_id": { "description": "Network ID of this network", "allOf": [ { @@ -167,7 +167,7 @@ ], "properties": { "message_hook": { - "$ref": "#/definitions/MessageHookMsg" + "$ref": "#/definitions/XcMessageData" } }, "additionalProperties": false @@ -205,11 +205,14 @@ "AssetItem": { "type": "object", "required": [ + "asset_id", "from_network_id", - "id", "local" ], "properties": { + "asset_id": { + "$ref": "#/definitions/AssetId" + }, "bridged": { "anyOf": [ { @@ -223,9 +226,6 @@ "from_network_id": { "$ref": "#/definitions/NetworkId" }, - "id": { - "$ref": "#/definitions/AssetId" - }, "local": { "$ref": "#/definitions/AssetReference" } @@ -257,16 +257,36 @@ { "type": "object", "required": [ - "virtual" + "a_d_r001" + ], + "properties": { + "a_d_r001": { + "type": "object", + "required": [ + "sha256" + ], + "properties": { + "sha256": { + "$ref": "#/definitions/HexBinary" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" ], "properties": { - "virtual": { + "cw20": { "type": "object", "required": [ - "cw20_address" + "contract" ], "properties": { - "cw20_address": { + "contract": { "$ref": "#/definitions/Addr" } } @@ -473,33 +493,36 @@ } }, "ConfigSubMsg": { + "description": "cross cross chain routing requires a lot of configuration, about chain executing this contract, about connectivity to and of other chains (even if not connected directly) and about assets and services on these chains (in future block hooks and some set of host extensions/precompiles would help to get some info automatically) `Force` message sets the data unconditionally.", "oneOf": [ { + "description": "Permissioned message (gov or admin) to force set information about network contract is executed. Network can be any network or this network (so it overrides some this network parameters too)", "type": "object", "required": [ - "force_network_to_network" + "force_network" ], "properties": { - "force_network_to_network": { - "$ref": "#/definitions/ForceNetworkToNetworkMsg" + "force_network": { + "$ref": "#/definitions/NetworkItem" } }, "additionalProperties": false }, { + "description": "Sets network to network connectivity/routing information", "type": "object", "required": [ - "force_network" + "force_network_to_network" ], "properties": { - "force_network": { - "$ref": "#/definitions/NetworkItem" + "force_network_to_network": { + "$ref": "#/definitions/ForceNetworkToNetworkMsg" } }, "additionalProperties": false }, { - "description": "Message sent by an admin to register a new asset.", + "description": "Permissioned message (gov or admin) to force set asset information.", "type": "object", "required": [ "force_asset" @@ -560,7 +583,7 @@ ] }, "Displayed_for_uint128": { - "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", + "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json_wasm::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json_wasm::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", "type": "integer", "format": "uint128", "minimum": 0.0 @@ -630,10 +653,10 @@ { "type": "object", "required": [ - "IbcIcs20" + "ibc_ics20" ], "properties": { - "IbcIcs20": { + "ibc_ics20": { "$ref": "#/definitions/PrefixedDenom" } }, @@ -685,31 +708,63 @@ ], "properties": { "cosm_wasm": { - "$ref": "#/definitions/Addr" + "type": "object", + "required": [ + "admin", + "contract", + "interpreter_code_id" + ], + "properties": { + "admin": { + "description": "admin of everything", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, + "contract": { + "$ref": "#/definitions/Addr" + }, + "interpreter_code_id": { + "description": "Address of the XCVM interpreter contract code", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + } + } } }, "additionalProperties": false } ] }, - "IbcEnabled": { + "HexBinary": { + "description": "This is a wrapper around Vec to add hex de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is similar to `cosmwasm_std::Binary` but uses hex. See also .", + "type": "string" + }, + "IbcChannels": { "type": "object", "properties": { - "ics_20_features": { + "ics20": { "anyOf": [ { - "$ref": "#/definitions/Ics20Features" + "$ref": "#/definitions/Ics20Channel" }, { "type": "null" } ] - }, - "ics_20_sender": { - "description": "specific per chain way to send IBC ICS 20 assets", + } + } + }, + "IbcEnabled": { + "type": "object", + "properties": { + "channels": { "anyOf": [ { - "$ref": "#/definitions/IbcIcs20Sender" + "$ref": "#/definitions/IbcChannels" }, { "type": "null" @@ -738,8 +793,8 @@ { "type": "string", "enum": [ - "OsmosisModule", - "CosmWasmStd" + "CosmosStargateIbcApplicationsTransferV1MsgTransfer", + "CosmWasmStd1_3" ] }, { @@ -804,7 +859,34 @@ } } }, + "Ics20Channel": { + "type": "object", + "required": [ + "sender" + ], + "properties": { + "features": { + "anyOf": [ + { + "$ref": "#/definitions/Ics20Features" + }, + { + "type": "null" + } + ] + }, + "sender": { + "description": "specific per chain way to send IBC ICS 20 assets", + "allOf": [ + { + "$ref": "#/definitions/IbcIcs20Sender" + } + ] + } + } + }, "Ics20Features": { + "description": "what features/modules/version enabled/installed/configured", "type": "object", "properties": { "pfm": { @@ -830,6 +912,22 @@ } } }, + "IcsPair": { + "description": "we need both, so we can unwrap", + "type": "object", + "required": [ + "sink", + "source" + ], + "properties": { + "sink": { + "$ref": "#/definitions/ChannelId" + }, + "source": { + "$ref": "#/definitions/ChannelId" + } + } + }, "Instruction_for_Array_of_uint8_and_CanonicalAddr_and_Funds_for_Balance": { "description": "Base XCVM instructions. This set will remain as small as possible, expressiveness must come on `top` of the base instructions.", "oneOf": [ @@ -989,22 +1087,6 @@ } } }, - "MessageHookMsg": { - "description": "This message should be send as part of wasm termination memo. So that can match it to sender hash and know what channel and origin was used to send message. All information here is not secured until compared with existing secured data.", - "type": "object", - "required": [ - "data", - "from_network_id" - ], - "properties": { - "data": { - "$ref": "#/definitions/Binary" - }, - "from_network_id": { - "$ref": "#/definitions/NetworkId" - } - } - }, "NetworkId": { "description": "Newtype for XCVM networks ID. Must be unique for each network and must never change. This ID is an opaque, arbitrary type from the XCVM protocol and no assumption must be made on how it is computed.", "type": "integer", @@ -1014,20 +1096,21 @@ "NetworkItem": { "type": "object", "required": [ - "admin", - "id", - "interpreter_code_id" + "network_id" ], "properties": { - "admin": { - "description": "The admin which is allowed to update the bridge list.", - "allOf": [ + "accounts": { + "description": "Account encoding type", + "anyOf": [ { - "$ref": "#/definitions/Addr" + "$ref": "#/definitions/Prefix" + }, + { + "type": "null" } ] }, - "gateway_to_send_to": { + "gateway": { "description": "something which will be receiver on other side case of network has XCVM deployed as contract, account address is stored here", "anyOf": [ { @@ -1048,25 +1131,8 @@ } ] }, - "id": { + "network_id": { "$ref": "#/definitions/NetworkId" - }, - "interpreter_code_id": { - "description": "Address of the XCVM interpreter contract code", - "type": "integer", - "format": "uint64", - "minimum": 0.0 - }, - "prefix": { - "description": "Cosmos bech32 prefix per network, if there is prefix chain accounts are Cosmos SDK compatible chain", - "anyOf": [ - { - "$ref": "#/definitions/Prefix" - }, - { - "type": "null" - } - ] } } }, @@ -1084,11 +1150,10 @@ } ] }, - "ics_20_channel": { - "description": "channel to use to send ics 20 tokens", + "ics_20": { "anyOf": [ { - "$ref": "#/definitions/ChannelId" + "$ref": "#/definitions/IcsPair" }, { "type": "null" @@ -1310,6 +1375,22 @@ "type": "boolean" } } + }, + "XcMessageData": { + "description": "This message should be send as part of wasm termination memo. So that can match it to sender hash and know what channel and origin was used to send message. All information here is not secured until compared with existing secured data.", + "type": "object", + "required": [ + "data", + "from_network_id" + ], + "properties": { + "data": { + "$ref": "#/definitions/Binary" + }, + "from_network_id": { + "$ref": "#/definitions/NetworkId" + } + } } } }, @@ -1349,7 +1430,7 @@ ] }, "Displayed_for_uint128": { - "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", + "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json_wasm::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json_wasm::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", "type": "integer", "format": "uint128", "minimum": 0.0 @@ -1387,11 +1468,14 @@ "AssetItem": { "type": "object", "required": [ + "asset_id", "from_network_id", - "id", "local" ], "properties": { + "asset_id": { + "$ref": "#/definitions/AssetId" + }, "bridged": { "anyOf": [ { @@ -1405,9 +1489,6 @@ "from_network_id": { "$ref": "#/definitions/NetworkId" }, - "id": { - "$ref": "#/definitions/AssetId" - }, "local": { "$ref": "#/definitions/AssetReference" } @@ -1439,16 +1520,36 @@ { "type": "object", "required": [ - "virtual" + "a_d_r001" ], "properties": { - "virtual": { + "a_d_r001": { "type": "object", "required": [ - "cw20_address" + "sha256" ], "properties": { - "cw20_address": { + "sha256": { + "$ref": "#/definitions/HexBinary" + } + } + } + }, + "additionalProperties": false + }, + { + "type": "object", + "required": [ + "cw20" + ], + "properties": { + "cw20": { + "type": "object", + "required": [ + "contract" + ], + "properties": { + "contract": { "$ref": "#/definitions/Addr" } } @@ -1470,7 +1571,7 @@ } }, "Displayed_for_uint128": { - "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", + "description": "A wrapper around a type which is serde-serialised as a string.\n\nFor serde-serialisation to be implemented for the type `T` must implement `Display` and `FromStr` traits.\n\n``` # use xc_core::Displayed;\n\n#[derive(serde::Serialize, serde::Deserialize)] struct Foo { value: Displayed }\n\nlet encoded = serde_json_wasm::to_string(&Foo { value: Displayed(42) }).unwrap(); assert_eq!(r#\"{\"value\":\"42\"}\"#, encoded);\n\nlet decoded = serde_json_wasm::from_str::(r#\"{\"value\":\"42\"}\"#).unwrap(); assert_eq!(Displayed(42), decoded.value); ```", "type": "integer", "format": "uint128", "minimum": 0.0 @@ -1480,10 +1581,10 @@ { "type": "object", "required": [ - "IbcIcs20" + "ibc_ics20" ], "properties": { - "IbcIcs20": { + "ibc_ics20": { "$ref": "#/definitions/PrefixedDenom" } }, @@ -1491,6 +1592,10 @@ } ] }, + "HexBinary": { + "description": "This is a wrapper around Vec to add hex de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is similar to `cosmwasm_std::Binary` but uses hex. See also .", + "type": "string" + }, "NetworkId": { "description": "Newtype for XCVM networks ID. Must be unique for each network and must never change. This ID is an opaque, arbitrary type from the XCVM protocol and no assumption must be made on how it is computed.", "type": "integer", diff --git a/code/xcvm/lib/core/src/asset.rs b/code/xcvm/lib/core/src/asset.rs index f19e8533a8e..9fe37a69bdc 100644 --- a/code/xcvm/lib/core/src/asset.rs +++ b/code/xcvm/lib/core/src/asset.rs @@ -1,4 +1,4 @@ -use crate::prelude::*; +use crate::{prelude::*, NetworkId}; #[cfg(feature = "cw-storage-plus")] use cw_storage_plus::{Key, Prefixer}; @@ -402,3 +402,33 @@ impl From> for Vec<(u128, T)> { .collect() } } + +// `protocol_id` - namespace like thing, default is 0, but can be used for example other consensus +// to create known ahead +/// `nonce` - local consensus atomic number, usually increasing monotonic increment +pub fn generate_asset_id(network_id: NetworkId, protocol_id: u32, nonce: u64) -> AssetId { + AssetId::from( + (u128::from(network_id.0) << 96) | (u128::from(protocol_id) << 64) | (u128::from(nonce)), + ) +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn devnet() { + let pica = generate_asset_id(0.into(), 0, 1); + assert_eq!(pica, 1.into()); + let pica = generate_asset_id(1.into(), 0, 1); + assert_eq!(pica, 79228162514264337593543950337.into()); + let pica = generate_asset_id(2.into(), 0, 1); + assert_eq!(pica, 158456325028528675187087900673.into()); + let pica = generate_asset_id(3.into(), 0, 1); + assert_eq!(pica, 237684487542793012780631851009.into()); + + let atom = generate_asset_id(2.into(), 0, 2); + assert_eq!(atom, 158456325028528675187087900674.into()); + let atom = generate_asset_id(3.into(), 0, 2); + assert_eq!(atom, 237684487542793012780631851010.into()); + } +} diff --git a/code/xcvm/lib/core/src/cosmos.rs b/code/xcvm/lib/core/src/cosmos.rs index 3488e33eebc..8102464491e 100644 --- a/code/xcvm/lib/core/src/cosmos.rs +++ b/code/xcvm/lib/core/src/cosmos.rs @@ -23,3 +23,29 @@ pub fn hash_denom_trace(unwrapped: &str) -> String { let digest = Sha256::digest(unwrapped.as_bytes()); ["ibc/", &hex::encode_upper(digest)].concat() } + +#[cfg(test)] +mod tests { + use super::*; + + // various devnet channels hashes + #[test] + fn devnet() { + let pica = hash_denom_trace("/transfer/channel-1/1"); + assert_eq!(pica, "ibc/B62D63F2BD5A7B70AB15F84BCB70EAC88222D3A8E8E0B22793EE788068EA22BA"); + let pica = hash_denom_trace("/transfer/channel-0/1"); + assert_eq!(pica, "ibc/F2B6EF5B6F86990A3863B78687ADE3D95E412657AAEB7CF2B3B8131B8055C1F1"); + + let pica: String = hash_denom_trace("/transfer/channel-1/ppica"); + assert_eq!(pica, "ibc/661BD30059657725608DF36907F06B70C1FA7A1772FF92AEE1844A3E35A80D63"); + + let pica: String = hash_denom_trace("/transfer/channel-0/ppica"); + assert_eq!(pica, "ibc/F0E228914E0E69E7B5E9231282FE6B7595CF90CB76E7193C6AFDCACDF5E83821"); + + let osmo: String = hash_denom_trace("/transfer/channel-1/uosmo"); + assert_eq!(osmo, "ibc/BCACECE44E39A9009D793D68CC5DF76B402607B1C574379DCB4F3A5D24BC1936"); + + let osmo: String = hash_denom_trace("/transfer/channel-0/uosmo"); + assert_eq!(osmo, "ibc/B4511F40A2844906F5940444691EE3AE877E8E0DD8354C8C4D36670A46C5680D"); + } +} diff --git a/code/xcvm/lib/core/src/gateway/config.rs b/code/xcvm/lib/core/src/gateway/config.rs index 48b5ea3f997..3a4d8030d61 100644 --- a/code/xcvm/lib/core/src/gateway/config.rs +++ b/code/xcvm/lib/core/src/gateway/config.rs @@ -33,10 +33,27 @@ pub struct Ics20Features { } #[allow(clippy::large_enum_variant)] -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[cfg_attr(feature = "std", derive(JsonSchema))] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Encode, Decode)] +#[serde(rename_all = "snake_case")] +#[cfg_attr(all(feature = "std", not(feature = "substrate")), derive(JsonSchema))] pub enum ForeignAssetId { IbcIcs20(PrefixedDenom), + #[cfg(feature = "substrate")] + Xcm(xcm::VersionedMultiLocation), +} + +#[cfg(feature = "substrate")] +impl parity_scale_codec::MaxEncodedLen for ForeignAssetId { + fn max_encoded_len() -> usize { + 2048 + } +} + +#[cfg(feature = "substrate")] +impl From for ForeignAssetId { + fn from(this: xcm::VersionedMultiLocation) -> Self { + Self::Xcm(this) + } } impl From for ForeignAssetId { @@ -127,6 +144,7 @@ pub struct OtherNetworkItem { /// and about assets and services on these chains /// (in future block hooks and some set of host extensions/precompiles would help to get some info /// automatically) +/// `Force` message sets the data unconditionally. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[serde(rename_all = "snake_case")] #[cfg_attr(feature = "std", derive(schemars::JsonSchema))] @@ -204,14 +222,17 @@ pub struct BridgeAsset { #[cfg_attr(feature = "std", derive(schemars::JsonSchema))] pub enum AssetReference { Native { denom: String }, - Virtual { cw20_address: Addr }, + Cw20 { contract: Addr }, + // ADR001 { sha256: cosmwasm_std::HexBinary }, } impl AssetReference { pub fn denom(&self) -> String { match self { + // AssetReference::ADR001 { sha256 } => ["ibc/", + // &sha256.to_string()].concat().to_string(), AssetReference::Native { denom } => denom.clone(), - AssetReference::Virtual { cw20_address } => ["cw20:", cw20_address.as_str()].concat(), + AssetReference::Cw20 { contract } => ["cw20:", contract.as_str()].concat(), } } } diff --git a/code/xcvm/lib/core/src/location.rs b/code/xcvm/lib/core/src/location.rs deleted file mode 100644 index d4e20ccc3ae..00000000000 --- a/code/xcvm/lib/core/src/location.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::prelude::*; -use ibc_rs_scale::applications::transfer::{BaseDenom, TracePath}; -use thiserror::Error; - -#[allow(clippy::large_enum_variant)] -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[cfg_attr(feature = "std", derive(JsonSchema))] -pub enum ForeignAssetId { - IbcIcs20(PrefixedDenom), -} - -impl From for ForeignAssetId { - fn from(this: PrefixedDenom) -> Self { - Self::IbcIcs20(this) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -#[cfg_attr(feature = "std", derive(schemars::JsonSchema))] -pub struct PrefixedDenom { - /// A series of `{port-id}/{channel-id}`s for tracing the source of the token. - pub trace_path: TracePath, - /// Base denomination of the relayed fungible token. - pub base_denom: BaseDenom, -} - -#[derive(Debug, Error)] -pub enum Error { - #[error("TokenTransferError")] - TokenTransferError, -} diff --git a/flake/osmosis.nix b/flake/osmosis.nix index 90c4f104957..a14635e547e 100644 --- a/flake/osmosis.nix +++ b/flake/osmosis.nix @@ -274,6 +274,53 @@ osmosisd tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_CENTAURI_TO_OSMOSIS" --chain-id="$CHAIN_ID" --node "tcp://localhost:36657" --output json --yes --gas 25000000 --fees 920000166uosmo --log_level info --keyring-backend test --home "$OSMOSIS_DATA" --from validator --keyring-dir "$KEYRING_TEST" --trace --log_level trace + sleep $BLOCK_SECONDS + FORCE_PICA=$(cat << EOF + { + "config": { + "force_asset": { + "asset_id": "158456325028528675187087900673", + "from_network_id": 2, + "local": { + "native": { + "denom": "ppica" + } + }, + "bridged": { + "location_on_network": { + "ibc_ics20": { + "base_denom" : "ppica", + "trace_path" : "transfer/channel-0" + } + } + } + } + } + } + EOF + ) + osmosisd tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_PICA" --chain-id="$CHAIN_ID" --node "tcp://localhost:36657" --output json --yes --gas 25000000 --fees 920000166uosmo --log_level info --keyring-backend test --home "$OSMOSIS_DATA" --from validator --keyring-dir "$KEYRING_TEST" --trace --log_level trace + + + sleep $BLOCK_SECONDS + FORCE_UATOM=$(cat << EOF + { + "config": { + "force_asset": { + "asset_id": "237684487542793012780631851010", + "from_network_id": 3, + "local": { + "native": { + "denom" : "uatom" + } + } + } + } + } + EOF + ) + osmosisd tx wasm execute "$GATEWAY_CONTRACT_ADDRESS" "$FORCE_UATOM" --chain-id="$CHAIN_ID" --node "tcp://localhost:36657" --output json --yes --gas 25000000 --fees 920000166uosmo --log_level info --keyring-backend test --home "$OSMOSIS_DATA" --from validator --keyring-dir "$KEYRING_TEST" --trace --log_level trace + sleep $BLOCK_SECONDS osmosisd query wasm contract-state all "$GATEWAY_CONTRACT_ADDRESS" --chain-id="$CHAIN_ID" --node "tcp://localhost:36657" --output json --home "$OSMOSIS_DATA" '';