From 4adfcefe088e5707a67c5798b2fa5b55f605b7d5 Mon Sep 17 00:00:00 2001 From: CJ Cobb <46455409+cjcobb23@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:45:45 -0400 Subject: [PATCH 01/21] feat(minor-router)!: change u32 to u64 in message ids (#666) --- CHANGELOG.md | 11 +++ Cargo.lock | 15 ++-- ampd/src/evm/verifier.rs | 72 +++++++++++++------ ampd/src/handlers/evm_verify_msg.rs | 45 +++++++----- ampd/src/handlers/evm_verify_verifier_set.rs | 16 +++-- ampd/src/handlers/mvx_verify_msg.rs | 6 ++ ampd/src/handlers/mvx_verify_verifier_set.rs | 5 ++ ampd/src/handlers/stellar_verify_msg.rs | 59 +++++++-------- .../handlers/stellar_verify_verifier_set.rs | 23 +++--- ampd/src/handlers/sui_verify_msg.rs | 21 ++++-- ampd/src/handlers/sui_verify_verifier_set.rs | 18 ++--- ampd/src/stellar/http_client.rs | 5 +- ampd/src/stellar/verifier.rs | 35 +++++---- ampd/src/sui/verifier.rs | 39 +++++----- contracts/router/src/contract/execute.rs | 2 +- contracts/voting-verifier/src/contract.rs | 8 +-- .../voting-verifier/src/contract/execute.rs | 4 +- .../voting-verifier/src/contract/query.rs | 4 +- contracts/voting-verifier/src/events.rs | 66 +++++++++++------ packages/axelar-core-std/src/nexus/execute.rs | 4 +- packages/axelar-wasm-std/Cargo.toml | 1 + .../src/msg_id/base_58_event_index.rs | 10 +-- .../src/msg_id/base_58_solana_event_index.rs | 8 +-- .../src/msg_id/tx_hash_event_index.rs | 20 ++++-- 24 files changed, 308 insertions(+), 189 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..4dc58dc07 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +## [Unreleased](https://github.com/axelarnetwork/axelar-amplifier/tree/HEAD) + +[Full Changelog](https://github.com/axelarnetwork/axelar-amplifier/compare/ampd-v1.2.0..HEAD) + +- Change event index in message ids from u32 to u64. Emit message id from voting verifier [#666](https://github.com/axelarnetwork/axelar-amplifier/pull/666) + +#### Migration Notes + +The voting verifier contracts must be migrated before ampd is upgraded. Existing ampd instances will continue to work even after the contract migration, but we recommend upgrading ampd. \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 4677d98fb..6830e3192 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -269,7 +269,7 @@ dependencies = [ "router-api", "serde", "serde_json", - "serde_with 3.8.1", + "serde_with 3.11.0", "service-registry-api", "sha3", "stellar", @@ -857,6 +857,7 @@ dependencies = [ "schemars", "serde", "serde_json", + "serde_with 3.11.0", "sha3", "stellar-xdr", "strum 0.25.0", @@ -7497,9 +7498,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.8.1" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" dependencies = [ "base64 0.22.1", "chrono", @@ -7509,7 +7510,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "serde_with_macros 3.8.1", + "serde_with_macros 3.11.0", "time", ] @@ -7527,9 +7528,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.8.1" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" +checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" dependencies = [ "darling 0.20.9", "proc-macro2 1.0.85", @@ -7933,7 +7934,7 @@ dependencies = [ "hex", "serde", "serde_json", - "serde_with 3.8.1", + "serde_with 3.11.0", "stellar-strkey 0.0.8", ] diff --git a/ampd/src/evm/verifier.rs b/ampd/src/evm/verifier.rs index b29c702f0..7879f32aa 100644 --- a/ampd/src/evm/verifier.rs +++ b/ampd/src/evm/verifier.rs @@ -2,7 +2,6 @@ use axelar_wasm_std::voting::Vote; use ethers_contract::EthLogDecode; use ethers_core::types::{Log, TransactionReceipt, H256}; use evm_gateway::{IAxelarAmplifierGatewayEvents, WeightedSigners}; -use num_traits::cast; use crate::handlers::evm_verify_msg::Message; use crate::handlers::evm_verify_verifier_set::VerifierSetConfirmation; @@ -16,7 +15,7 @@ impl PartialEq> for &Message { match event { IAxelarAmplifierGatewayEvents::ContractCallFilter(event) => { - log.transaction_hash == Some(self.tx_id) + log.transaction_hash == Some(self.message_id.tx_hash.into()) && event.sender == self.source_address && self.destination_chain == event.destination_chain && event.destination_contract_address == self.destination_address @@ -37,7 +36,7 @@ impl PartialEq> for &VerifierSetConfirmation { Err(_) => return false, }; - log.transaction_hash == Some(self.tx_id) + log.transaction_hash == Some(self.message_id.tx_hash.into()) && event.signers_hash == weighted_signers.hash() && event.signers == weighted_signers.abi_encode() } @@ -53,9 +52,9 @@ fn has_failed(tx_receipt: &TransactionReceipt) -> bool { fn event<'a>( gateway_address: &EVMAddress, tx_receipt: &'a TransactionReceipt, - log_index: u32, + log_index: u64, ) -> Option> { - let log_index: usize = cast(log_index).expect("log_index must be a valid usize"); + let log_index: usize = usize::try_from(log_index).ok()?; tx_receipt .logs @@ -74,7 +73,7 @@ fn verify<'a, V>( tx_receipt: &'a TransactionReceipt, to_verify: V, expected_transaction_hash: H256, - expected_event_index: u32, + expected_event_index: u64, ) -> Vote where V: PartialEq>, @@ -98,7 +97,13 @@ pub fn verify_message( tx_receipt: &TransactionReceipt, msg: &Message, ) -> Vote { - verify(gateway_address, tx_receipt, msg, msg.tx_id, msg.event_index) + verify( + gateway_address, + tx_receipt, + msg, + msg.message_id.tx_hash.into(), + msg.message_id.event_index, + ) } pub fn verify_verifier_set( @@ -110,13 +115,14 @@ pub fn verify_verifier_set( gateway_address, tx_receipt, confirmation, - confirmation.tx_id, - confirmation.event_index, + confirmation.message_id.tx_hash.into(), + confirmation.message_id.event_index, ) } #[cfg(test)] mod tests { + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; use axelar_wasm_std::voting::Vote; use cosmwasm_std::Uint128; use ethers_contract::EthEvent; @@ -137,7 +143,7 @@ mod tests { let (gateway_address, tx_receipt, mut verifier_set) = matching_verifier_set_and_tx_receipt(); - verifier_set.tx_id = Hash::random(); + verifier_set.message_id.tx_hash = Hash::random().into(); assert_eq!( verify_verifier_set(&gateway_address, &tx_receipt, &verifier_set), Vote::NotFound @@ -172,17 +178,29 @@ mod tests { let (gateway_address, tx_receipt, mut verifier_set) = matching_verifier_set_and_tx_receipt(); - verifier_set.event_index = 0; + verifier_set.message_id.event_index = 0; assert_eq!( verify_verifier_set(&gateway_address, &tx_receipt, &verifier_set), Vote::NotFound ); - verifier_set.event_index = 2; + verifier_set.message_id.event_index = 2; assert_eq!( verify_verifier_set(&gateway_address, &tx_receipt, &verifier_set), Vote::NotFound ); - verifier_set.event_index = 3; + verifier_set.message_id.event_index = 3; + assert_eq!( + verify_verifier_set(&gateway_address, &tx_receipt, &verifier_set), + Vote::NotFound + ); + } + + #[test] + fn should_not_verify_verifier_set_if_log_index_greater_than_u32_max() { + let (gateway_address, tx_receipt, mut verifier_set) = + matching_verifier_set_and_tx_receipt(); + + verifier_set.message_id.event_index = u32::MAX as u64 + 1; assert_eq!( verify_verifier_set(&gateway_address, &tx_receipt, &verifier_set), Vote::NotFound @@ -215,7 +233,7 @@ mod tests { fn should_not_verify_msg_if_tx_id_does_not_match() { let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_receipt(); - msg.tx_id = Hash::random(); + msg.message_id.tx_hash = Hash::random().into(); assert_eq!( verify_message(&gateway_address, &tx_receipt, &msg), Vote::NotFound @@ -248,17 +266,28 @@ mod tests { fn should_not_verify_msg_if_log_index_does_not_match() { let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_receipt(); - msg.event_index = 0; + msg.message_id.event_index = 0; + assert_eq!( + verify_message(&gateway_address, &tx_receipt, &msg), + Vote::NotFound + ); + msg.message_id.event_index = 2; assert_eq!( verify_message(&gateway_address, &tx_receipt, &msg), Vote::NotFound ); - msg.event_index = 2; + msg.message_id.event_index = 3; assert_eq!( verify_message(&gateway_address, &tx_receipt, &msg), Vote::NotFound ); - msg.event_index = 3; + } + + #[test] + fn should_not_verify_msg_if_log_index_greater_than_u32_max() { + let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_receipt(); + + msg.message_id.event_index = u32::MAX as u64 + 1; assert_eq!( verify_message(&gateway_address, &tx_receipt, &msg), Vote::NotFound @@ -295,8 +324,7 @@ mod tests { let verifier_set = build_verifier_set(KeyType::Ecdsa, &ecdsa_test_data::signers()); let verifier_set = VerifierSetConfirmation { - tx_id, - event_index: log_index, + message_id: HexTxHashAndEventIndex::new(tx_id, log_index as u64), verifier_set, }; @@ -331,8 +359,10 @@ mod tests { let gateway_address = EVMAddress::random(); let msg = Message { - tx_id, - event_index: log_index, + message_id: HexTxHashAndEventIndex::new(tx_id, log_index as u64) + .to_string() + .parse() + .unwrap(), source_address: "0xd48e199950589a4336e4dc43bd2c72ba0c0baa86" .parse() .unwrap(), diff --git a/ampd/src/handlers/evm_verify_msg.rs b/ampd/src/handlers/evm_verify_msg.rs index 6fe8ca131..7380a49ca 100644 --- a/ampd/src/handlers/evm_verify_msg.rs +++ b/ampd/src/handlers/evm_verify_msg.rs @@ -32,8 +32,7 @@ type Result = error_stack::Result; #[derive(Deserialize, Debug)] pub struct Message { - pub tx_id: Hash, - pub event_index: u32, + pub message_id: HexTxHashAndEventIndex, pub destination_address: String, pub destination_chain: ChainName, pub source_address: EVMAddress, @@ -121,7 +120,6 @@ where }) .collect()) } - fn vote_msg(&self, poll_id: PollId, votes: Vec) -> MsgExecuteContract { MsgExecuteContract { sender: self.verifier.as_ref().clone(), @@ -174,24 +172,25 @@ where return Ok(vec![]); } - let tx_hashes: HashSet<_> = messages.iter().map(|message| message.tx_id).collect(); + let tx_hashes: HashSet = messages + .iter() + .map(|msg| msg.message_id.tx_hash.into()) + .collect(); let finalized_tx_receipts = self .finalized_tx_receipts(tx_hashes, confirmation_height) .await?; let poll_id_str: String = poll_id.into(); let source_chain_str: String = source_chain.into(); - let message_ids = messages - .iter() - .map(|message| { - HexTxHashAndEventIndex::new(message.tx_id, message.event_index).to_string() - }) - .collect::>(); let votes = info_span!( "verify messages from an EVM chain", poll_id = poll_id_str, source_chain = source_chain_str, - message_ids = message_ids.as_value() + message_ids = messages + .iter() + .map(|msg| msg.message_id.to_string()) + .collect::>() + .as_value(), ) .in_scope(|| { info!("ready to verify messages in poll",); @@ -200,7 +199,7 @@ where .iter() .map(|msg| { finalized_tx_receipts - .get(&msg.tx_id) + .get(&msg.message_id.tx_hash.into()) .map_or(Vote::NotFound, |tx_receipt| { verify_message(&source_gateway_address, tx_receipt, msg) }) @@ -226,6 +225,7 @@ mod tests { use std::convert::TryInto; use std::str::FromStr; + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; use base64::engine::general_purpose::STANDARD; use base64::Engine; use cosmwasm_std; @@ -247,6 +247,11 @@ mod tests { use crate::PREFIX; fn poll_started_event(participants: Vec, expires_at: u64) -> PollStarted { + let msg_ids = [ + HexTxHashAndEventIndex::new(Hash::random(), 0u64), + HexTxHashAndEventIndex::new(Hash::random(), 1u64), + HexTxHashAndEventIndex::new(Hash::random(), 10u64), + ]; PollStarted::Messages { metadata: PollMetadata { poll_id: "100".parse().unwrap(), @@ -261,26 +266,30 @@ mod tests { .map(|addr| cosmwasm_std::Addr::unchecked(addr.to_string())) .collect(), }, + #[allow(deprecated)] // TODO: The below events use the deprecated tx_id and event_index fields. Remove this attribute when those fields are removed messages: vec![ TxEventConfirmation { - tx_id: format!("0x{:x}", Hash::random()).parse().unwrap(), - event_index: 0, + tx_id: msg_ids[0].tx_hash_as_hex(), + event_index: u32::try_from(msg_ids[0].event_index).unwrap(), + message_id: msg_ids[0].to_string().parse().unwrap(), source_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), destination_chain: "ethereum".parse().unwrap(), destination_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), payload_hash: Hash::random().to_fixed_bytes(), }, TxEventConfirmation { - tx_id: format!("0x{:x}", Hash::random()).parse().unwrap(), - event_index: 1, + tx_id: msg_ids[1].tx_hash_as_hex(), + event_index: u32::try_from(msg_ids[1].event_index).unwrap(), + message_id: msg_ids[1].to_string().parse().unwrap(), source_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), destination_chain: "ethereum".parse().unwrap(), destination_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), payload_hash: Hash::random().to_fixed_bytes(), }, TxEventConfirmation { - tx_id: format!("0x{:x}", Hash::random()).parse().unwrap(), - event_index: 10, + tx_id: msg_ids[2].tx_hash_as_hex(), + event_index: u32::try_from(msg_ids[2].event_index).unwrap(), + message_id: msg_ids[2].to_string().parse().unwrap(), source_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), destination_chain: "ethereum".parse().unwrap(), destination_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), diff --git a/ampd/src/handlers/evm_verify_verifier_set.rs b/ampd/src/handlers/evm_verify_verifier_set.rs index 6097f85b9..85f1ed2a6 100644 --- a/ampd/src/handlers/evm_verify_verifier_set.rs +++ b/ampd/src/handlers/evm_verify_verifier_set.rs @@ -30,8 +30,7 @@ type Result = error_stack::Result; #[derive(Deserialize, Debug)] pub struct VerifierSetConfirmation { - pub tx_id: Hash, - pub event_index: u32, + pub message_id: HexTxHashAndEventIndex, pub verifier_set: VerifierSet, } @@ -166,14 +165,13 @@ where } let tx_receipt = self - .finalized_tx_receipt(verifier_set.tx_id, confirmation_height) + .finalized_tx_receipt(verifier_set.message_id.tx_hash.into(), confirmation_height) .await?; let vote = info_span!( "verify a new verifier set for an EVM chain", poll_id = poll_id.to_string(), source_chain = source_chain.to_string(), - id = HexTxHashAndEventIndex::new(verifier_set.tx_id, verifier_set.event_index) - .to_string() + id = verifier_set.message_id.to_string() ) .in_scope(|| { info!("ready to verify a new verifier set in poll"); @@ -201,6 +199,7 @@ mod tests { use std::convert::TryInto; use std::str::FromStr; + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; use base64::engine::general_purpose::STANDARD; use base64::Engine; use error_stack::{Report, Result}; @@ -271,10 +270,13 @@ mod tests { } fn poll_started_event(participants: Vec, expires_at: u64) -> PollStarted { + let msg_id = HexTxHashAndEventIndex::new(Hash::random(), 100u64); PollStarted::VerifierSet { + #[allow(deprecated)] // TODO: The below event uses the deprecated tx_id and event_index fields. Remove this attribute when those fields are removed verifier_set: VerifierSetConfirmation { - tx_id: format!("0x{:x}", Hash::random()).parse().unwrap(), - event_index: 100, + tx_id: msg_id.tx_hash_as_hex(), + event_index: u32::try_from(msg_id.event_index).unwrap(), + message_id: msg_id.to_string().parse().unwrap(), verifier_set: build_verifier_set(KeyType::Ecdsa, &ecdsa_test_data::signers()), }, metadata: PollMetadata { diff --git a/ampd/src/handlers/mvx_verify_msg.rs b/ampd/src/handlers/mvx_verify_msg.rs index a836ef40e..afed50e88 100644 --- a/ampd/src/handlers/mvx_verify_msg.rs +++ b/ampd/src/handlers/mvx_verify_msg.rs @@ -316,11 +316,17 @@ mod tests { .map(|addr| cosmwasm_std::Addr::unchecked(addr.to_string())) .collect(), }, + #[allow(deprecated)] // TODO: The below event uses the deprecated tx_id and event_index fields. Remove this attribute when those fields are removed messages: vec![TxEventConfirmation { tx_id: "dfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312" .parse() .unwrap(), event_index: 1, + message_id: + "0xdfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312-1" + .to_string() + .parse() + .unwrap(), source_address: "erd1qqqqqqqqqqqqqpgqzqvm5ywqqf524efwrhr039tjs29w0qltkklsa05pk7" .parse() .unwrap(), diff --git a/ampd/src/handlers/mvx_verify_verifier_set.rs b/ampd/src/handlers/mvx_verify_verifier_set.rs index 1953ee000..efceec210 100644 --- a/ampd/src/handlers/mvx_verify_verifier_set.rs +++ b/ampd/src/handlers/mvx_verify_verifier_set.rs @@ -343,11 +343,16 @@ mod tests { .map(|addr| cosmwasm_std::Addr::unchecked(addr.to_string())) .collect(), }, + #[allow(deprecated)] // TODO: The below event uses the deprecated tx_id and event_index fields. Remove this attribute when those fields are removed verifier_set: VerifierSetConfirmation { tx_id: "dfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312" .parse() .unwrap(), event_index: 1, + message_id: "dfaf64de66510723f2efbacd7ead3c4f8c856aed1afc2cb30254552aeda47312-1" + .to_string() + .try_into() + .unwrap(), verifier_set: build_verifier_set(KeyType::Ed25519, &ed25519_test_data::signers()), }, } diff --git a/ampd/src/handlers/stellar_verify_msg.rs b/ampd/src/handlers/stellar_verify_msg.rs index 7de378a12..a4ab51ab6 100644 --- a/ampd/src/handlers/stellar_verify_msg.rs +++ b/ampd/src/handlers/stellar_verify_msg.rs @@ -2,6 +2,7 @@ use std::collections::HashSet; use std::convert::TryInto; use async_trait::async_trait; +use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; use axelar_wasm_std::voting::{PollId, Vote}; use cosmrs::cosmwasm::MsgExecuteContract; use cosmrs::tx::Msg; @@ -11,8 +12,7 @@ use events::Event; use events_derive::try_from; use prost_types::Any; use router_api::ChainName; -use serde::de::Error as DeserializeError; -use serde::{Deserialize, Deserializer}; +use serde::Deserialize; use serde_with::{serde_as, DisplayFromStr}; use stellar_xdr::curr::{ScAddress, ScBytes, ScString}; use tokio::sync::watch::Receiver; @@ -27,24 +27,10 @@ use crate::stellar::http_client::Client; use crate::stellar::verifier::verify_message; use crate::types::TMAddress; -pub fn deserialize_tx_id<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - let tx_id = String::deserialize(deserializer)?; - - tx_id - .strip_prefix("0x") - .map(String::from) - .ok_or(D::Error::custom(Error::DeserializeEvent)) -} - #[serde_as] #[derive(Deserialize, Debug, Clone)] pub struct Message { - #[serde(deserialize_with = "deserialize_tx_id")] - pub tx_id: String, - pub event_index: u32, + pub message_id: HexTxHashAndEventIndex, pub destination_address: ScString, pub destination_chain: ScString, #[serde_as(as = "DisplayFromStr")] @@ -132,7 +118,7 @@ impl EventHandler for Handler { let tx_hashes: HashSet<_> = messages .iter() - .map(|message| message.tx_id.clone()) + .map(|message| message.message_id.tx_hash_as_hex_no_prefix().to_string()) .collect(); let transaction_responses = self @@ -143,7 +129,7 @@ impl EventHandler for Handler { let message_ids = messages .iter() - .map(|message| format!("{}-{}", message.tx_id.clone(), message.event_index)) + .map(|message| message.message_id.to_string()) .collect::>(); let votes = info_span!( @@ -159,7 +145,7 @@ impl EventHandler for Handler { .iter() .map(|msg| { transaction_responses - .get(&msg.tx_id) + .get(&msg.message_id.tx_hash_as_hex_no_prefix().to_string()) .map_or(Vote::NotFound, |tx_response| { verify_message(&source_gateway_address, tx_response, msg) }) @@ -185,6 +171,7 @@ mod tests { use std::collections::HashMap; use std::convert::TryInto; + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; use cosmrs::cosmwasm::MsgExecuteContract; use cosmrs::tx::Msg; use error_stack::Result; @@ -329,18 +316,26 @@ mod tests { .collect(), }, messages: (0..2) - .map(|i| TxEventConfirmation { - tx_id: format!("0x{:x}", Hash::random()).parse().unwrap(), - event_index: i, - source_address: ScAddress::Contract(stellar_xdr::curr::Hash::from( - Hash::random().0, - )) - .to_string() - .try_into() - .unwrap(), - destination_chain: "ethereum".parse().unwrap(), - destination_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), - payload_hash: Hash::random().to_fixed_bytes(), + .map(|i| { + let msg_id = HexTxHashAndEventIndex::new(Hash::random(), i as u64); + #[allow(deprecated)] + // TODO: The below event uses the deprecated tx_id and event_index fields. Remove this attribute when those fields are removed + TxEventConfirmation { + tx_id: msg_id.tx_hash_as_hex(), + event_index: u32::try_from(msg_id.event_index).unwrap(), + message_id: msg_id.to_string().parse().unwrap(), + source_address: ScAddress::Contract(stellar_xdr::curr::Hash::from( + Hash::random().0, + )) + .to_string() + .try_into() + .unwrap(), + destination_chain: "ethereum".parse().unwrap(), + destination_address: format!("0x{:x}", EVMAddress::random()) + .parse() + .unwrap(), + payload_hash: Hash::random().to_fixed_bytes(), + } }) .collect::>(), } diff --git a/ampd/src/handlers/stellar_verify_verifier_set.rs b/ampd/src/handlers/stellar_verify_verifier_set.rs index a7eb57f57..fb77e519f 100644 --- a/ampd/src/handlers/stellar_verify_verifier_set.rs +++ b/ampd/src/handlers/stellar_verify_verifier_set.rs @@ -1,6 +1,7 @@ use std::convert::TryInto; use async_trait::async_trait; +use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; use axelar_wasm_std::voting::{PollId, Vote}; use cosmrs::cosmwasm::MsgExecuteContract; use cosmrs::tx::Msg; @@ -18,7 +19,6 @@ use tracing::{info, info_span}; use valuable::Valuable; use voting_verifier::msg::ExecuteMsg; -use super::stellar_verify_msg::deserialize_tx_id; use crate::event_processor::EventHandler; use crate::handlers::errors::Error; use crate::handlers::errors::Error::DeserializeEvent; @@ -28,9 +28,7 @@ use crate::types::TMAddress; #[derive(Deserialize, Debug)] pub struct VerifierSetConfirmation { - #[serde(deserialize_with = "deserialize_tx_id")] - pub tx_id: String, - pub event_index: u32, + pub message_id: HexTxHashAndEventIndex, pub verifier_set: VerifierSet, } @@ -112,14 +110,19 @@ impl EventHandler for Handler { let transaction_response = self .http_client - .transaction_response(verifier_set.tx_id.clone()) + .transaction_response( + verifier_set + .message_id + .tx_hash_as_hex_no_prefix() + .to_string(), + ) .await .change_context(Error::TxReceipts)?; let vote = info_span!( "verify a new verifier set", poll_id = poll_id.to_string(), - id = format!("0x{}-{}", verifier_set.tx_id, verifier_set.event_index), + id = verifier_set.message_id.to_string(), ) .in_scope(|| { info!("ready to verify verifier set in poll",); @@ -147,6 +150,7 @@ impl EventHandler for Handler { mod tests { use std::convert::TryInto; + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; use cosmrs::cosmwasm::MsgExecuteContract; use cosmrs::tx::Msg; use error_stack::Result; @@ -275,6 +279,7 @@ mod tests { } fn poll_started_event(participants: Vec, expires_at: u64) -> PollStarted { + let msg_id = HexTxHashAndEventIndex::new(Hash::random(), 0u64); PollStarted::VerifierSet { metadata: PollMetadata { poll_id: "100".parse().unwrap(), @@ -292,9 +297,11 @@ mod tests { .map(|addr| cosmwasm_std::Addr::unchecked(addr.to_string())) .collect(), }, + #[allow(deprecated)] // TODO: The below event uses the deprecated tx_id and event_index fields. Remove this attribute when those fields are removed verifier_set: VerifierSetConfirmation { - tx_id: format!("0x{:x}", Hash::random()).parse().unwrap(), - event_index: 0, + tx_id: msg_id.tx_hash_as_hex(), + event_index: u32::try_from(msg_id.event_index).unwrap(), + message_id: msg_id.to_string().parse().unwrap(), verifier_set: build_verifier_set(KeyType::Ed25519, &ed25519_test_data::signers()), }, } diff --git a/ampd/src/handlers/sui_verify_msg.rs b/ampd/src/handlers/sui_verify_msg.rs index 3d60cb2b0..542084ffc 100644 --- a/ampd/src/handlers/sui_verify_msg.rs +++ b/ampd/src/handlers/sui_verify_msg.rs @@ -2,6 +2,7 @@ use std::collections::HashSet; use std::convert::TryInto; use async_trait::async_trait; +use axelar_wasm_std::msg_id::Base58TxDigestAndEventIndex; use axelar_wasm_std::voting::{PollId, Vote}; use cosmrs::cosmwasm::MsgExecuteContract; use cosmrs::tx::Msg; @@ -11,7 +12,7 @@ use events::Error::EventTypeMismatch; use events::Event; use events_derive::try_from; use serde::Deserialize; -use sui_types::base_types::{SuiAddress, TransactionDigest}; +use sui_types::base_types::SuiAddress; use tokio::sync::watch::Receiver; use tracing::info; use voting_verifier::msg::ExecuteMsg; @@ -26,8 +27,7 @@ type Result = error_stack::Result; #[derive(Deserialize, Debug)] pub struct Message { - pub tx_id: TransactionDigest, - pub event_index: u32, + pub message_id: Base58TxDigestAndEventIndex, pub destination_address: String, pub destination_chain: router_api::ChainName, pub source_address: SuiAddress, @@ -122,7 +122,10 @@ where // Does not assume voting verifier emits unique tx ids. // RPC will throw an error if the input contains any duplicate, deduplicate tx ids to avoid unnecessary failures. - let deduplicated_tx_ids: HashSet<_> = messages.iter().map(|msg| msg.tx_id).collect(); + let deduplicated_tx_ids: HashSet<_> = messages + .iter() + .map(|msg| msg.message_id.tx_digest.into()) + .collect(); let transaction_blocks = self .rpc_client .finalized_transaction_blocks(deduplicated_tx_ids) @@ -133,7 +136,7 @@ where .iter() .map(|msg| { transaction_blocks - .get(&msg.tx_id) + .get(&msg.message_id.tx_digest.into()) .map_or(Vote::NotFound, |tx_block| { verify_message(&source_gateway_address, tx_block, msg) }) @@ -152,6 +155,7 @@ mod tests { use std::collections::HashMap; use std::convert::TryInto; + use axelar_wasm_std::msg_id::Base58TxDigestAndEventIndex; use cosmrs::cosmwasm::MsgExecuteContract; use cosmrs::tx::Msg; use cosmwasm_std; @@ -322,6 +326,7 @@ mod tests { } fn poll_started_event(participants: Vec, expires_at: u64) -> PollStarted { + let msg_id = Base58TxDigestAndEventIndex::new(TransactionDigest::random(), 0u64); PollStarted::Messages { metadata: PollMetadata { poll_id: "100".parse().unwrap(), @@ -337,9 +342,11 @@ mod tests { .map(|addr| cosmwasm_std::Addr::unchecked(addr.to_string())) .collect(), }, + #[allow(deprecated)] // TODO: The below event uses the deprecated tx_id and event_index fields. Remove this attribute when those fields are removed messages: vec![TxEventConfirmation { - tx_id: TransactionDigest::random().to_string().parse().unwrap(), - event_index: 0, + tx_id: msg_id.tx_digest_as_base58(), + event_index: u32::try_from(msg_id.event_index).unwrap(), + message_id: msg_id.to_string().parse().unwrap(), source_address: SuiAddress::random_for_testing_only() .to_string() .parse() diff --git a/ampd/src/handlers/sui_verify_verifier_set.rs b/ampd/src/handlers/sui_verify_verifier_set.rs index 4a159c916..2b42d9d44 100644 --- a/ampd/src/handlers/sui_verify_verifier_set.rs +++ b/ampd/src/handlers/sui_verify_verifier_set.rs @@ -12,7 +12,7 @@ use events::Event; use events_derive::try_from; use multisig::verifier_set::VerifierSet; use serde::Deserialize; -use sui_types::base_types::{SuiAddress, TransactionDigest}; +use sui_types::base_types::SuiAddress; use tokio::sync::watch::Receiver; use tracing::{info, info_span}; use valuable::Valuable; @@ -26,8 +26,7 @@ use crate::types::TMAddress; #[derive(Deserialize, Debug)] pub struct VerifierSetConfirmation { - pub tx_id: TransactionDigest, - pub event_index: u32, + pub message_id: Base58TxDigestAndEventIndex, pub verifier_set: VerifierSet, } @@ -121,15 +120,14 @@ where let transaction_block = self .rpc_client - .finalized_transaction_block(verifier_set.tx_id) + .finalized_transaction_block(verifier_set.message_id.tx_digest.into()) .await .change_context(Error::TxReceipts)?; let vote = info_span!( "verify a new verifier set for Sui", poll_id = poll_id.to_string(), - id = Base58TxDigestAndEventIndex::new(verifier_set.tx_id, verifier_set.event_index) - .to_string() + id = verifier_set.message_id.to_string() ) .in_scope(|| { let vote = transaction_block.map_or(Vote::NotFound, |tx_receipt| { @@ -155,6 +153,7 @@ where mod tests { use std::convert::TryInto; + use axelar_wasm_std::msg_id::Base58TxDigestAndEventIndex; use error_stack::{Report, Result}; use ethers_providers::ProviderError; use events::Event; @@ -225,6 +224,7 @@ mod tests { participants: Vec, expires_at: u64, ) -> PollStarted { + let msg_id = Base58TxDigestAndEventIndex::new(TransactionDigest::random(), 0u64); PollStarted::VerifierSet { metadata: PollMetadata { poll_id: "100".parse().unwrap(), @@ -240,9 +240,11 @@ mod tests { .map(|addr| cosmwasm_std::Addr::unchecked(addr.to_string())) .collect(), }, + #[allow(deprecated)] // TODO: The below event uses the deprecated tx_id and event_index fields. Remove this attribute when those fields are removed verifier_set: VerifierSetConfirmation { - tx_id: TransactionDigest::random().to_string().parse().unwrap(), - event_index: 0, + tx_id: msg_id.tx_digest_as_base58(), + event_index: u32::try_from(msg_id.event_index).unwrap(), + message_id: msg_id.to_string().parse().unwrap(), verifier_set: build_verifier_set(KeyType::Ecdsa, &ecdsa_test_data::signers()), }, } diff --git a/ampd/src/stellar/http_client.rs b/ampd/src/stellar/http_client.rs index 469d8774a..568b0304e 100644 --- a/ampd/src/stellar/http_client.rs +++ b/ampd/src/stellar/http_client.rs @@ -3,7 +3,6 @@ use std::str::FromStr; use error_stack::{report, Result}; use futures::future::join_all; -use num_traits::cast; use stellar_rs::horizon_client::HorizonClient; use stellar_rs::transactions::prelude::{SingleTransactionRequest, TransactionResponse}; use stellar_xdr::curr::{ContractEvent, Limits, ReadXdr, ScAddress, TransactionMeta, VecM}; @@ -53,10 +52,10 @@ impl TxResponse { !self.successful } - pub fn event(&self, index: u32) -> Option<&ContractEvent> { + pub fn event(&self, index: u64) -> Option<&ContractEvent> { match self.contract_events { Some(ref events) => { - let log_index: usize = cast(index).expect("event index must be a valid usize"); + let log_index = usize::try_from(index).ok()?; events.get(log_index) } None => None, diff --git a/ampd/src/stellar/verifier.rs b/ampd/src/stellar/verifier.rs index 6dbd54b5b..5efa0e926 100644 --- a/ampd/src/stellar/verifier.rs +++ b/ampd/src/stellar/verifier.rs @@ -74,8 +74,8 @@ pub fn verify_message(gateway_address: &ScAddress, tx_receipt: &TxResponse, msg: gateway_address, tx_receipt, msg, - msg.tx_id.clone(), - msg.event_index, + msg.message_id.tx_hash_as_hex_no_prefix().to_string(), + msg.message_id.event_index, ) } @@ -88,8 +88,11 @@ pub fn verify_verifier_set( gateway_address, tx_receipt, verifier_set_confirmation, - verifier_set_confirmation.tx_id.clone(), - verifier_set_confirmation.event_index, + verifier_set_confirmation + .message_id + .tx_hash_as_hex_no_prefix() + .to_string(), + verifier_set_confirmation.message_id.event_index, ) } @@ -98,7 +101,7 @@ fn verify<'a>( tx_receipt: &'a TxResponse, to_verify: impl PartialEq<&'a ContractEventBody>, expected_tx_id: String, - expected_event_index: u32, + expected_event_index: u64, ) -> Vote { if expected_tx_id != tx_receipt.transaction_hash { return Vote::NotFound; @@ -126,6 +129,7 @@ fn verify<'a>( mod test { use std::str::FromStr; + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; use axelar_wasm_std::voting::Vote; use cosmrs::tx::MessageExt; use cosmwasm_std::{Addr, HexBinary, Uint128}; @@ -152,7 +156,7 @@ mod test { #[test] fn should_not_verify_msg_if_tx_id_does_not_match() { let (gateway_address, tx_response, mut msg) = matching_msg_and_tx_block(); - msg.tx_id = "different_tx_hash".to_string(); + msg.message_id.tx_hash = Hash::random().into(); assert_eq!( verify_message(&gateway_address, &tx_response, &msg), @@ -163,7 +167,7 @@ mod test { #[test] fn should_not_verify_msg_if_event_index_does_not_match() { let (gateway_address, tx_response, mut msg) = matching_msg_and_tx_block(); - msg.event_index = 1; + msg.message_id.event_index = 1; assert_eq!( verify_message(&gateway_address, &tx_response, &msg), @@ -236,7 +240,7 @@ mod test { #[test] fn should_not_verify_verifier_set_if_tx_id_does_not_match() { let (gateway_address, tx_response, mut confirmation) = matching_verifier_set_and_tx_block(); - confirmation.tx_id = "different_tx_hash".to_string(); + confirmation.message_id.tx_hash = Hash::random().into(); assert_eq!( verify_verifier_set(&gateway_address, &tx_response, &confirmation), @@ -247,7 +251,7 @@ mod test { #[test] fn should_not_verify_verifier_set_if_event_index_does_not_match() { let (gateway_address, tx_response, mut confirmation) = matching_verifier_set_and_tx_block(); - confirmation.event_index = 1; + confirmation.message_id.event_index = 1; assert_eq!( verify_verifier_set(&gateway_address, &tx_response, &confirmation), @@ -292,8 +296,7 @@ mod test { let signing_key = SigningKey::generate(&mut OsRng); let msg = Message { - tx_id: format!("{:x}", Hash::random()), - event_index: 0, + message_id: HexTxHashAndEventIndex::new(Hash::random(), 0u64), source_address: ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519( Uint256::from(signing_key.verifying_key().to_bytes()), ))), @@ -326,7 +329,7 @@ mod test { }; let tx_response = TxResponse { - transaction_hash: msg.tx_id.clone(), + transaction_hash: msg.message_id.tx_hash_as_hex_no_prefix().to_string(), source_address: msg.source_address.clone(), successful: true, contract_events: Some(vec![event].try_into().unwrap()), @@ -344,8 +347,7 @@ mod test { let threshold = Uint128::new(2u128); let verifier_set_confirmation = VerifierSetConfirmation { - tx_id: format!("{:x}", Hash::random()), - event_index: 0, + message_id: HexTxHashAndEventIndex::new(Hash::random(), 0u64), verifier_set: VerifierSet { signers: signers .iter() @@ -383,7 +385,10 @@ mod test { }; let tx_response = TxResponse { - transaction_hash: verifier_set_confirmation.tx_id.clone(), + transaction_hash: verifier_set_confirmation + .message_id + .tx_hash_as_hex_no_prefix() + .to_string(), source_address: ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519( Uint256::from(SigningKey::generate(&mut OsRng).verifying_key().to_bytes()), ))), diff --git a/ampd/src/sui/verifier.rs b/ampd/src/sui/verifier.rs index b16dba471..9b772cc4c 100644 --- a/ampd/src/sui/verifier.rs +++ b/ampd/src/sui/verifier.rs @@ -107,9 +107,9 @@ pub fn verify_message( transaction_block: &SuiTransactionBlockResponse, message: &Message, ) -> Vote { - match find_event(transaction_block, message.event_index as u64) { + match find_event(transaction_block, message.message_id.event_index) { Some(event) - if transaction_block.digest == message.tx_id + if transaction_block.digest == message.message_id.tx_digest.into() && event.type_ == EventType::ContractCall.struct_tag(gateway_address) && event == message => { @@ -124,9 +124,9 @@ pub fn verify_verifier_set( transaction_block: &SuiTransactionBlockResponse, confirmation: &VerifierSetConfirmation, ) -> Vote { - match find_event(transaction_block, confirmation.event_index as u64) { + match find_event(transaction_block, confirmation.message_id.event_index) { Some(event) - if transaction_block.digest == confirmation.tx_id + if transaction_block.digest == confirmation.message_id.tx_digest.into() && event.type_ == EventType::SignersRotated.struct_tag(gateway_address) && event == confirmation => { @@ -138,6 +138,7 @@ pub fn verify_verifier_set( #[cfg(test)] mod tests { + use axelar_wasm_std::msg_id::Base58TxDigestAndEventIndex; use axelar_wasm_std::voting::Vote; use cosmrs::crypto::PublicKey; use cosmwasm_std::{Addr, HexBinary, Uint128}; @@ -166,7 +167,7 @@ mod tests { fn should_not_verify_msg_if_tx_id_does_not_match() { let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_block(); - msg.tx_id = TransactionDigest::random(); + msg.message_id.tx_digest = TransactionDigest::random().into(); assert_eq!( verify_message(&gateway_address, &tx_receipt, &msg), Vote::NotFound @@ -177,7 +178,7 @@ mod tests { fn should_not_verify_msg_if_event_index_does_not_match() { let (gateway_address, tx_receipt, mut msg) = matching_msg_and_tx_block(); - msg.event_index = rand::random::(); + msg.message_id.event_index = rand::random::(); assert_eq!( verify_message(&gateway_address, &tx_receipt, &msg), Vote::NotFound @@ -275,7 +276,7 @@ mod tests { #[test] fn should_not_verify_verifier_set_if_event_seq_mismatch() { let (gateway_address, tx_block, mut verifier_set) = matching_verifier_set_and_tx_block(); - verifier_set.event_index = rand::random(); + verifier_set.message_id.event_index = rand::random(); assert_eq!( verify_verifier_set(&gateway_address, &tx_block, &verifier_set), @@ -347,8 +348,10 @@ mod tests { let gateway_address = SuiAddress::random_for_testing_only(); let msg = Message { - tx_id: TransactionDigest::random(), - event_index: rand::random::(), + message_id: Base58TxDigestAndEventIndex::new( + TransactionDigest::random(), + rand::random::(), + ), source_address: SuiAddress::random_for_testing_only(), destination_chain: rand_chain_name(), destination_address: format!("0x{:x}", EVMAddress::random()).parse().unwrap(), @@ -364,8 +367,8 @@ mod tests { }; let event = SuiEvent { id: EventID { - tx_digest: msg.tx_id, - event_seq: msg.event_index as u64, + tx_digest: msg.message_id.tx_digest.into(), + event_seq: msg.message_id.event_index, }, package_id: gateway_address.into(), transaction_module: "gateway".parse().unwrap(), @@ -382,7 +385,7 @@ mod tests { }; let tx_block = SuiTransactionBlockResponse { - digest: msg.tx_id, + digest: msg.message_id.tx_digest.into(), events: Some(SuiTransactionBlockEvents { data: vec![event] }), ..Default::default() }; @@ -415,8 +418,10 @@ mod tests { let created_at = rand::random(); let threshold = Uint128::one(); let verifier_set_confirmation = VerifierSetConfirmation { - tx_id: TransactionDigest::random(), - event_index: rand::random(), + message_id: Base58TxDigestAndEventIndex::new( + TransactionDigest::random(), + rand::random::(), + ), verifier_set: VerifierSet { signers: signers .iter() @@ -451,8 +456,8 @@ mod tests { }; let event = SuiEvent { id: EventID { - tx_digest: verifier_set_confirmation.tx_id, - event_seq: verifier_set_confirmation.event_index as u64, + tx_digest: verifier_set_confirmation.message_id.tx_digest.into(), + event_seq: verifier_set_confirmation.message_id.event_index, }, package_id: gateway_address.into(), transaction_module: "gateway".parse().unwrap(), @@ -469,7 +474,7 @@ mod tests { }; let tx_block = SuiTransactionBlockResponse { - digest: verifier_set_confirmation.tx_id, + digest: verifier_set_confirmation.message_id.tx_digest.into(), events: Some(SuiTransactionBlockEvents { data: vec![event] }), ..Default::default() }; diff --git a/contracts/router/src/contract/execute.rs b/contracts/router/src/contract/execute.rs index 08334bd78..7a2790771 100644 --- a/contracts/router/src/contract/execute.rs +++ b/contracts/router/src/contract/execute.rs @@ -286,7 +286,7 @@ mod test { let id = HexTxHashAndEventIndex { tx_hash: bytes, - event_index: random::(), + event_index: random::(), } .to_string(); diff --git a/contracts/voting-verifier/src/contract.rs b/contracts/voting-verifier/src/contract.rs index bf68113eb..099f392a9 100644 --- a/contracts/voting-verifier/src/contract.rs +++ b/contracts/voting-verifier/src/contract.rs @@ -72,7 +72,7 @@ pub fn execute( } => Ok(execute::verify_verifier_set( deps, env, - &message_id, + message_id, new_verifier_set, )?), ExecuteMsg::UpdateVotingThreshold { @@ -232,7 +232,7 @@ mod test { deps } - fn message_id(id: &str, index: u32, msg_id_format: &MessageIdFormat) -> nonempty::String { + fn message_id(id: &str, index: u64, msg_id_format: &MessageIdFormat) -> nonempty::String { match msg_id_format { MessageIdFormat::HexTxHashAndEventIndex => HexTxHashAndEventIndex { tx_hash: Keccak256::digest(id.as_bytes()).into(), @@ -266,7 +266,7 @@ mod test { } } - fn messages(len: u32, msg_id_format: &MessageIdFormat) -> Vec { + fn messages(len: u64, msg_id_format: &MessageIdFormat) -> Vec { (0..len) .map(|i| Message { cc_id: CrossChainId::new(source_chain(), message_id("id", i, msg_id_format)) @@ -477,7 +477,7 @@ mod test { let mut deps = setup(verifiers.clone(), &msg_id_format); let messages_count = 5; let messages_in_progress = 3; - let messages = messages(messages_count as u32, &msg_id_format); + let messages = messages(messages_count as u64, &msg_id_format); execute( deps.as_mut(), diff --git a/contracts/voting-verifier/src/contract/execute.rs b/contracts/voting-verifier/src/contract/execute.rs index 79766491d..fc8fe18ec 100644 --- a/contracts/voting-verifier/src/contract/execute.rs +++ b/contracts/voting-verifier/src/contract/execute.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use axelar_wasm_std::address::{validate_address, AddressFormat}; use axelar_wasm_std::utils::TryMapExt; use axelar_wasm_std::voting::{PollId, PollResults, Vote, WeightedPoll}; -use axelar_wasm_std::{snapshot, MajorityThreshold, VerificationStatus}; +use axelar_wasm_std::{nonempty, snapshot, MajorityThreshold, VerificationStatus}; use cosmwasm_std::{ to_json_binary, Deps, DepsMut, Env, Event, MessageInfo, OverflowError, OverflowOperation, Response, Storage, WasmMsg, @@ -43,7 +43,7 @@ pub fn update_voting_threshold( pub fn verify_verifier_set( deps: DepsMut, env: Env, - message_id: &str, + message_id: nonempty::String, new_verifier_set: VerifierSet, ) -> Result { let status = verifier_set_status(deps.as_ref(), &new_verifier_set, env.block.height)?; diff --git a/contracts/voting-verifier/src/contract/query.rs b/contracts/voting-verifier/src/contract/query.rs index 56726c2f4..e5f07f971 100644 --- a/contracts/voting-verifier/src/contract/query.rs +++ b/contracts/voting-verifier/src/contract/query.rs @@ -300,7 +300,7 @@ mod tests { ) .unwrap(); - let messages = (0..poll.poll_size as u32).map(message); + let messages = (0..poll.poll_size as u64).map(message); messages.clone().enumerate().for_each(|(idx, msg)| { poll_messages() .save( @@ -321,7 +321,7 @@ mod tests { ); } - fn message(id: u32) -> Message { + fn message(id: u64) -> Message { Message { cc_id: CrossChainId::new( "source-chain", diff --git a/contracts/voting-verifier/src/events.rs b/contracts/voting-verifier/src/events.rs index 61d2cf404..6a4b527ac 100644 --- a/contracts/voting-verifier/src/events.rs +++ b/contracts/voting-verifier/src/events.rs @@ -136,12 +136,16 @@ impl From for Event { #[cw_serde] pub struct VerifierSetConfirmation { + #[deprecated(since = "1.1.0", note = "use message_id field instead")] pub tx_id: nonempty::String, + #[deprecated(since = "1.1.0", note = "use message_id field instead")] pub event_index: u32, + pub message_id: nonempty::String, pub verifier_set: VerifierSet, } /// If parsing is successful, returns (tx_id, event_index). Otherwise returns ContractError::InvalidMessageID +#[deprecated(since = "1.1.0", note = "don't parse message id, just emit as is")] fn parse_message_id( message_id: &str, msg_id_format: &MessageIdFormat, @@ -150,19 +154,31 @@ fn parse_message_id( MessageIdFormat::Base58TxDigestAndEventIndex => { let id = Base58TxDigestAndEventIndex::from_str(message_id) .map_err(|_| ContractError::InvalidMessageID(message_id.to_string()))?; - Ok((id.tx_digest_as_base58(), id.event_index)) + Ok(( + id.tx_digest_as_base58(), + u32::try_from(id.event_index) + .map_err(|_| ContractError::InvalidMessageID(message_id.to_string()))?, + )) } MessageIdFormat::HexTxHashAndEventIndex => { let id = HexTxHashAndEventIndex::from_str(message_id) .map_err(|_| ContractError::InvalidMessageID(message_id.to_string()))?; - Ok((id.tx_hash_as_hex(), id.event_index)) + Ok(( + id.tx_hash_as_hex(), + u32::try_from(id.event_index) + .map_err(|_| ContractError::InvalidMessageID(message_id.to_string()))?, + )) } MessageIdFormat::Base58SolanaTxSignatureAndEventIndex => { let id = Base58SolanaTxSignatureAndEventIndex::from_str(message_id) .map_err(|_| ContractError::InvalidMessageID(message_id.to_string()))?; - Ok((id.signature_as_base58(), id.event_index)) + Ok(( + id.signature_as_base58(), + u32::try_from(id.event_index) + .map_err(|_| ContractError::InvalidMessageID(message_id.to_string()))?, + )) } MessageIdFormat::HexTxHash => { let id = HexTxHash::from_str(message_id) @@ -175,15 +191,19 @@ fn parse_message_id( impl VerifierSetConfirmation { pub fn new( - message_id: &str, + message_id: nonempty::String, msg_id_format: MessageIdFormat, verifier_set: VerifierSet, ) -> Result { - let (tx_id, event_index) = parse_message_id(message_id, &msg_id_format)?; + #[allow(deprecated)] + let (tx_id, event_index) = parse_message_id(&message_id, &msg_id_format)?; + #[allow(deprecated)] + // TODO: remove this attribute when tx_id and event_index are removed from the event Ok(Self { tx_id, event_index, + message_id, verifier_set, }) } @@ -191,8 +211,11 @@ impl VerifierSetConfirmation { #[cw_serde] pub struct TxEventConfirmation { + #[deprecated(since = "1.1.0", note = "use message_id field instead")] pub tx_id: nonempty::String, + #[deprecated(since = "1.1.0", note = "use message_id field instead")] pub event_index: u32, + pub message_id: nonempty::String, pub destination_address: Address, pub destination_chain: ChainName, pub source_address: Address, @@ -206,11 +229,15 @@ pub struct TxEventConfirmation { impl TryFrom<(Message, &MessageIdFormat)> for TxEventConfirmation { type Error = ContractError; fn try_from((msg, msg_id_format): (Message, &MessageIdFormat)) -> Result { + #[allow(deprecated)] let (tx_id, event_index) = parse_message_id(&msg.cc_id.message_id, msg_id_format)?; + #[allow(deprecated)] + // TODO: remove this attribute when tx_id and event_index are removed from the event Ok(TxEventConfirmation { tx_id, event_index, + message_id: msg.cc_id.message_id, destination_address: msg.destination_address, destination_chain: msg.destination_chain, source_address: msg.source_address, @@ -338,8 +365,7 @@ mod test { TxEventConfirmation::try_from((msg.clone(), &MessageIdFormat::HexTxHashAndEventIndex)) .unwrap(); - assert_eq!(event.tx_id, msg_id.tx_hash_as_hex()); - assert_eq!(event.event_index, msg_id.event_index); + assert_eq!(event.message_id, msg.cc_id.message_id); compare_event_to_message(event, msg); } @@ -353,8 +379,7 @@ mod test { let event = TxEventConfirmation::try_from((msg.clone(), &MessageIdFormat::HexTxHash)).unwrap(); - assert_eq!(event.tx_id, msg_id.tx_hash_as_hex()); - assert_eq!(event.event_index, 0); + assert_eq!(event.message_id, msg.cc_id.message_id); compare_event_to_message(event, msg); } @@ -372,8 +397,7 @@ mod test { )) .unwrap(); - assert_eq!(event.tx_id, msg_id.tx_digest_as_base58()); - assert_eq!(event.event_index, msg_id.event_index); + assert_eq!(event.message_id, msg.cc_id.message_id); compare_event_to_message(event, msg); } @@ -404,7 +428,7 @@ mod test { fn should_make_verifier_set_confirmation_with_hex_msg_id() { let msg_id = HexTxHashAndEventIndex { tx_hash: random_32_bytes(), - event_index: rand::random::(), + event_index: rand::random::() as u64, }; let verifier_set = VerifierSet { signers: BTreeMap::new(), @@ -412,14 +436,13 @@ mod test { created_at: 1, }; let event = VerifierSetConfirmation::new( - &msg_id.to_string(), + msg_id.to_string().parse().unwrap(), MessageIdFormat::HexTxHashAndEventIndex, verifier_set.clone(), ) .unwrap(); - assert_eq!(event.tx_id, msg_id.tx_hash_as_hex()); - assert_eq!(event.event_index, msg_id.event_index); + assert_eq!(event.message_id, msg_id.to_string().try_into().unwrap()); assert_eq!(event.verifier_set, verifier_set); } @@ -427,7 +450,7 @@ mod test { fn should_make_verifier_set_confirmation_with_base58_msg_id() { let msg_id = Base58TxDigestAndEventIndex { tx_digest: random_32_bytes(), - event_index: rand::random::(), + event_index: rand::random::() as u64, }; let verifier_set = VerifierSet { signers: BTreeMap::new(), @@ -435,14 +458,13 @@ mod test { created_at: 1, }; let event = VerifierSetConfirmation::new( - &msg_id.to_string(), + msg_id.to_string().parse().unwrap(), MessageIdFormat::Base58TxDigestAndEventIndex, verifier_set.clone(), ) .unwrap(); - assert_eq!(event.tx_id, msg_id.tx_digest_as_base58()); - assert_eq!(event.event_index, msg_id.event_index); + assert_eq!(event.message_id, msg_id.to_string().try_into().unwrap()); assert_eq!(event.verifier_set, verifier_set); } @@ -456,7 +478,7 @@ mod test { }; let event = VerifierSetConfirmation::new( - msg_id, + msg_id.to_string().parse().unwrap(), MessageIdFormat::Base58TxDigestAndEventIndex, verifier_set, ); @@ -467,7 +489,7 @@ mod test { fn make_verifier_set_confirmation_should_fail_with_different_msg_id_format() { let msg_id = HexTxHashAndEventIndex { tx_hash: random_32_bytes(), - event_index: rand::random::(), + event_index: rand::random::(), }; let verifier_set = VerifierSet { signers: BTreeMap::new(), @@ -476,7 +498,7 @@ mod test { }; let event = VerifierSetConfirmation::new( - &msg_id.to_string(), + msg_id.to_string().parse().unwrap(), MessageIdFormat::Base58TxDigestAndEventIndex, verifier_set, ); diff --git a/packages/axelar-core-std/src/nexus/execute.rs b/packages/axelar-core-std/src/nexus/execute.rs index ace895da8..074dc1ff4 100644 --- a/packages/axelar-core-std/src/nexus/execute.rs +++ b/packages/axelar-core-std/src/nexus/execute.rs @@ -34,7 +34,7 @@ fn parse_message_id(message_id: &str) -> Result<(nonempty::Vec, u64), Error> let tx_id = nonempty::Vec::::try_from(id.tx_hash.to_vec()) .change_context(Error::InvalidMessageId(message_id.into()))?; - Ok((tx_id, id.event_index.into())) + Ok((tx_id, id.event_index)) } impl From for Message { @@ -102,7 +102,7 @@ mod test { destination_address: "something else".parse().unwrap(), payload_hash: [1; 32], source_tx_id: msg_id.tx_hash.to_vec().try_into().unwrap(), - source_tx_index: msg_id.event_index as u64, + source_tx_index: msg_id.event_index, id: msg_id.to_string(), }; diff --git a/packages/axelar-wasm-std/Cargo.toml b/packages/axelar-wasm-std/Cargo.toml index 6ad8685eb..3472b6de8 100644 --- a/packages/axelar-wasm-std/Cargo.toml +++ b/packages/axelar-wasm-std/Cargo.toml @@ -45,6 +45,7 @@ report = { workspace = true } schemars = "0.8.10" serde = { version = "1.0.145", default-features = false, features = ["derive"] } serde_json = "1.0.89" +serde_with = { version = "3.11.0", features = ["macros"] } sha3 = { workspace = true } stellar-xdr = { workspace = true } strum = { workspace = true } diff --git a/packages/axelar-wasm-std/src/msg_id/base_58_event_index.rs b/packages/axelar-wasm-std/src/msg_id/base_58_event_index.rs index 1c75542b2..35ae9405a 100644 --- a/packages/axelar-wasm-std/src/msg_id/base_58_event_index.rs +++ b/packages/axelar-wasm-std/src/msg_id/base_58_event_index.rs @@ -5,14 +5,16 @@ use std::str::FromStr; use error_stack::{Report, ResultExt}; use lazy_static::lazy_static; use regex::Regex; +use serde_with::DeserializeFromStr; use super::Error; use crate::hash::Hash; use crate::nonempty; +#[derive(Debug, DeserializeFromStr)] pub struct Base58TxDigestAndEventIndex { pub tx_digest: Hash, - pub event_index: u32, + pub event_index: u64, } impl Base58TxDigestAndEventIndex { @@ -23,7 +25,7 @@ impl Base58TxDigestAndEventIndex { .expect("failed to convert tx hash to non-empty string") } - pub fn new(tx_id: impl Into<[u8; 32]>, event_index: impl Into) -> Self { + pub fn new(tx_id: impl Into<[u8; 32]>, event_index: impl Into) -> Self { Self { tx_digest: tx_id.into(), event_index: event_index.into(), @@ -96,7 +98,7 @@ mod tests { bs58::encode(random_bytes()).into_string() } - fn random_event_index() -> u32 { + fn random_event_index() -> u64 { rand::random() } @@ -285,7 +287,7 @@ mod tests { fn should_not_parse_msg_id_with_overflowing_event_index() { let event_index: u64 = u64::MAX; let tx_digest = random_tx_digest(); - let res = Base58TxDigestAndEventIndex::from_str(&format!("{}-{}", tx_digest, event_index)); + let res = Base58TxDigestAndEventIndex::from_str(&format!("{}-{}1", tx_digest, event_index)); assert!(res.is_err()); } diff --git a/packages/axelar-wasm-std/src/msg_id/base_58_solana_event_index.rs b/packages/axelar-wasm-std/src/msg_id/base_58_solana_event_index.rs index 1a625f910..d90c2e066 100644 --- a/packages/axelar-wasm-std/src/msg_id/base_58_solana_event_index.rs +++ b/packages/axelar-wasm-std/src/msg_id/base_58_solana_event_index.rs @@ -14,7 +14,7 @@ type RawSignature = [u8; 64]; pub struct Base58SolanaTxSignatureAndEventIndex { // Base58 decoded bytes of the Solana signature. pub raw_signature: RawSignature, - pub event_index: u32, + pub event_index: u64, } impl Base58SolanaTxSignatureAndEventIndex { @@ -25,7 +25,7 @@ impl Base58SolanaTxSignatureAndEventIndex { .expect("failed to convert tx hash to non-empty string") } - pub fn new(tx_id: impl Into, event_index: impl Into) -> Self { + pub fn new(tx_id: impl Into, event_index: impl Into) -> Self { Self { raw_signature: tx_id.into(), event_index: event_index.into(), @@ -102,7 +102,7 @@ mod tests { bs58::encode(random_bytes()).into_string() } - fn random_event_index() -> u32 { + fn random_event_index() -> u64 { rand::random() } @@ -327,7 +327,7 @@ mod tests { let event_index: u64 = u64::MAX; let tx_digest = random_tx_digest(); let res = Base58SolanaTxSignatureAndEventIndex::from_str(&format!( - "{}-{}", + "{}-{}1", tx_digest, event_index )); assert!(res.is_err()); diff --git a/packages/axelar-wasm-std/src/msg_id/tx_hash_event_index.rs b/packages/axelar-wasm-std/src/msg_id/tx_hash_event_index.rs index 9f9bf8376..97f269bce 100644 --- a/packages/axelar-wasm-std/src/msg_id/tx_hash_event_index.rs +++ b/packages/axelar-wasm-std/src/msg_id/tx_hash_event_index.rs @@ -6,24 +6,34 @@ use cosmwasm_std::HexBinary; use error_stack::{Report, ResultExt}; use lazy_static::lazy_static; use regex::Regex; +use serde_with::DeserializeFromStr; use super::Error; use crate::hash::Hash; use crate::nonempty; +#[derive(Debug, DeserializeFromStr, Clone)] pub struct HexTxHashAndEventIndex { pub tx_hash: Hash, - pub event_index: u32, + pub event_index: u64, } impl HexTxHashAndEventIndex { pub fn tx_hash_as_hex(&self) -> nonempty::String { - format!("0x{}", HexBinary::from(self.tx_hash).to_hex()) + format!("0x{}", self.tx_hash_as_hex_no_prefix()) .try_into() .expect("failed to convert tx hash to non-empty string") } - pub fn new(tx_id: impl Into<[u8; 32]>, event_index: impl Into) -> Self { + pub fn tx_hash_as_hex_no_prefix(&self) -> nonempty::String { + HexBinary::from(self.tx_hash) + .to_hex() + .to_string() + .try_into() + .expect("failed to convert tx hash to non-empty string") + } + + pub fn new(tx_id: impl Into<[u8; 32]>, event_index: impl Into) -> Self { Self { tx_hash: tx_id.into(), event_index: event_index.into(), @@ -98,7 +108,7 @@ mod tests { format!("0x{}", HexBinary::from(bytes).to_hex()) } - fn random_event_index() -> u32 { + fn random_event_index() -> u64 { rand::random() } @@ -223,7 +233,7 @@ mod tests { fn should_not_parse_msg_id_with_overflowing_event_index() { let event_index: u64 = u64::MAX; let tx_hash = random_hash(); - let res = HexTxHashAndEventIndex::from_str(&format!("{}-{}", tx_hash, event_index)); + let res = HexTxHashAndEventIndex::from_str(&format!("{}-{}1", tx_hash, event_index)); assert!(res.is_err()); } } From dc20405a2b1e5eb327d024ed40b21126fff81219 Mon Sep 17 00:00:00 2001 From: Sammy Date: Wed, 30 Oct 2024 02:53:44 +0800 Subject: [PATCH 02/21] feat(interchain-token-service): add message type SetChainConfig (#669) --- .../interchain-token-service/src/contract.rs | 10 +++- .../src/contract/execute.rs | 30 +++++++++--- contracts/interchain-token-service/src/msg.rs | 8 +++ .../interchain-token-service/src/state.rs | 34 +++++++++++-- .../interchain-token-service/tests/execute.rs | 49 ++++++++++++++++++- .../tests/utils/execute.rs | 19 +++++++ packages/axelar-wasm-std/src/nonempty/uint.rs | 1 + 7 files changed, 137 insertions(+), 14 deletions(-) diff --git a/contracts/interchain-token-service/src/contract.rs b/contracts/interchain-token-service/src/contract.rs index 503ea1539..ee536d5ec 100644 --- a/contracts/interchain-token-service/src/contract.rs +++ b/contracts/interchain-token-service/src/contract.rs @@ -29,8 +29,8 @@ pub enum Error { RegisterItsContract, #[error("failed to deregsiter an its edge contract")] DeregisterItsContract, - #[error("too many coins attached. Execute accepts zero or one coins")] - TooManyCoins, + #[error("failed to set chain config")] + SetChainConfig, #[error("failed to query its address")] QueryItsContract, #[error("failed to query all its addresses")] @@ -99,6 +99,12 @@ pub fn execute( execute::deregister_its_contract(deps, chain) .change_context(Error::DeregisterItsContract) } + ExecuteMsg::SetChainConfig { + chain, + max_uint, + max_target_decimals, + } => execute::set_chain_config(deps, chain, max_uint, max_target_decimals) + .change_context(Error::SetChainConfig), }? .then(Ok) } diff --git a/contracts/interchain-token-service/src/contract/execute.rs b/contracts/interchain-token-service/src/contract/execute.rs index 11f3e0558..0112882ce 100644 --- a/contracts/interchain-token-service/src/contract/execute.rs +++ b/contracts/interchain-token-service/src/contract/execute.rs @@ -1,4 +1,4 @@ -use axelar_wasm_std::IntoContractError; +use axelar_wasm_std::{nonempty, FnExt, IntoContractError}; use cosmwasm_std::{DepsMut, HexBinary, QuerierWrapper, Response, Storage}; use error_stack::{bail, ensure, report, Result, ResultExt}; use router_api::{Address, ChainName, ChainNameRaw, CrossChainId}; @@ -21,12 +21,12 @@ pub enum Error { FailedItsContractRegistration(ChainNameRaw), #[error("failed to deregister its contract for chain {0}")] FailedItsContractDeregistration(ChainNameRaw), - #[error("failed to execute message")] - FailedExecuteMessage, - #[error("failed to query nexus")] - NexusQueryError, - #[error("storage error")] - StorageError, + #[error("chain config for {0} already set")] + ChainConfigAlreadySet(ChainNameRaw), + #[error("invalid chain max uint")] + LoadChainConfig(ChainNameRaw), + #[error("failed to save chain config for chain {0}")] + SaveChainConfig(ChainNameRaw), } /// Executes an incoming ITS message. @@ -132,3 +132,19 @@ pub fn deregister_its_contract(deps: DepsMut, chain: ChainNameRaw) -> Result Result { + match state::may_load_chain_config(deps.storage, &chain) + .change_context_lazy(|| Error::LoadChainConfig(chain.clone()))? + { + Some(_) => bail!(Error::ChainConfigAlreadySet(chain)), + None => state::save_chain_config(deps.storage, &chain, max_uint, max_target_decimals) + .change_context_lazy(|| Error::SaveChainConfig(chain))? + .then(|_| Ok(Response::new())), + } +} diff --git a/contracts/interchain-token-service/src/msg.rs b/contracts/interchain-token-service/src/msg.rs index c9ff32b55..8b570c1c1 100644 --- a/contracts/interchain-token-service/src/msg.rs +++ b/contracts/interchain-token-service/src/msg.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; +use axelar_wasm_std::nonempty; use axelarnet_gateway::AxelarExecutableMsg; use cosmwasm_schema::{cw_serde, QueryResponses}; use msgs_derive::EnsurePermissions; @@ -33,6 +34,13 @@ pub enum ExecuteMsg { /// The admin is allowed to remove the ITS address of a chain for emergencies. #[permission(Elevated)] DeregisterItsContract { chain: ChainNameRaw }, + /// Set the chain configuration for a chain. + #[permission(Governance)] + SetChainConfig { + chain: ChainNameRaw, + max_uint: nonempty::Uint256, // The maximum uint value that is supported by the chain's token standard + max_target_decimals: u8, // The maximum number of decimals that is preserved when deploying a token to anothe chain where smaller uint values are used + }, } #[cw_serde] diff --git a/contracts/interchain-token-service/src/state.rs b/contracts/interchain-token-service/src/state.rs index c977ad5fa..806e7a01c 100644 --- a/contracts/interchain-token-service/src/state.rs +++ b/contracts/interchain-token-service/src/state.rs @@ -10,14 +10,10 @@ use router_api::{Address, ChainNameRaw}; pub enum Error { #[error(transparent)] Std(#[from] StdError), - #[error("ITS contract got into an invalid state, its config is missing")] - MissingConfig, #[error("its address for chain {0} not found")] ItsContractNotFound(ChainNameRaw), #[error("its address for chain {0} already registered")] ItsContractAlreadyRegistered(ChainNameRaw), - #[error("gateway token already registered {0}")] - GatewayTokenAlreadyRegistered(nonempty::String), } #[cw_serde] @@ -25,8 +21,15 @@ pub struct Config { pub axelarnet_gateway: Addr, } +#[cw_serde] +pub struct ChainConfig { + max_uint: nonempty::Uint256, + max_target_decimals: u8, +} + const CONFIG: Item = Item::new("config"); const ITS_CONTRACTS: Map<&ChainNameRaw, Address> = Map::new("its_contracts"); +const CHAIN_CONFIGS: Map<&ChainNameRaw, ChainConfig> = Map::new("chain_configs"); pub fn load_config(storage: &dyn Storage) -> Config { CONFIG @@ -38,6 +41,29 @@ pub fn save_config(storage: &mut dyn Storage, config: &Config) -> Result<(), Err Ok(CONFIG.save(storage, config)?) } +pub fn may_load_chain_config( + storage: &dyn Storage, + chain: &ChainNameRaw, +) -> Result, Error> { + Ok(CHAIN_CONFIGS.may_load(storage, chain)?) +} + +pub fn save_chain_config( + storage: &mut dyn Storage, + chain: &ChainNameRaw, + max_uint: nonempty::Uint256, + max_target_decimals: u8, +) -> Result<(), Error> { + Ok(CHAIN_CONFIGS.save( + storage, + chain, + &ChainConfig { + max_uint, + max_target_decimals, + }, + )?) +} + pub fn may_load_its_contract( storage: &dyn Storage, chain: &ChainNameRaw, diff --git a/contracts/interchain-token-service/tests/execute.rs b/contracts/interchain-token-service/tests/execute.rs index 368d60d39..dd82596d3 100644 --- a/contracts/interchain-token-service/tests/execute.rs +++ b/contracts/interchain-token-service/tests/execute.rs @@ -1,9 +1,11 @@ +use std::str::FromStr; + use assert_ok::assert_ok; use axelar_wasm_std::response::inspect_response_msg; use axelar_wasm_std::{assert_err_contains, permission_control}; use axelarnet_gateway::msg::ExecuteMsg as AxelarnetGatewayExecuteMsg; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; -use cosmwasm_std::HexBinary; +use cosmwasm_std::{HexBinary, Uint256}; use interchain_token_service::contract::{self, ExecuteError}; use interchain_token_service::events::Event; use interchain_token_service::msg::ExecuteMsg; @@ -314,3 +316,48 @@ fn execute_message_when_invalid_message_type_fails() { ); assert_err_contains!(result, ExecuteError, ExecuteError::InvalidMessageType); } + +#[test] +fn set_chain_config_should_succeed() { + let chain = "ethereum".parse().unwrap(); + let max_uint = Uint256::from_str("120000000000000000000000000") + .unwrap() + .try_into() + .unwrap(); + let decimals = 18; + + let mut deps = mock_dependencies(); + utils::instantiate_contract(deps.as_mut()).unwrap(); + + assert_ok!(utils::set_chain_config( + deps.as_mut(), + chain, + max_uint, + decimals + )); +} + +#[test] +fn set_chain_config_should_fail_if_chain_config_is_already_set() { + let chain: ChainNameRaw = "ethereum".parse().unwrap(); + let max_uint = Uint256::from_str("120000000000000000000000000") + .unwrap() + .try_into() + .unwrap(); + let decimals = 18; + + let mut deps = mock_dependencies(); + utils::instantiate_contract(deps.as_mut()).unwrap(); + + assert_ok!(utils::set_chain_config( + deps.as_mut(), + chain.clone(), + max_uint, + decimals + )); + assert_err_contains!( + utils::set_chain_config(deps.as_mut(), chain, max_uint, decimals), + ExecuteError, + ExecuteError::ChainConfigAlreadySet(_) + ) +} diff --git a/contracts/interchain-token-service/tests/utils/execute.rs b/contracts/interchain-token-service/tests/utils/execute.rs index 8dda5fbd5..77d1ba538 100644 --- a/contracts/interchain-token-service/tests/utils/execute.rs +++ b/contracts/interchain-token-service/tests/utils/execute.rs @@ -4,6 +4,7 @@ use axelar_core_std::nexus; use axelar_core_std::nexus::query::IsChainRegisteredResponse; use axelar_core_std::query::AxelarQueryMsg; use axelar_wasm_std::error::ContractError; +use axelar_wasm_std::nonempty; use cosmwasm_std::testing::{mock_env, mock_info, MockApi, MockQuerier, MockStorage}; use cosmwasm_std::{ from_json, to_json_binary, Addr, DepsMut, HexBinary, MemoryStorage, OwnedDeps, Response, @@ -58,6 +59,24 @@ pub fn deregister_its_contract( ) } +pub fn set_chain_config( + deps: DepsMut, + chain: ChainNameRaw, + max_uint: nonempty::Uint256, + max_target_decimals: u8, +) -> Result { + contract::execute( + deps, + mock_env(), + mock_info(params::GOVERNANCE, &[]), + ExecuteMsg::SetChainConfig { + chain, + max_uint, + max_target_decimals, + }, + ) +} + pub fn make_deps() -> OwnedDeps> { let addr = Addr::unchecked(params::GATEWAY); let mut deps = OwnedDeps { diff --git a/packages/axelar-wasm-std/src/nonempty/uint.rs b/packages/axelar-wasm-std/src/nonempty/uint.rs index 861fedaba..db52cb7d9 100644 --- a/packages/axelar-wasm-std/src/nonempty/uint.rs +++ b/packages/axelar-wasm-std/src/nonempty/uint.rs @@ -53,6 +53,7 @@ impl fmt::Display for Uint64 { // TODO: consider using macro for these types #[cw_serde] #[derive(Copy, PartialOrd, Eq, IntoInner)] +#[serde(try_from = "cosmwasm_std::Uint256")] pub struct Uint256(cosmwasm_std::Uint256); impl TryFrom for Uint256 { From a6952bbbee1d03159da2421561ede1a43bdd1c03 Mon Sep 17 00:00:00 2001 From: CJ Cobb <46455409+cjcobb23@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:51:35 -0400 Subject: [PATCH 03/21] feat(minor-its-hub): add support for global freeze (#668) --- .../interchain-token-service/src/contract.rs | 14 +- .../src/contract/execute.rs | 139 +++++++++++++++++- .../interchain-token-service/src/events.rs | 4 + contracts/interchain-token-service/src/msg.rs | 6 + .../interchain-token-service/tests/execute.rs | 78 +++++++++- 5 files changed, 237 insertions(+), 4 deletions(-) diff --git a/contracts/interchain-token-service/src/contract.rs b/contracts/interchain-token-service/src/contract.rs index ee536d5ec..6424d8084 100644 --- a/contracts/interchain-token-service/src/contract.rs +++ b/contracts/interchain-token-service/src/contract.rs @@ -1,7 +1,7 @@ use std::fmt::Debug; use axelar_wasm_std::error::ContractError; -use axelar_wasm_std::{address, permission_control, FnExt, IntoContractError}; +use axelar_wasm_std::{address, killswitch, permission_control, FnExt, IntoContractError}; use axelarnet_gateway::AxelarExecutableMsg; #[cfg(not(feature = "library"))] use cosmwasm_std::entry_point; @@ -35,6 +35,10 @@ pub enum Error { QueryItsContract, #[error("failed to query all its addresses")] QueryAllItsContracts, + #[error("failed to disable execution")] + DisableExecution, + #[error("failed to enable execution")] + EnableExecution, } #[cfg_attr(not(feature = "library"), entry_point)] @@ -70,6 +74,8 @@ pub fn instantiate( state::save_its_contract(deps.storage, chain, address)?; } + killswitch::init(deps.storage, killswitch::State::Disengaged)?; + Ok(Response::new().add_events( msg.its_contracts .into_iter() @@ -99,6 +105,12 @@ pub fn execute( execute::deregister_its_contract(deps, chain) .change_context(Error::DeregisterItsContract) } + ExecuteMsg::DisableExecution => { + execute::disable_execution(deps).change_context(Error::DisableExecution) + } + ExecuteMsg::EnableExecution => { + execute::enable_execution(deps).change_context(Error::EnableExecution) + } ExecuteMsg::SetChainConfig { chain, max_uint, diff --git a/contracts/interchain-token-service/src/contract/execute.rs b/contracts/interchain-token-service/src/contract/execute.rs index 0112882ce..f554ff3bd 100644 --- a/contracts/interchain-token-service/src/contract/execute.rs +++ b/contracts/interchain-token-service/src/contract/execute.rs @@ -1,4 +1,4 @@ -use axelar_wasm_std::{nonempty, FnExt, IntoContractError}; +use axelar_wasm_std::{killswitch, nonempty, FnExt, IntoContractError}; use cosmwasm_std::{DepsMut, HexBinary, QuerierWrapper, Response, Storage}; use error_stack::{bail, ensure, report, Result, ResultExt}; use router_api::{Address, ChainName, ChainNameRaw, CrossChainId}; @@ -21,12 +21,18 @@ pub enum Error { FailedItsContractRegistration(ChainNameRaw), #[error("failed to deregister its contract for chain {0}")] FailedItsContractDeregistration(ChainNameRaw), + #[error("failed to execute message")] + FailedExecuteMessage, + #[error("execution is currently disabled")] + ExecutionDisabled, #[error("chain config for {0} already set")] ChainConfigAlreadySet(ChainNameRaw), - #[error("invalid chain max uint")] + #[error("failed to load chain config for chain {0}")] LoadChainConfig(ChainNameRaw), #[error("failed to save chain config for chain {0}")] SaveChainConfig(ChainNameRaw), + #[error("state error")] + State, } /// Executes an incoming ITS message. @@ -40,6 +46,10 @@ pub fn execute_message( source_address: Address, payload: HexBinary, ) -> Result { + ensure!( + killswitch::is_contract_active(deps.storage), + Error::ExecutionDisabled + ); ensure_its_source_address(deps.storage, &cc_id.source_chain, &source_address)?; match HubMessage::abi_decode(&payload).change_context(Error::InvalidPayload)? { @@ -133,6 +143,14 @@ pub fn deregister_its_contract(deps: DepsMut, chain: ChainNameRaw) -> Result Result { + killswitch::engage(deps.storage, Event::ExecutionDisabled).change_context(Error::State) +} + +pub fn enable_execution(deps: DepsMut) -> Result { + killswitch::disengage(deps.storage, Event::ExecutionEnabled).change_context(Error::State) +} + pub fn set_chain_config( deps: DepsMut, chain: ChainNameRaw, @@ -148,3 +166,120 @@ pub fn set_chain_config( .then(|_| Ok(Response::new())), } } + +#[cfg(test)] +mod tests { + use assert_ok::assert_ok; + use axelar_wasm_std::msg_id::HexTxHashAndEventIndex; + use axelar_wasm_std::{assert_err_contains, killswitch, nonempty, permission_control}; + use cosmwasm_std::testing::{mock_dependencies, MockApi, MockQuerier}; + use cosmwasm_std::{Addr, HexBinary, MemoryStorage, OwnedDeps, Uint256}; + use router_api::{ChainNameRaw, CrossChainId}; + + use super::disable_execution; + use crate::contract::execute::{ + enable_execution, execute_message, register_its_contract, Error, + }; + use crate::state::{self, Config}; + use crate::{HubMessage, Message}; + + const SOLANA: &str = "solana"; + const ETHEREUM: &str = "ethereum"; + + const ITS_ADDRESS: &str = "68d30f47F19c07bCCEf4Ac7FAE2Dc12FCa3e0dC9"; + + const ADMIN: &str = "admin"; + const GOVERNANCE: &str = "governance"; + const AXELARNET_GATEWAY: &str = "axelarnet-gateway"; + + fn its_address() -> nonempty::HexBinary { + HexBinary::from_hex(ITS_ADDRESS) + .unwrap() + .try_into() + .unwrap() + } + + #[test] + fn should_be_able_to_disable_and_enable_execution() { + let mut deps = mock_dependencies(); + init(&mut deps); + + assert_ok!(disable_execution(deps.as_mut())); + + let msg = HubMessage::SendToHub { + destination_chain: ChainNameRaw::try_from(SOLANA).unwrap(), + message: Message::InterchainTransfer { + token_id: [7u8; 32].into(), + source_address: its_address(), + destination_address: its_address(), + amount: Uint256::one().try_into().unwrap(), + data: None, + }, + }; + let res = execute_message( + deps.as_mut(), + CrossChainId { + source_chain: ChainNameRaw::try_from(SOLANA).unwrap(), + message_id: HexTxHashAndEventIndex::new([1u8; 32], 0u32) + .to_string() + .try_into() + .unwrap(), + }, + ITS_ADDRESS.to_string().try_into().unwrap(), + msg.clone().abi_encode(), + ); + assert_err_contains!(res, Error, Error::ExecutionDisabled); + + assert_ok!(enable_execution(deps.as_mut())); + + assert_ok!(execute_message( + deps.as_mut(), + CrossChainId { + source_chain: ChainNameRaw::try_from(SOLANA).unwrap(), + message_id: HexTxHashAndEventIndex::new([1u8; 32], 0u32) + .to_string() + .try_into() + .unwrap(), + }, + ITS_ADDRESS.to_string().try_into().unwrap(), + msg.abi_encode(), + )); + } + + fn init(deps: &mut OwnedDeps) { + assert_ok!(permission_control::set_admin( + deps.as_mut().storage, + &Addr::unchecked(ADMIN) + )); + assert_ok!(permission_control::set_governance( + deps.as_mut().storage, + &Addr::unchecked(GOVERNANCE) + )); + + assert_ok!(state::save_config( + deps.as_mut().storage, + &Config { + axelarnet_gateway: Addr::unchecked(AXELARNET_GATEWAY), + }, + )); + + assert_ok!(killswitch::init( + deps.as_mut().storage, + killswitch::State::Disengaged + )); + let amplifier_chain = ChainNameRaw::try_from(SOLANA).unwrap(); + let core_chain = ChainNameRaw::try_from(ETHEREUM).unwrap(); + + assert_ok!(register_its_contract( + deps.as_mut(), + core_chain.clone(), + ITS_ADDRESS.to_string().try_into().unwrap(), + )); + + assert_ok!(register_its_contract( + deps.as_mut(), + amplifier_chain.clone(), + ITS_ADDRESS.to_string().try_into().unwrap(), + )); + } +} diff --git a/contracts/interchain-token-service/src/events.rs b/contracts/interchain-token-service/src/events.rs index db7ce05ca..6d98c9597 100644 --- a/contracts/interchain-token-service/src/events.rs +++ b/contracts/interchain-token-service/src/events.rs @@ -16,6 +16,8 @@ pub enum Event { ItsContractDeregistered { chain: ChainNameRaw, }, + ExecutionDisabled, + ExecutionEnabled, } impl From for cosmwasm_std::Event { @@ -35,6 +37,8 @@ impl From for cosmwasm_std::Event { cosmwasm_std::Event::new("its_contract_deregistered") .add_attribute("chain", chain.to_string()) } + Event::ExecutionDisabled => cosmwasm_std::Event::new("execution_disabled"), + Event::ExecutionEnabled => cosmwasm_std::Event::new("execution_enabled"), } } } diff --git a/contracts/interchain-token-service/src/msg.rs b/contracts/interchain-token-service/src/msg.rs index 8b570c1c1..30216ebf3 100644 --- a/contracts/interchain-token-service/src/msg.rs +++ b/contracts/interchain-token-service/src/msg.rs @@ -34,6 +34,12 @@ pub enum ExecuteMsg { /// The admin is allowed to remove the ITS address of a chain for emergencies. #[permission(Elevated)] DeregisterItsContract { chain: ChainNameRaw }, + + #[permission(Elevated)] + DisableExecution, + + #[permission(Elevated)] + EnableExecution, /// Set the chain configuration for a chain. #[permission(Governance)] SetChainConfig { diff --git a/contracts/interchain-token-service/tests/execute.rs b/contracts/interchain-token-service/tests/execute.rs index dd82596d3..e0524f8ea 100644 --- a/contracts/interchain-token-service/tests/execute.rs +++ b/contracts/interchain-token-service/tests/execute.rs @@ -11,7 +11,7 @@ use interchain_token_service::events::Event; use interchain_token_service::msg::ExecuteMsg; use interchain_token_service::{HubMessage, Message, TokenId, TokenManagerType}; use router_api::{Address, ChainName, ChainNameRaw, CrossChainId}; -use utils::{make_deps, TestMessage}; +use utils::{make_deps, params, TestMessage}; mod utils; @@ -317,6 +317,82 @@ fn execute_message_when_invalid_message_type_fails() { assert_err_contains!(result, ExecuteError, ExecuteError::InvalidMessageType); } +#[test] +fn disable_execution_when_not_admin_fails() { + let mut deps = mock_dependencies(); + utils::instantiate_contract(deps.as_mut()).unwrap(); + + let result = contract::execute( + deps.as_mut(), + mock_env(), + mock_info("not-admin", &[]), + ExecuteMsg::DisableExecution, + ); + assert_err_contains!( + result, + permission_control::Error, + permission_control::Error::PermissionDenied { .. } + ); +} + +#[test] +fn enable_execution_when_not_admin_fails() { + let mut deps = mock_dependencies(); + utils::instantiate_contract(deps.as_mut()).unwrap(); + + let result = contract::execute( + deps.as_mut(), + mock_env(), + mock_info("not-admin", &[]), + ExecuteMsg::EnableExecution, + ); + assert_err_contains!( + result, + permission_control::Error, + permission_control::Error::PermissionDenied { .. } + ); +} + +#[test] +fn admin_or_governance_can_enable_execution() { + let mut deps = mock_dependencies(); + utils::instantiate_contract(deps.as_mut()).unwrap(); + + assert_ok!(contract::execute( + deps.as_mut(), + mock_env(), + mock_info(params::ADMIN, &[]), + ExecuteMsg::EnableExecution + )); + + assert_ok!(contract::execute( + deps.as_mut(), + mock_env(), + mock_info(params::GOVERNANCE, &[]), + ExecuteMsg::EnableExecution + )); +} + +#[test] +fn admin_or_governance_can_disable_execution() { + let mut deps = mock_dependencies(); + utils::instantiate_contract(deps.as_mut()).unwrap(); + + assert_ok!(contract::execute( + deps.as_mut(), + mock_env(), + mock_info(params::ADMIN, &[]), + ExecuteMsg::DisableExecution + )); + + assert_ok!(contract::execute( + deps.as_mut(), + mock_env(), + mock_info(params::GOVERNANCE, &[]), + ExecuteMsg::DisableExecution + )); +} + #[test] fn set_chain_config_should_succeed() { let chain = "ethereum".parse().unwrap(); From 03f7fb453c957baaff80a5b7e72233a1f0d5134c Mon Sep 17 00:00:00 2001 From: CJ Cobb <46455409+cjcobb23@users.noreply.github.com> Date: Thu, 31 Oct 2024 11:13:08 -0400 Subject: [PATCH 04/21] feat(minor-interchain-token-service): freeze/unfreeze (#670) --- .../interchain-token-service/src/contract.rs | 13 +- .../src/contract/execute.rs | 197 ++++++++++++++++-- .../src/contract/query.rs | 25 ++- contracts/interchain-token-service/src/msg.rs | 8 + .../interchain-token-service/src/state.rs | 86 ++++++-- .../interchain-token-service/tests/execute.rs | 136 ++++++++++++ 6 files changed, 424 insertions(+), 41 deletions(-) diff --git a/contracts/interchain-token-service/src/contract.rs b/contracts/interchain-token-service/src/contract.rs index 6424d8084..eaa92f2ba 100644 --- a/contracts/interchain-token-service/src/contract.rs +++ b/contracts/interchain-token-service/src/contract.rs @@ -7,6 +7,7 @@ use axelarnet_gateway::AxelarExecutableMsg; use cosmwasm_std::entry_point; use cosmwasm_std::{Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Response, Storage}; use error_stack::{Report, ResultExt}; +use execute::{freeze_chain, unfreeze_chain}; use crate::events::Event; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; @@ -29,6 +30,10 @@ pub enum Error { RegisterItsContract, #[error("failed to deregsiter an its edge contract")] DeregisterItsContract, + #[error("failed to freeze chain")] + FreezeChain, + #[error("failed to unfreeze chain")] + UnfreezeChain, #[error("failed to set chain config")] SetChainConfig, #[error("failed to query its address")] @@ -105,6 +110,12 @@ pub fn execute( execute::deregister_its_contract(deps, chain) .change_context(Error::DeregisterItsContract) } + ExecuteMsg::FreezeChain { chain } => { + freeze_chain(deps, chain).change_context(Error::FreezeChain) + } + ExecuteMsg::UnfreezeChain { chain } => { + unfreeze_chain(deps, chain).change_context(Error::UnfreezeChain) + } ExecuteMsg::DisableExecution => { execute::disable_execution(deps).change_context(Error::DisableExecution) } @@ -129,7 +140,7 @@ fn match_gateway(storage: &dyn Storage, _: &ExecuteMsg) -> Result Result { match msg { QueryMsg::ItsContract { chain } => { - query::its_contracts(deps, chain).change_context(Error::QueryItsContract) + query::its_contract(deps, chain).change_context(Error::QueryItsContract) } QueryMsg::AllItsContracts => { query::all_its_contracts(deps).change_context(Error::QueryAllItsContracts) diff --git a/contracts/interchain-token-service/src/contract/execute.rs b/contracts/interchain-token-service/src/contract/execute.rs index f554ff3bd..6444b3d7a 100644 --- a/contracts/interchain-token-service/src/contract/execute.rs +++ b/contracts/interchain-token-service/src/contract/execute.rs @@ -5,7 +5,7 @@ use router_api::{Address, ChainName, ChainNameRaw, CrossChainId}; use crate::events::Event; use crate::primitives::HubMessage; -use crate::state::{self, load_config, load_its_contract}; +use crate::state::{self, is_chain_frozen, load_config, load_its_contract}; #[derive(thiserror::Error, Debug, IntoContractError)] pub enum Error { @@ -31,6 +31,8 @@ pub enum Error { LoadChainConfig(ChainNameRaw), #[error("failed to save chain config for chain {0}")] SaveChainConfig(ChainNameRaw), + #[error("chain {0} is frozen")] + ChainFrozen(ChainNameRaw), #[error("state error")] State, } @@ -60,6 +62,8 @@ pub fn execute_message( let destination_address = load_its_contract(deps.storage, &destination_chain) .change_context_lazy(|| Error::UnknownChain(destination_chain.clone()))?; + verify_chains_not_frozen(deps.storage, &cc_id.source_chain, &destination_chain)?; + let destination_payload = HubMessage::ReceiveFromHub { source_chain: cc_id.source_chain.clone(), message: message.clone(), @@ -86,6 +90,19 @@ pub fn execute_message( } } +fn verify_chains_not_frozen( + storage: &dyn Storage, + source_chain: &ChainNameRaw, + destination_chain: &ChainNameRaw, +) -> Result<(), Error> { + for chain in [source_chain, destination_chain] { + if is_chain_frozen(storage, chain).change_context(Error::State)? { + return Err(report!(Error::ChainFrozen(chain.to_owned()))); + } + } + Ok(()) +} + fn normalize(chain: &ChainNameRaw) -> ChainName { ChainName::try_from(chain.as_ref()).expect("invalid chain name") } @@ -143,6 +160,18 @@ pub fn deregister_its_contract(deps: DepsMut, chain: ChainNameRaw) -> Result Result { + state::freeze_chain(deps.storage, &chain).change_context(Error::State)?; + + Ok(Response::new()) +} + +pub fn unfreeze_chain(deps: DepsMut, chain: ChainNameRaw) -> Result { + state::unfreeze_chain(deps.storage, &chain).change_context(Error::State)?; + + Ok(Response::new()) +} + pub fn disable_execution(deps: DepsMut) -> Result { killswitch::engage(deps.storage, Event::ExecutionDisabled).change_context(Error::State) } @@ -176,15 +205,16 @@ mod tests { use cosmwasm_std::{Addr, HexBinary, MemoryStorage, OwnedDeps, Uint256}; use router_api::{ChainNameRaw, CrossChainId}; - use super::disable_execution; use crate::contract::execute::{ - enable_execution, execute_message, register_its_contract, Error, + disable_execution, enable_execution, execute_message, freeze_chain, register_its_contract, + set_chain_config, unfreeze_chain, Error, }; use crate::state::{self, Config}; use crate::{HubMessage, Message}; const SOLANA: &str = "solana"; const ETHEREUM: &str = "ethereum"; + const XRPL: &str = "xrpl"; const ITS_ADDRESS: &str = "68d30f47F19c07bCCEf4Ac7FAE2Dc12FCa3e0dC9"; @@ -246,6 +276,140 @@ mod tests { )); } + #[test] + fn execution_should_fail_if_source_chain_is_frozen() { + let mut deps = mock_dependencies(); + init(&mut deps); + + let source_chain = ChainNameRaw::try_from(SOLANA).unwrap(); + let destination_chain = ChainNameRaw::try_from(ETHEREUM).unwrap(); + + assert_ok!(freeze_chain(deps.as_mut(), source_chain.clone())); + + let msg = HubMessage::SendToHub { + destination_chain, + message: Message::InterchainTransfer { + token_id: [7u8; 32].into(), + source_address: its_address(), + destination_address: its_address(), + amount: Uint256::one().try_into().unwrap(), + data: None, + }, + }; + let res = execute_message( + deps.as_mut(), + CrossChainId { + source_chain: source_chain.clone(), + message_id: HexTxHashAndEventIndex::new([1u8; 32], 0u32) + .to_string() + .try_into() + .unwrap(), + }, + ITS_ADDRESS.to_string().try_into().unwrap(), + msg.clone().abi_encode(), + ); + + assert_err_contains!(res, Error, Error::ChainFrozen(..)); + + assert_ok!(unfreeze_chain(deps.as_mut(), source_chain.clone())); + + assert_ok!(execute_message( + deps.as_mut(), + CrossChainId { + source_chain, + message_id: HexTxHashAndEventIndex::new([1u8; 32], 0u32) + .to_string() + .try_into() + .unwrap(), + }, + ITS_ADDRESS.to_string().try_into().unwrap(), + msg.clone().abi_encode(), + )); + } + + #[test] + fn execution_should_fail_if_destination_chain_is_frozen() { + let mut deps = mock_dependencies(); + init(&mut deps); + + let source_chain = ChainNameRaw::try_from(SOLANA).unwrap(); + let destination_chain = ChainNameRaw::try_from(ETHEREUM).unwrap(); + + assert_ok!(freeze_chain(deps.as_mut(), destination_chain.clone())); + + let msg = HubMessage::SendToHub { + destination_chain: destination_chain.clone(), + message: Message::InterchainTransfer { + token_id: [7u8; 32].into(), + source_address: its_address(), + destination_address: its_address(), + amount: Uint256::one().try_into().unwrap(), + data: None, + }, + }; + let cc_id = CrossChainId { + source_chain, + message_id: HexTxHashAndEventIndex::new([1u8; 32], 0u32) + .to_string() + .try_into() + .unwrap(), + }; + + let res = execute_message( + deps.as_mut(), + cc_id.clone(), + ITS_ADDRESS.to_string().try_into().unwrap(), + msg.clone().abi_encode(), + ); + assert_err_contains!(res, Error, Error::ChainFrozen(..)); + + assert_ok!(unfreeze_chain(deps.as_mut(), destination_chain)); + + assert_ok!(execute_message( + deps.as_mut(), + cc_id, + ITS_ADDRESS.to_string().try_into().unwrap(), + msg.clone().abi_encode(), + )); + } + + #[test] + fn frozen_chain_that_is_not_source_or_destination_should_not_affect_execution() { + let mut deps = mock_dependencies(); + init(&mut deps); + + let source_chain = ChainNameRaw::try_from(SOLANA).unwrap(); + let destination_chain = ChainNameRaw::try_from(ETHEREUM).unwrap(); + let other_chain = ChainNameRaw::try_from(XRPL).unwrap(); + + assert_ok!(freeze_chain(deps.as_mut(), other_chain.clone())); + + let msg = HubMessage::SendToHub { + destination_chain: destination_chain.clone(), + message: Message::InterchainTransfer { + token_id: [7u8; 32].into(), + source_address: its_address(), + destination_address: its_address(), + amount: Uint256::one().try_into().unwrap(), + data: None, + }, + }; + let cc_id = CrossChainId { + source_chain, + message_id: HexTxHashAndEventIndex::new([1u8; 32], 0u32) + .to_string() + .try_into() + .unwrap(), + }; + + assert_ok!(execute_message( + deps.as_mut(), + cc_id.clone(), + ITS_ADDRESS.to_string().try_into().unwrap(), + msg.clone().abi_encode(), + )); + } + fn init(deps: &mut OwnedDeps) { assert_ok!(permission_control::set_admin( deps.as_mut().storage, @@ -267,19 +431,20 @@ mod tests { deps.as_mut().storage, killswitch::State::Disengaged )); - let amplifier_chain = ChainNameRaw::try_from(SOLANA).unwrap(); - let core_chain = ChainNameRaw::try_from(ETHEREUM).unwrap(); - assert_ok!(register_its_contract( - deps.as_mut(), - core_chain.clone(), - ITS_ADDRESS.to_string().try_into().unwrap(), - )); - - assert_ok!(register_its_contract( - deps.as_mut(), - amplifier_chain.clone(), - ITS_ADDRESS.to_string().try_into().unwrap(), - )); + for chain_name in [SOLANA, ETHEREUM, XRPL] { + let chain = ChainNameRaw::try_from(chain_name).unwrap(); + assert_ok!(register_its_contract( + deps.as_mut(), + chain.clone(), + ITS_ADDRESS.to_string().try_into().unwrap(), + )); + assert_ok!(set_chain_config( + deps.as_mut(), + chain, + Uint256::one().try_into().unwrap(), + 16u8 + )); + } } } diff --git a/contracts/interchain-token-service/src/contract/query.rs b/contracts/interchain-token-service/src/contract/query.rs index f9aeeea0e..1ae2e043b 100644 --- a/contracts/interchain-token-service/src/contract/query.rs +++ b/contracts/interchain-token-service/src/contract/query.rs @@ -1,14 +1,25 @@ +use axelar_wasm_std::IntoContractError; use cosmwasm_std::{to_json_binary, Binary, Deps}; +use error_stack::{Result, ResultExt}; use router_api::ChainNameRaw; -use crate::state; +use crate::state::{load_all_its_contracts, may_load_its_contract}; -pub fn its_contracts(deps: Deps, chain: ChainNameRaw) -> Result { - let contract_address = state::may_load_its_contract(deps.storage, &chain)?; - Ok(to_json_binary(&contract_address)?) +#[derive(thiserror::Error, Debug, IntoContractError)] +pub enum Error { + #[error("failed to serialize data to JSON")] + JsonSerialization, + #[error("state error")] + State, } -pub fn all_its_contracts(deps: Deps) -> Result { - let contract_addresses = state::load_all_its_contracts(deps.storage)?; - Ok(to_json_binary(&contract_addresses)?) +pub fn its_contract(deps: Deps, chain: ChainNameRaw) -> Result { + let contract_address = + may_load_its_contract(deps.storage, &chain).change_context(Error::State)?; + to_json_binary(&contract_address).change_context(Error::JsonSerialization) +} + +pub fn all_its_contracts(deps: Deps) -> Result { + let contract_addresses = load_all_its_contracts(deps.storage).change_context(Error::State)?; + to_json_binary(&contract_addresses).change_context(Error::JsonSerialization) } diff --git a/contracts/interchain-token-service/src/msg.rs b/contracts/interchain-token-service/src/msg.rs index 30216ebf3..3730bc014 100644 --- a/contracts/interchain-token-service/src/msg.rs +++ b/contracts/interchain-token-service/src/msg.rs @@ -35,6 +35,14 @@ pub enum ExecuteMsg { #[permission(Elevated)] DeregisterItsContract { chain: ChainNameRaw }, + /// Freeze execution of ITS messages for a particular chain + #[permission(Elevated)] + FreezeChain { chain: ChainNameRaw }, + + /// Unfreeze execution of ITS messages for a particular chain + #[permission(Elevated)] + UnfreezeChain { chain: ChainNameRaw }, + #[permission(Elevated)] DisableExecution, diff --git a/contracts/interchain-token-service/src/state.rs b/contracts/interchain-token-service/src/state.rs index 806e7a01c..053aacfc1 100644 --- a/contracts/interchain-token-service/src/state.rs +++ b/contracts/interchain-token-service/src/state.rs @@ -4,16 +4,23 @@ use axelar_wasm_std::{nonempty, IntoContractError}; use cosmwasm_schema::cw_serde; use cosmwasm_std::{ensure, Addr, StdError, Storage}; use cw_storage_plus::{Item, Map}; +use error_stack::{report, Result, ResultExt}; use router_api::{Address, ChainNameRaw}; #[derive(thiserror::Error, Debug, IntoContractError)] pub enum Error { - #[error(transparent)] - Std(#[from] StdError), + #[error("ITS contract got into an invalid state, its config is missing")] + MissingConfig, #[error("its address for chain {0} not found")] ItsContractNotFound(ChainNameRaw), #[error("its address for chain {0} already registered")] ItsContractAlreadyRegistered(ChainNameRaw), + #[error("chain not found {0}")] + ChainNotFound(ChainNameRaw), + // This is a generic error to use when cw_storage_plus returns an error that is unexpected and + // should never happen, such as an error encountered when saving data. + #[error("storage error")] + Storage, } #[cw_serde] @@ -25,6 +32,7 @@ pub struct Config { pub struct ChainConfig { max_uint: nonempty::Uint256, max_target_decimals: u8, + frozen: bool, } const CONFIG: Item = Item::new("config"); @@ -38,14 +46,16 @@ pub fn load_config(storage: &dyn Storage) -> Config { } pub fn save_config(storage: &mut dyn Storage, config: &Config) -> Result<(), Error> { - Ok(CONFIG.save(storage, config)?) + CONFIG.save(storage, config).change_context(Error::Storage) } pub fn may_load_chain_config( storage: &dyn Storage, chain: &ChainNameRaw, ) -> Result, Error> { - Ok(CHAIN_CONFIGS.may_load(storage, chain)?) + CHAIN_CONFIGS + .may_load(storage, chain) + .change_context(Error::Storage) } pub fn save_chain_config( @@ -54,25 +64,32 @@ pub fn save_chain_config( max_uint: nonempty::Uint256, max_target_decimals: u8, ) -> Result<(), Error> { - Ok(CHAIN_CONFIGS.save( - storage, - chain, - &ChainConfig { - max_uint, - max_target_decimals, - }, - )?) + CHAIN_CONFIGS + .save( + storage, + chain, + &ChainConfig { + max_uint, + max_target_decimals, + frozen: false, + }, + ) + .change_context(Error::Storage) } pub fn may_load_its_contract( storage: &dyn Storage, chain: &ChainNameRaw, ) -> Result, Error> { - Ok(ITS_CONTRACTS.may_load(storage, chain)?) + ITS_CONTRACTS + .may_load(storage, chain) + .change_context(Error::Storage) } pub fn load_its_contract(storage: &dyn Storage, chain: &ChainNameRaw) -> Result { - may_load_its_contract(storage, chain)?.ok_or_else(|| Error::ItsContractNotFound(chain.clone())) + may_load_its_contract(storage, chain) + .change_context(Error::Storage)? + .ok_or_else(|| report!(Error::ItsContractNotFound(chain.clone()))) } pub fn save_its_contract( @@ -85,7 +102,9 @@ pub fn save_its_contract( Error::ItsContractAlreadyRegistered(chain.clone()) ); - Ok(ITS_CONTRACTS.save(storage, chain, address)?) + ITS_CONTRACTS + .save(storage, chain, address) + .change_context(Error::Storage) } pub fn remove_its_contract(storage: &mut dyn Storage, chain: &ChainNameRaw) -> Result<(), Error> { @@ -102,9 +121,42 @@ pub fn remove_its_contract(storage: &mut dyn Storage, chain: &ChainNameRaw) -> R pub fn load_all_its_contracts( storage: &dyn Storage, ) -> Result, Error> { - Ok(ITS_CONTRACTS + ITS_CONTRACTS .range(storage, None, None, cosmwasm_std::Order::Ascending) - .collect::, _>>()?) + .map(|res| res.change_context(Error::Storage)) + .collect::, _>>() +} + +pub fn is_chain_frozen(storage: &dyn Storage, chain: &ChainNameRaw) -> Result { + CHAIN_CONFIGS + .load(storage, chain) + .change_context(Error::ChainNotFound(chain.to_owned())) + .map(|chain_config| chain_config.frozen) +} + +pub fn freeze_chain(storage: &mut dyn Storage, chain: &ChainNameRaw) -> Result { + CHAIN_CONFIGS + .update(storage, chain, |elt| match elt { + Some(x) => Ok(ChainConfig { frozen: true, ..x }), + None => Err(StdError::NotFound { + kind: "chain not found".to_string(), + }), + }) + .change_context(Error::ChainNotFound(chain.to_owned())) +} + +pub fn unfreeze_chain( + storage: &mut dyn Storage, + chain: &ChainNameRaw, +) -> Result { + CHAIN_CONFIGS + .update(storage, chain, |elt| match elt { + Some(x) => Ok(ChainConfig { frozen: false, ..x }), + None => Err(StdError::NotFound { + kind: "chain not found".to_string(), + }), + }) + .change_context(Error::ChainNotFound(chain.to_owned())) } #[cfg(test)] diff --git a/contracts/interchain-token-service/tests/execute.rs b/contracts/interchain-token-service/tests/execute.rs index e0524f8ea..cfaf94d9f 100644 --- a/contracts/interchain-token-service/tests/execute.rs +++ b/contracts/interchain-token-service/tests/execute.rs @@ -105,6 +105,26 @@ fn execute_hub_message_succeeds() { ) .unwrap(); + let max_uint = Uint256::from_str("120000000000000000000000000") + .unwrap() + .try_into() + .unwrap(); + let decimals = 18; + + assert_ok!(utils::set_chain_config( + deps.as_mut(), + source_its_chain.clone(), + max_uint, + decimals + )); + + assert_ok!(utils::set_chain_config( + deps.as_mut(), + destination_its_chain.clone(), + max_uint, + decimals + )); + let token_id = TokenId::new([1; 32]); let test_messages = vec![ Message::InterchainTransfer { @@ -317,6 +337,122 @@ fn execute_message_when_invalid_message_type_fails() { assert_err_contains!(result, ExecuteError, ExecuteError::InvalidMessageType); } +#[test] +fn freeze_chain_when_not_admin_fails() { + let mut deps = mock_dependencies(); + utils::instantiate_contract(deps.as_mut()).unwrap(); + + let result = contract::execute( + deps.as_mut(), + mock_env(), + mock_info("not-admin", &[]), + ExecuteMsg::FreezeChain { + chain: ChainNameRaw::try_from("ethereum").unwrap(), + }, + ); + assert_err_contains!( + result, + permission_control::Error, + permission_control::Error::PermissionDenied { .. } + ); +} + +#[test] +fn unfreeze_chain_when_not_admin_fails() { + let mut deps = mock_dependencies(); + utils::instantiate_contract(deps.as_mut()).unwrap(); + + let result = contract::execute( + deps.as_mut(), + mock_env(), + mock_info("not-admin", &[]), + ExecuteMsg::UnfreezeChain { + chain: ChainNameRaw::try_from("ethereum").unwrap(), + }, + ); + assert_err_contains!( + result, + permission_control::Error, + permission_control::Error::PermissionDenied { .. } + ); +} + +#[test] +fn admin_or_governance_can_freeze_chain() { + let mut deps = mock_dependencies(); + utils::instantiate_contract(deps.as_mut()).unwrap(); + + let chain = "ethereum".parse().unwrap(); + let max_uint = Uint256::from_str("120000000000000000000000000") + .unwrap() + .try_into() + .unwrap(); + let decimals = 18; + + assert_ok!(utils::set_chain_config( + deps.as_mut(), + chain, + max_uint, + decimals + )); + + assert_ok!(contract::execute( + deps.as_mut(), + mock_env(), + mock_info(params::ADMIN, &[]), + ExecuteMsg::FreezeChain { + chain: ChainNameRaw::try_from("ethereum").unwrap() + } + )); + + assert_ok!(contract::execute( + deps.as_mut(), + mock_env(), + mock_info(params::GOVERNANCE, &[]), + ExecuteMsg::FreezeChain { + chain: ChainNameRaw::try_from("ethereum").unwrap() + } + )); +} + +#[test] +fn admin_or_governance_can_unfreeze_chain() { + let mut deps = mock_dependencies(); + utils::instantiate_contract(deps.as_mut()).unwrap(); + + let chain = "ethereum".parse().unwrap(); + let max_uint = Uint256::from_str("120000000000000000000000000") + .unwrap() + .try_into() + .unwrap(); + let decimals = 18; + + assert_ok!(utils::set_chain_config( + deps.as_mut(), + chain, + max_uint, + decimals + )); + + assert_ok!(contract::execute( + deps.as_mut(), + mock_env(), + mock_info(params::ADMIN, &[]), + ExecuteMsg::UnfreezeChain { + chain: ChainNameRaw::try_from("ethereum").unwrap() + } + )); + + assert_ok!(contract::execute( + deps.as_mut(), + mock_env(), + mock_info(params::GOVERNANCE, &[]), + ExecuteMsg::UnfreezeChain { + chain: ChainNameRaw::try_from("ethereum").unwrap() + } + )); +} + #[test] fn disable_execution_when_not_admin_fails() { let mut deps = mock_dependencies(); From 09137ee7b8fe042c75cbd8ed1f6cdd0eb773d7e5 Mon Sep 17 00:00:00 2001 From: Christian Gorenflo Date: Fri, 1 Nov 2024 10:12:47 -0400 Subject: [PATCH 05/21] docs: correct spelling mistake (#673) --- contracts/interchain-token-service/src/msg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/interchain-token-service/src/msg.rs b/contracts/interchain-token-service/src/msg.rs index 3730bc014..76d8c09ca 100644 --- a/contracts/interchain-token-service/src/msg.rs +++ b/contracts/interchain-token-service/src/msg.rs @@ -53,7 +53,7 @@ pub enum ExecuteMsg { SetChainConfig { chain: ChainNameRaw, max_uint: nonempty::Uint256, // The maximum uint value that is supported by the chain's token standard - max_target_decimals: u8, // The maximum number of decimals that is preserved when deploying a token to anothe chain where smaller uint values are used + max_target_decimals: u8, // The maximum number of decimals that is preserved when deploying a token to another chain where smaller uint values are used }, } From ab3f01d343c82710fb414533b7ffc4997c83739e Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Sat, 2 Nov 2024 17:05:00 -0400 Subject: [PATCH 06/21] feat(major-interchain-token-service): apply token balance invariants (#671) Co-authored-by: Christian Gorenflo --- Cargo.lock | 1 + contracts/interchain-token-service/Cargo.toml | 1 + contracts/interchain-token-service/README.md | 26 +- contracts/interchain-token-service/src/abi.rs | 105 ++-- .../interchain-token-service/src/contract.rs | 18 +- .../src/contract/execute.rs | 441 ++++++++++++--- .../src/contract/query.rs | 17 +- .../interchain-token-service/src/events.rs | 58 +- contracts/interchain-token-service/src/lib.rs | 1 + contracts/interchain-token-service/src/msg.rs | 14 +- .../src/primitives.rs | 106 ++-- .../interchain-token-service/src/state.rs | 122 +++- .../interchain-token-service/tests/execute.rs | 527 +++++++++++++++--- .../interchain-token-service/tests/query.rs | 13 + .../execute_hub_message_succeeds.golden | 99 +++- .../tests/utils/execute.rs | 58 +- .../tests/utils/messages.rs | 14 +- .../tests/utils/query.rs | 22 + packages/router-api/src/primitives.rs | 7 + 19 files changed, 1346 insertions(+), 304 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6830e3192..a6fb746e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4089,6 +4089,7 @@ dependencies = [ "error-stack", "goldie", "hex", + "itertools 0.11.0", "msgs-derive", "report", "router-api", diff --git a/contracts/interchain-token-service/Cargo.toml b/contracts/interchain-token-service/Cargo.toml index fdf4422a8..7829e06cb 100644 --- a/contracts/interchain-token-service/Cargo.toml +++ b/contracts/interchain-token-service/Cargo.toml @@ -43,6 +43,7 @@ cw-storage-plus = { workspace = true } cw2 = { workspace = true } error-stack = { workspace = true } hex = { workspace = true } +itertools = "0.11.0" msgs-derive = { workspace = true } report = { workspace = true } router-api = { workspace = true } diff --git a/contracts/interchain-token-service/README.md b/contracts/interchain-token-service/README.md index 260766bf4..285686a4e 100644 --- a/contracts/interchain-token-service/README.md +++ b/contracts/interchain-token-service/README.md @@ -2,14 +2,30 @@ ## Overview -The Interchain Token Service (ITS) Hub contract is a crucial component of a cross-chain ITS protocol. It facilitates the transfer of tokens between different blockchains, manages token deployments, and maintains balance integrity across chains. It connects to ITS edge contracts on different chains (e.g. EVM ITS [contract](https://github.com/axelarnetwork/interchain-token-service)). +The Interchain Token Service (ITS) Hub contract is a crucial component of a cross-chain ITS protocol. It facilitates the +transfer of tokens between different blockchains, manages token deployments, and tracks the token supply across chains. +It connects to ITS edge contracts on different chains (e.g. EVM +ITS [contract](https://github.com/axelarnetwork/interchain-token-service)). ## Key Components 1. **ITS Message Processing**: Processes incoming ITS messages from trusted sources. -2. **Balance Tracking**: Ensures accurate token balances are maintained during cross-chain operations. -3. **ITS Address Registry**: Tracks the trusted ITS address for each chain for routing. +2. **Token Supply Tracking**: Ensures accurate token supply is maintained during cross-chain operations. +3. **ITS Address Registry**: Tracks the trusted ITS address for each chain for routing. This avoids each chain's ITS + contract from having to know about all other ITS contract addresses. -### Cross-chain messaging +## Cross-chain messaging -The ITS Hub makes use of the Axelarnet gateway [contract](../contracts/axelarnet-gateway/) to facilitate sending or receiving cross-chain messages. Messages are sent via `CallContract`, and received when the Axelarnet gateway is executed (by a relayer / user) through `Execute`, which in turn executes ITS Hub's `Execute` method. +The ITS Hub makes use of the Axelarnet gateway [contract](../contracts/axelarnet-gateway/) to facilitate sending or +receiving cross-chain messages. Messages are sent via `CallContract`, and received when the Axelarnet gateway is +executed (by a relayer / user) through `Execute`, which in turn executes ITS Hub's `Execute` method. + +## Token Supply Invariant + +ITS Hub maintains the token supply for native interchain tokens for every chain they're deployed to. This helps isolate +the security risk between chains. A compromised chain can only affect the total token supply that was sent to that chain +in the worst case. For example, if USDC was deployed from Ethereum to Solana via the ITS Hub, and 10M USDC was moved to +Solana (in total). If there's a compromise on Solana (potentially the chain itself, or the Solana ITS contract, or +another related contract), an attacker can only withdraw at most 10M USDC back to Ethereum or another chain (and not all +the bridged USDC locked on the Ethereum ITS contract). ITS Hub will prevent all USDC transfers from Solana once 10M USDC +has been moved back out from it. diff --git a/contracts/interchain-token-service/src/abi.rs b/contracts/interchain-token-service/src/abi.rs index 4bbffd0be..dabec3048 100644 --- a/contracts/interchain-token-service/src/abi.rs +++ b/contracts/interchain-token-service/src/abi.rs @@ -6,7 +6,7 @@ use error_stack::{bail, ensure, report, Report, ResultExt}; use router_api::ChainNameRaw; use crate::primitives::{HubMessage, Message}; -use crate::{TokenId, TokenManagerType}; +use crate::{primitives, TokenId, TokenManagerType}; // ITS Message payload types // Reference: https://github.com/axelarnetwork/interchain-token-service/blob/v1.2.4/DESIGN.md#interchain-communication-spec @@ -80,13 +80,13 @@ pub enum Error { impl Message { pub fn abi_encode(self) -> HexBinary { match self { - Message::InterchainTransfer { + Message::InterchainTransfer(primitives::InterchainTransfer { token_id, source_address, destination_address, amount, data, - } => InterchainTransfer { + }) => InterchainTransfer { messageType: MessageType::InterchainTransfer.into(), tokenId: FixedBytes::<32>::new(token_id.into()), sourceAddress: Vec::::from(source_address).into(), @@ -95,13 +95,13 @@ impl Message { data: into_vec(data).into(), } .abi_encode_params(), - Message::DeployInterchainToken { + Message::DeployInterchainToken(primitives::DeployInterchainToken { token_id, name, symbol, decimals, minter, - } => DeployInterchainToken { + }) => DeployInterchainToken { messageType: MessageType::DeployInterchainToken.into(), tokenId: FixedBytes::<32>::new(token_id.into()), name: name.into(), @@ -110,11 +110,11 @@ impl Message { minter: into_vec(minter).into(), } .abi_encode_params(), - Message::DeployTokenManager { + Message::DeployTokenManager(primitives::DeployTokenManager { token_id, token_manager_type, params, - } => DeployTokenManager { + }) => DeployTokenManager { messageType: MessageType::DeployTokenManager.into(), tokenId: FixedBytes::<32>::new(token_id.into()), tokenManagerType: token_manager_type.into(), @@ -136,7 +136,7 @@ impl Message { let decoded = InterchainTransfer::abi_decode_params(payload, true) .map_err(Error::AbiDecodeFailed)?; - Message::InterchainTransfer { + primitives::InterchainTransfer { token_id: TokenId::new(decoded.tokenId.into()), source_address: Vec::::from(decoded.sourceAddress) .try_into() @@ -149,18 +149,20 @@ impl Message { .map_err(Error::NonEmpty)?, data: from_vec(decoded.data.into())?, } + .into() } MessageType::DeployInterchainToken => { let decoded = DeployInterchainToken::abi_decode_params(payload, true) .map_err(Error::AbiDecodeFailed)?; - Message::DeployInterchainToken { + primitives::DeployInterchainToken { token_id: TokenId::new(decoded.tokenId.into()), name: decoded.name.try_into().map_err(Error::NonEmpty)?, symbol: decoded.symbol.try_into().map_err(Error::NonEmpty)?, decimals: decoded.decimals, minter: from_vec(decoded.minter.into())?, } + .into() } MessageType::DeployTokenManager => { let decoded = DeployTokenManager::abi_decode_params(payload, true) @@ -171,13 +173,14 @@ impl Message { .then(TokenManagerType::from_repr) .ok_or_else(|| Error::InvalidTokenManagerType)?; - Message::DeployTokenManager { + primitives::DeployTokenManager { token_id: TokenId::new(decoded.tokenId.into()), token_manager_type, params: Vec::::from(decoded.params) .try_into() .map_err(Error::NonEmpty)?, } + .into() } _ => bail!(Error::InvalidMessageType), }; @@ -284,7 +287,7 @@ mod tests { use super::{DeployInterchainToken, InterchainTransfer}; use crate::abi::{DeployTokenManager, Error, MessageType, SendToHub}; - use crate::{HubMessage, Message, TokenManagerType}; + use crate::{primitives, HubMessage, TokenManagerType}; fn from_hex(hex: &str) -> nonempty::HexBinary { HexBinary::from_hex(hex).unwrap().try_into().unwrap() @@ -297,43 +300,47 @@ mod tests { let cases = vec![ HubMessage::SendToHub { destination_chain: remote_chain.clone(), - message: Message::InterchainTransfer { + message: primitives::InterchainTransfer { token_id: [0u8; 32].into(), source_address: from_hex("00"), destination_address: from_hex("00"), amount: 1u64.try_into().unwrap(), data: None, - }, + } + .into(), }, HubMessage::SendToHub { destination_chain: remote_chain.clone(), - message: Message::InterchainTransfer { + message: primitives::InterchainTransfer { token_id: [255u8; 32].into(), source_address: from_hex("4F4495243837681061C4743b74B3eEdf548D56A5"), destination_address: from_hex("4F4495243837681061C4743b74B3eEdf548D56A5"), amount: Uint256::MAX.try_into().unwrap(), data: Some(from_hex("abcd")), - }, + } + .into(), }, HubMessage::ReceiveFromHub { source_chain: remote_chain.clone(), - message: Message::InterchainTransfer { + message: primitives::InterchainTransfer { token_id: [0u8; 32].into(), source_address: from_hex("00"), destination_address: from_hex("00"), amount: 1u64.try_into().unwrap(), data: None, - }, + } + .into(), }, HubMessage::ReceiveFromHub { source_chain: remote_chain.clone(), - message: Message::InterchainTransfer { + message: primitives::InterchainTransfer { token_id: [255u8; 32].into(), source_address: from_hex("4F4495243837681061C4743b74B3eEdf548D56A5"), destination_address: from_hex("4F4495243837681061C4743b74B3eEdf548D56A5"), amount: Uint256::MAX.try_into().unwrap(), data: Some(from_hex("abcd")), - }, + } + .into(), }, ]; @@ -428,63 +435,69 @@ mod tests { let cases = vec![ HubMessage::SendToHub { destination_chain: remote_chain.clone(), - message: Message::DeployInterchainToken { + message: primitives::DeployInterchainToken { token_id: [0u8; 32].into(), name: "t".try_into().unwrap(), symbol: "T".try_into().unwrap(), decimals: 0, minter: None, - }, + } + .into(), }, HubMessage::SendToHub { destination_chain: remote_chain.clone(), - message: Message::DeployInterchainToken { + message: primitives::DeployInterchainToken { token_id: [1u8; 32].into(), name: "Test Token".try_into().unwrap(), symbol: "TST".try_into().unwrap(), decimals: 18, minter: Some(from_hex("1234")), - }, + } + .into(), }, HubMessage::SendToHub { destination_chain: remote_chain.clone(), - message: Message::DeployInterchainToken { + message: primitives::DeployInterchainToken { token_id: [0u8; 32].into(), name: "Unicode Token 🪙".try_into().unwrap(), symbol: "UNI🔣".try_into().unwrap(), decimals: 255, minter: Some(from_hex("abcd")), - }, + } + .into(), }, HubMessage::ReceiveFromHub { source_chain: remote_chain.clone(), - message: Message::DeployInterchainToken { + message: primitives::DeployInterchainToken { token_id: [0u8; 32].into(), name: "t".try_into().unwrap(), symbol: "T".try_into().unwrap(), decimals: 0, minter: None, - }, + } + .into(), }, HubMessage::ReceiveFromHub { source_chain: remote_chain.clone(), - message: Message::DeployInterchainToken { + message: primitives::DeployInterchainToken { token_id: [1u8; 32].into(), name: "Test Token".try_into().unwrap(), symbol: "TST".try_into().unwrap(), decimals: 18, minter: Some(from_hex("1234")), - }, + } + .into(), }, HubMessage::ReceiveFromHub { source_chain: remote_chain.clone(), - message: Message::DeployInterchainToken { + message: primitives::DeployInterchainToken { token_id: [0u8; 32].into(), name: "Unicode Token 🪙".try_into().unwrap(), symbol: "UNI🔣".try_into().unwrap(), decimals: 255, minter: Some(from_hex("abcd")), - }, + } + .into(), }, ]; @@ -509,35 +522,39 @@ mod tests { let cases = vec![ HubMessage::SendToHub { destination_chain: remote_chain.clone(), - message: Message::DeployTokenManager { + message: primitives::DeployTokenManager { token_id: [0u8; 32].into(), token_manager_type: TokenManagerType::NativeInterchainToken, params: from_hex("00"), - }, + } + .into(), }, HubMessage::SendToHub { destination_chain: remote_chain.clone(), - message: Message::DeployTokenManager { + message: primitives::DeployTokenManager { token_id: [1u8; 32].into(), token_manager_type: TokenManagerType::Gateway, params: from_hex("1234"), - }, + } + .into(), }, HubMessage::ReceiveFromHub { source_chain: remote_chain.clone(), - message: Message::DeployTokenManager { + message: primitives::DeployTokenManager { token_id: [0u8; 32].into(), token_manager_type: TokenManagerType::NativeInterchainToken, params: from_hex("00"), - }, + } + .into(), }, HubMessage::ReceiveFromHub { source_chain: remote_chain.clone(), - message: Message::DeployTokenManager { + message: primitives::DeployTokenManager { token_id: [1u8; 32].into(), token_manager_type: TokenManagerType::Gateway, params: from_hex("1234"), - }, + } + .into(), }, ]; @@ -646,13 +663,14 @@ mod tests { let large_data = vec![0u8; 1024 * 1024]; // 1MB of data let original = HubMessage::SendToHub { destination_chain: ChainNameRaw::from_str("large-data-chain").unwrap(), - message: Message::InterchainTransfer { + message: primitives::InterchainTransfer { token_id: [0u8; 32].into(), source_address: from_hex("1234"), destination_address: from_hex("5678"), amount: Uint256::from(1u128).try_into().unwrap(), data: Some(large_data.try_into().unwrap()), - }, + } + .into(), }; let encoded = original.clone().abi_encode(); @@ -664,13 +682,14 @@ mod tests { fn encode_decode_unicode_strings() { let original = HubMessage::SendToHub { destination_chain: ChainNameRaw::from_str("chain").unwrap(), - message: Message::DeployInterchainToken { + message: primitives::DeployInterchainToken { token_id: [0u8; 32].into(), name: "Unicode Token 🪙".try_into().unwrap(), symbol: "UNI🔣".try_into().unwrap(), decimals: 18, minter: Some(from_hex("abcd")), - }, + } + .into(), }; let encoded = original.clone().abi_encode(); diff --git a/contracts/interchain-token-service/src/contract.rs b/contracts/interchain-token-service/src/contract.rs index eaa92f2ba..e7d3aaf7a 100644 --- a/contracts/interchain-token-service/src/contract.rs +++ b/contracts/interchain-token-service/src/contract.rs @@ -36,14 +36,18 @@ pub enum Error { UnfreezeChain, #[error("failed to set chain config")] SetChainConfig, - #[error("failed to query its address")] - QueryItsContract, - #[error("failed to query all its addresses")] - QueryAllItsContracts, #[error("failed to disable execution")] DisableExecution, #[error("failed to enable execution")] EnableExecution, + #[error("failed to query its address")] + QueryItsContract, + #[error("failed to query all its addresses")] + QueryAllItsContracts, + #[error("failed to query a specific token instance")] + QueryTokenInstance, + #[error("failed to query the token config")] + QueryTokenConfig, } #[cfg_attr(not(feature = "library"), entry_point)] @@ -145,6 +149,12 @@ pub fn query(deps: Deps, _: Env, msg: QueryMsg) -> Result QueryMsg::AllItsContracts => { query::all_its_contracts(deps).change_context(Error::QueryAllItsContracts) } + QueryMsg::TokenInstance { chain, token_id } => { + query::token_instance(deps, chain, token_id).change_context(Error::QueryTokenInstance) + } + QueryMsg::TokenConfig { token_id } => { + query::token_config(deps, token_id).change_context(Error::QueryTokenConfig) + } }? .then(Ok) } diff --git a/contracts/interchain-token-service/src/contract/execute.rs b/contracts/interchain-token-service/src/contract/execute.rs index 6444b3d7a..a5dcd6a46 100644 --- a/contracts/interchain-token-service/src/contract/execute.rs +++ b/contracts/interchain-token-service/src/contract/execute.rs @@ -1,11 +1,15 @@ use axelar_wasm_std::{killswitch, nonempty, FnExt, IntoContractError}; use cosmwasm_std::{DepsMut, HexBinary, QuerierWrapper, Response, Storage}; use error_stack::{bail, ensure, report, Result, ResultExt}; -use router_api::{Address, ChainName, ChainNameRaw, CrossChainId}; +use router_api::{Address, ChainNameRaw, CrossChainId}; use crate::events::Event; use crate::primitives::HubMessage; -use crate::state::{self, is_chain_frozen, load_config, load_its_contract}; +use crate::state::{self, is_chain_frozen, load_config, load_its_contract, TokenDeploymentType}; +use crate::{ + DeployInterchainToken, DeployTokenManager, InterchainTransfer, Message, TokenConfig, TokenId, + TokenInstance, +}; #[derive(thiserror::Error, Debug, IntoContractError)] pub enum Error { @@ -25,22 +29,39 @@ pub enum Error { FailedExecuteMessage, #[error("execution is currently disabled")] ExecutionDisabled, - #[error("chain config for {0} already set")] - ChainConfigAlreadySet(ChainNameRaw), - #[error("failed to load chain config for chain {0}")] - LoadChainConfig(ChainNameRaw), - #[error("failed to save chain config for chain {0}")] - SaveChainConfig(ChainNameRaw), #[error("chain {0} is frozen")] ChainFrozen(ChainNameRaw), #[error("state error")] State, + #[error("chain config for {0} already set")] + ChainConfigAlreadySet(ChainNameRaw), + #[error("token {token_id} not deployed on chain {chain}")] + TokenNotDeployed { + token_id: TokenId, + chain: ChainNameRaw, + }, + #[error("token {token_id} already deployed on chain {chain}")] + TokenAlreadyDeployed { + token_id: TokenId, + chain: ChainNameRaw, + }, + #[error("token {token_id} can only be deployed from its origin chain {origin_chain} and not from {chain}")] + TokenDeployedFromNonOriginChain { + token_id: TokenId, + origin_chain: ChainNameRaw, + chain: ChainNameRaw, + }, + #[error("token supply invariant violated for token {token_id} on chain {chain}")] + TokenSupplyInvariantViolated { + token_id: TokenId, + chain: ChainNameRaw, + }, } /// Executes an incoming ITS message. /// /// This function handles the execution of ITS (Interchain Token Service) messages received from -/// its sources. It verifies the source address, decodes the message, applies balance tracking, +/// its sources. It verifies the source address, decodes the message, applies various checks and transformations, /// and forwards the message to the destination chain. pub fn execute_message( deps: DepsMut, @@ -52,63 +73,103 @@ pub fn execute_message( killswitch::is_contract_active(deps.storage), Error::ExecutionDisabled ); - ensure_its_source_address(deps.storage, &cc_id.source_chain, &source_address)?; + ensure_is_its_source_address(deps.storage, &cc_id.source_chain, &source_address)?; match HubMessage::abi_decode(&payload).change_context(Error::InvalidPayload)? { HubMessage::SendToHub { destination_chain, message, - } => { - let destination_address = load_its_contract(deps.storage, &destination_chain) - .change_context_lazy(|| Error::UnknownChain(destination_chain.clone()))?; - - verify_chains_not_frozen(deps.storage, &cc_id.source_chain, &destination_chain)?; - - let destination_payload = HubMessage::ReceiveFromHub { - source_chain: cc_id.source_chain.clone(), - message: message.clone(), - } - .abi_encode(); - - Ok(send_to_destination( - deps.storage, - deps.querier, - destination_chain.clone(), - destination_address, - destination_payload, - )? - .add_event( - Event::MessageReceived { - cc_id, - destination_chain, - message, - } - .into(), - )) - } + } => execute_message_on_hub(deps, cc_id, destination_chain, message), _ => bail!(Error::InvalidMessageType), } } -fn verify_chains_not_frozen( - storage: &dyn Storage, - source_chain: &ChainNameRaw, - destination_chain: &ChainNameRaw, +fn execute_message_on_hub( + deps: DepsMut, + cc_id: CrossChainId, + destination_chain: ChainNameRaw, + message: Message, +) -> Result { + let destination_address = load_its_contract(deps.storage, &destination_chain) + .change_context_lazy(|| Error::UnknownChain(destination_chain.clone()))?; + + apply_to_hub( + deps.storage, + cc_id.source_chain.clone(), + destination_chain.clone(), + &message, + )?; + + let destination_payload = HubMessage::ReceiveFromHub { + source_chain: cc_id.source_chain.clone(), + message: message.clone(), + } + .abi_encode(); + + Ok(send_to_destination( + deps.storage, + deps.querier, + &destination_chain, + destination_address, + destination_payload, + )? + .add_event( + Event::MessageReceived { + cc_id, + destination_chain, + message, + } + .into(), + )) +} + +fn apply_to_hub( + storage: &mut dyn Storage, + source_chain: ChainNameRaw, + destination_chain: ChainNameRaw, + message: &Message, ) -> Result<(), Error> { - for chain in [source_chain, destination_chain] { - if is_chain_frozen(storage, chain).change_context(Error::State)? { - return Err(report!(Error::ChainFrozen(chain.to_owned()))); + ensure_chain_not_frozen(storage, &source_chain)?; + ensure_chain_not_frozen(storage, &destination_chain)?; + + match message { + Message::InterchainTransfer(transfer) => { + apply_transfer(storage, source_chain, destination_chain, transfer)?; + } + Message::DeployInterchainToken(deploy_token) => { + apply_token_deployment( + storage, + source_chain, + destination_chain, + deploy_token.token_id.clone(), + &deploy_token.deployment_type(), + )?; + } + Message::DeployTokenManager(deploy_manager) => { + apply_token_deployment( + storage, + source_chain, + destination_chain, + deploy_manager.token_id.clone(), + &deploy_manager.deployment_type(), + )?; } } + Ok(()) } -fn normalize(chain: &ChainNameRaw) -> ChainName { - ChainName::try_from(chain.as_ref()).expect("invalid chain name") +fn ensure_chain_not_frozen(storage: &dyn Storage, chain: &ChainNameRaw) -> Result<(), Error> { + ensure!( + !is_chain_frozen(storage, chain).change_context(Error::State)?, + Error::ChainFrozen(chain.to_owned()) + ); + + Ok(()) } /// Ensures that the source address of the cross-chain message is the registered ITS contract for the source chain. -fn ensure_its_source_address( +fn ensure_is_its_source_address( storage: &dyn Storage, source_chain: &ChainNameRaw, source_address: &Address, @@ -127,7 +188,7 @@ fn ensure_its_source_address( fn send_to_destination( storage: &dyn Storage, querier: QuerierWrapper, - destination_chain: ChainNameRaw, + destination_chain: &ChainNameRaw, destination_address: Address, payload: HexBinary, ) -> Result { @@ -137,7 +198,7 @@ fn send_to_destination( client::ContractClient::new(querier, &config.axelarnet_gateway).into(); let call_contract_msg = - gateway.call_contract(normalize(&destination_chain), destination_address, payload); + gateway.call_contract(destination_chain.normalize(), destination_address, payload); Ok(Response::new().add_message(call_contract_msg)) } @@ -186,16 +247,189 @@ pub fn set_chain_config( max_uint: nonempty::Uint256, max_target_decimals: u8, ) -> Result { - match state::may_load_chain_config(deps.storage, &chain) - .change_context_lazy(|| Error::LoadChainConfig(chain.clone()))? - { + match state::may_load_chain_config(deps.storage, &chain).change_context(Error::State)? { Some(_) => bail!(Error::ChainConfigAlreadySet(chain)), None => state::save_chain_config(deps.storage, &chain, max_uint, max_target_decimals) - .change_context_lazy(|| Error::SaveChainConfig(chain))? + .change_context(Error::State)? .then(|_| Ok(Response::new())), } } +fn apply_transfer( + storage: &mut dyn Storage, + source_chain: ChainNameRaw, + destination_chain: ChainNameRaw, + transfer: &InterchainTransfer, +) -> Result<(), Error> { + subtract_amount_from_source(storage, source_chain, transfer)?; + add_amount_to_destination(storage, destination_chain, transfer) +} + +fn subtract_amount_from_source( + storage: &mut dyn Storage, + source_chain: ChainNameRaw, + transfer: &InterchainTransfer, +) -> Result<(), Error> { + let mut source_instance = + try_load_token_instance(storage, source_chain.clone(), transfer.token_id.clone())?; + + source_instance.supply = source_instance + .supply + .checked_sub(transfer.amount) + .change_context_lazy(|| Error::TokenSupplyInvariantViolated { + token_id: transfer.token_id.clone(), + chain: source_chain.clone(), + })?; + + state::save_token_instance( + storage, + source_chain, + transfer.token_id.clone(), + &source_instance, + ) + .change_context(Error::State) +} + +fn add_amount_to_destination( + storage: &mut dyn Storage, + destination_chain: ChainNameRaw, + transfer: &InterchainTransfer, +) -> Result<(), Error> { + let mut destination_instance = try_load_token_instance( + storage, + destination_chain.clone(), + transfer.token_id.clone(), + )?; + + destination_instance.supply = destination_instance + .supply + .checked_add(transfer.amount) + .change_context_lazy(|| Error::TokenSupplyInvariantViolated { + token_id: transfer.token_id.clone(), + chain: destination_chain.clone(), + })?; + + state::save_token_instance( + storage, + destination_chain, + transfer.token_id.clone(), + &destination_instance, + ) + .change_context(Error::State) +} + +fn apply_token_deployment( + storage: &mut dyn Storage, + source_chain: ChainNameRaw, + destination_chain: ChainNameRaw, + token_id: TokenId, + deployment_type: &TokenDeploymentType, +) -> Result<(), Error> { + ensure_token_not_deployed_on_destination(storage, token_id.clone(), destination_chain.clone())?; + + let token_config = + state::may_load_token_config(storage, &token_id).change_context(Error::State)?; + + if let Some(TokenConfig { origin_chain, .. }) = token_config { + ensure_origin_matches_source_chain(source_chain, origin_chain, token_id.clone())?; + } else { + initialize_token_on_origin(storage, source_chain, token_id.clone())?; + } + + let destination_instance = TokenInstance::new(deployment_type); + + state::save_token_instance(storage, destination_chain, token_id, &destination_instance) + .change_context(Error::State) +} + +fn try_load_token_instance( + storage: &mut dyn Storage, + chain: ChainNameRaw, + token_id: TokenId, +) -> Result { + state::may_load_token_instance(storage, chain.clone(), token_id.clone()) + .change_context(Error::State)? + .ok_or(report!(Error::TokenNotDeployed { + token_id: token_id.clone(), + chain, + })) +} + +fn ensure_origin_matches_source_chain( + source_chain: ChainNameRaw, + origin_chain: ChainNameRaw, + token_id: TokenId, +) -> Result<(), Error> { + ensure!( + origin_chain == source_chain, + Error::TokenDeployedFromNonOriginChain { + token_id, + origin_chain, + chain: source_chain.clone(), + } + ); + + Ok(()) +} + +fn initialize_token_on_origin( + storage: &mut dyn Storage, + source_chain: ChainNameRaw, + token_id: TokenId, +) -> Result<(), Error> { + // Token is being deployed for the first time + let token_config = TokenConfig { + origin_chain: source_chain.clone(), + }; + let instance = TokenInstance::new_on_origin(); + + state::save_token_config(storage, &token_id, &token_config) + .and_then(|_| state::save_token_instance(storage, source_chain, token_id, &instance)) + .change_context(Error::State)?; + Ok(()) +} + +/// Ensures that the token is not being redeployed to the same destination chain. +fn ensure_token_not_deployed_on_destination( + storage: &dyn Storage, + token_id: TokenId, + destination_chain: ChainNameRaw, +) -> Result<(), Error> { + let token_instance = + state::may_load_token_instance(storage, destination_chain.clone(), token_id.clone()) + .change_context(Error::State)?; + + ensure!( + token_instance.is_none(), + Error::TokenAlreadyDeployed { + token_id, + chain: destination_chain, + } + ); + + Ok(()) +} + +trait DeploymentType { + fn deployment_type(&self) -> TokenDeploymentType; +} + +impl DeploymentType for DeployInterchainToken { + fn deployment_type(&self) -> TokenDeploymentType { + if self.minter.is_some() { + TokenDeploymentType::CustomMinter + } else { + TokenDeploymentType::Trustless + } + } +} + +impl DeploymentType for DeployTokenManager { + fn deployment_type(&self) -> TokenDeploymentType { + TokenDeploymentType::CustomMinter + } +} + #[cfg(test)] mod tests { use assert_ok::assert_ok; @@ -210,7 +444,7 @@ mod tests { set_chain_config, unfreeze_chain, Error, }; use crate::state::{self, Config}; - use crate::{HubMessage, Message}; + use crate::{DeployInterchainToken, HubMessage, InterchainTransfer}; const SOLANA: &str = "solana"; const ETHEREUM: &str = "ethereum"; @@ -234,27 +468,46 @@ mod tests { let mut deps = mock_dependencies(); init(&mut deps); + let msg = HubMessage::SendToHub { + destination_chain: ChainNameRaw::try_from(SOLANA).unwrap(), + message: DeployInterchainToken { + token_id: [7u8; 32].into(), + name: "Test".parse().unwrap(), + symbol: "TEST".parse().unwrap(), + decimals: 18, + minter: None, + } + .into(), + }; + let cc_id = CrossChainId { + source_chain: ChainNameRaw::try_from(ETHEREUM).unwrap(), + message_id: HexTxHashAndEventIndex::new([1u8; 32], 0u32).into(), + }; + + assert_ok!(execute_message( + deps.as_mut(), + cc_id.clone(), + ITS_ADDRESS.to_string().try_into().unwrap(), + msg.clone().abi_encode(), + )); + assert_ok!(disable_execution(deps.as_mut())); let msg = HubMessage::SendToHub { destination_chain: ChainNameRaw::try_from(SOLANA).unwrap(), - message: Message::InterchainTransfer { + message: InterchainTransfer { token_id: [7u8; 32].into(), + amount: Uint256::one().try_into().unwrap(), source_address: its_address(), destination_address: its_address(), - amount: Uint256::one().try_into().unwrap(), data: None, - }, + } + .into(), }; + let res = execute_message( deps.as_mut(), - CrossChainId { - source_chain: ChainNameRaw::try_from(SOLANA).unwrap(), - message_id: HexTxHashAndEventIndex::new([1u8; 32], 0u32) - .to_string() - .try_into() - .unwrap(), - }, + cc_id.clone(), ITS_ADDRESS.to_string().try_into().unwrap(), msg.clone().abi_encode(), ); @@ -262,15 +515,20 @@ mod tests { assert_ok!(enable_execution(deps.as_mut())); + let msg = HubMessage::SendToHub { + destination_chain: ChainNameRaw::try_from(SOLANA).unwrap(), + message: DeployInterchainToken { + token_id: [1u8; 32].into(), + name: "Test".parse().unwrap(), + symbol: "TEST".parse().unwrap(), + decimals: 18, + minter: None, + } + .into(), + }; assert_ok!(execute_message( deps.as_mut(), - CrossChainId { - source_chain: ChainNameRaw::try_from(SOLANA).unwrap(), - message_id: HexTxHashAndEventIndex::new([1u8; 32], 0u32) - .to_string() - .try_into() - .unwrap(), - }, + cc_id.clone(), ITS_ADDRESS.to_string().try_into().unwrap(), msg.abi_encode(), )); @@ -288,13 +546,14 @@ mod tests { let msg = HubMessage::SendToHub { destination_chain, - message: Message::InterchainTransfer { + message: DeployInterchainToken { token_id: [7u8; 32].into(), - source_address: its_address(), - destination_address: its_address(), - amount: Uint256::one().try_into().unwrap(), - data: None, - }, + name: "Test".parse().unwrap(), + symbol: "TEST".parse().unwrap(), + decimals: 18, + minter: None, + } + .into(), }; let res = execute_message( deps.as_mut(), @@ -339,13 +598,14 @@ mod tests { let msg = HubMessage::SendToHub { destination_chain: destination_chain.clone(), - message: Message::InterchainTransfer { + message: DeployInterchainToken { token_id: [7u8; 32].into(), - source_address: its_address(), - destination_address: its_address(), - amount: Uint256::one().try_into().unwrap(), - data: None, - }, + name: "Test".parse().unwrap(), + symbol: "TEST".parse().unwrap(), + decimals: 18, + minter: None, + } + .into(), }; let cc_id = CrossChainId { source_chain, @@ -386,13 +646,14 @@ mod tests { let msg = HubMessage::SendToHub { destination_chain: destination_chain.clone(), - message: Message::InterchainTransfer { + message: DeployInterchainToken { token_id: [7u8; 32].into(), - source_address: its_address(), - destination_address: its_address(), - amount: Uint256::one().try_into().unwrap(), - data: None, - }, + name: "Test".parse().unwrap(), + symbol: "TEST".parse().unwrap(), + decimals: 18, + minter: None, + } + .into(), }; let cc_id = CrossChainId { source_chain, diff --git a/contracts/interchain-token-service/src/contract/query.rs b/contracts/interchain-token-service/src/contract/query.rs index 1ae2e043b..cb9da38b2 100644 --- a/contracts/interchain-token-service/src/contract/query.rs +++ b/contracts/interchain-token-service/src/contract/query.rs @@ -3,7 +3,10 @@ use cosmwasm_std::{to_json_binary, Binary, Deps}; use error_stack::{Result, ResultExt}; use router_api::ChainNameRaw; -use crate::state::{load_all_its_contracts, may_load_its_contract}; +use crate::state::{ + load_all_its_contracts, may_load_its_contract, may_load_token_config, may_load_token_instance, +}; +use crate::TokenId; #[derive(thiserror::Error, Debug, IntoContractError)] pub enum Error { @@ -23,3 +26,15 @@ pub fn all_its_contracts(deps: Deps) -> Result { let contract_addresses = load_all_its_contracts(deps.storage).change_context(Error::State)?; to_json_binary(&contract_addresses).change_context(Error::JsonSerialization) } + +pub fn token_instance(deps: Deps, chain: ChainNameRaw, token_id: TokenId) -> Result { + let token_instance = + may_load_token_instance(deps.storage, chain, token_id).change_context(Error::State)?; + to_json_binary(&token_instance).change_context(Error::JsonSerialization) +} + +pub fn token_config(deps: Deps, token_id: TokenId) -> Result { + let token_config = + may_load_token_config(deps.storage, &token_id).change_context(Error::State)?; + to_json_binary(&token_config).change_context(Error::JsonSerialization) +} diff --git a/contracts/interchain-token-service/src/events.rs b/contracts/interchain-token-service/src/events.rs index 6d98c9597..b13634dfc 100644 --- a/contracts/interchain-token-service/src/events.rs +++ b/contracts/interchain-token-service/src/events.rs @@ -2,6 +2,7 @@ use axelar_wasm_std::event::EventExt; use router_api::{Address, ChainNameRaw, CrossChainId}; use crate::primitives::Message; +use crate::{DeployInterchainToken, DeployTokenManager, InterchainTransfer}; pub enum Event { MessageReceived { @@ -55,35 +56,35 @@ fn make_message_event( .add_attribute("message_type", msg.as_ref().to_string()); match msg { - Message::InterchainTransfer { + Message::InterchainTransfer(InterchainTransfer { token_id, source_address, destination_address, amount, data, - } => event + }) => event .add_attribute("token_id", token_id.to_string()) .add_attribute("source_address", source_address.to_string()) .add_attribute("destination_address", destination_address.to_string()) .add_attribute("amount", amount.to_string()) .add_attribute_if_some("data", data.map(|data| data.to_string())), - Message::DeployInterchainToken { + Message::DeployInterchainToken(DeployInterchainToken { token_id, name, symbol, decimals, minter, - } => event + }) => event .add_attribute("token_id", token_id.to_string()) .add_attribute("name", name) .add_attribute("symbol", symbol) .add_attribute("decimals", decimals.to_string()) .add_attribute_if_some("minter", minter.map(|minter| minter.to_string())), - Message::DeployTokenManager { + Message::DeployTokenManager(DeployTokenManager { token_id, token_manager_type, params, - } => event + }) => event .add_attribute("token_id", token_id.to_string()) .add_attribute( "token_manager_type", @@ -99,30 +100,36 @@ mod test { use router_api::CrossChainId; use crate::events::Event; - use crate::{Message, TokenId, TokenManagerType}; + use crate::{ + DeployInterchainToken, DeployTokenManager, InterchainTransfer, Message, TokenId, + TokenManagerType, + }; #[test] fn message_received_with_all_attributes() { let test_cases: Vec = vec![ - Message::InterchainTransfer { + InterchainTransfer { token_id: TokenId::new([1; 32]), source_address: HexBinary::from([1; 32]).try_into().unwrap(), destination_address: HexBinary::from([1, 2, 3, 4]).try_into().unwrap(), amount: 1u64.try_into().unwrap(), data: Some(HexBinary::from([1, 2, 3, 4]).try_into().unwrap()), - }, - Message::DeployInterchainToken { + } + .into(), + DeployInterchainToken { token_id: TokenId::new([1; 32]), name: "Test".try_into().unwrap(), symbol: "TST".try_into().unwrap(), decimals: 18, minter: Some(HexBinary::from([1; 32]).try_into().unwrap()), - }, - Message::DeployTokenManager { + } + .into(), + DeployTokenManager { token_id: TokenId::new([1; 32]), token_manager_type: TokenManagerType::MintBurn, params: HexBinary::from([1, 2, 3, 4]).try_into().unwrap(), - }, + } + .into(), ]; let events: Vec<_> = test_cases @@ -144,39 +151,44 @@ mod test { #[test] fn message_received_with_empty_attributes() { let test_cases: Vec = vec![ - Message::InterchainTransfer { + InterchainTransfer { token_id: TokenId::new([1; 32]), source_address: HexBinary::from([1; 32]).try_into().unwrap(), destination_address: HexBinary::from([1, 2, 3, 4]).try_into().unwrap(), amount: 1u64.try_into().unwrap(), data: None, - }, - Message::InterchainTransfer { + } + .into(), + InterchainTransfer { token_id: TokenId::new([1; 32]), source_address: HexBinary::from([0u8]).try_into().unwrap(), destination_address: HexBinary::from([0u8]).try_into().unwrap(), amount: 1u64.try_into().unwrap(), data: None, - }, - Message::DeployInterchainToken { + } + .into(), + DeployInterchainToken { token_id: TokenId::new([1; 32]), name: "Test".try_into().unwrap(), symbol: "TST".try_into().unwrap(), decimals: 18, minter: None, - }, - Message::DeployInterchainToken { + } + .into(), + DeployInterchainToken { token_id: TokenId::new([1; 32]), name: "t".try_into().unwrap(), symbol: "T".try_into().unwrap(), decimals: 0, minter: None, - }, - Message::DeployTokenManager { + } + .into(), + DeployTokenManager { token_id: TokenId::new([1; 32]), token_manager_type: TokenManagerType::MintBurn, params: HexBinary::from([0u8]).try_into().unwrap(), - }, + } + .into(), ]; let events: Vec<_> = test_cases diff --git a/contracts/interchain-token-service/src/lib.rs b/contracts/interchain-token-service/src/lib.rs index 45a424182..bbdc33d88 100644 --- a/contracts/interchain-token-service/src/lib.rs +++ b/contracts/interchain-token-service/src/lib.rs @@ -6,3 +6,4 @@ pub mod contract; pub mod events; pub mod msg; mod state; +pub use state::{TokenConfig, TokenInstance, TokenSupply}; diff --git a/contracts/interchain-token-service/src/msg.rs b/contracts/interchain-token-service/src/msg.rs index 76d8c09ca..b38732379 100644 --- a/contracts/interchain-token-service/src/msg.rs +++ b/contracts/interchain-token-service/src/msg.rs @@ -6,6 +6,9 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use msgs_derive::EnsurePermissions; use router_api::{Address, ChainNameRaw}; +use crate::state::{TokenConfig, TokenInstance}; +use crate::TokenId; + #[cw_serde] pub struct InstantiateMsg { pub governance_address: String, @@ -63,7 +66,16 @@ pub enum QueryMsg { /// Query the ITS contract address registered for a chain #[returns(Option
)] ItsContract { chain: ChainNameRaw }, - /// Query all registererd ITS contract addresses + /// Query all registered ITS contract addresses #[returns(HashMap)] AllItsContracts, + /// Query a token instance on a specific chain + #[returns(Option)] + TokenInstance { + chain: ChainNameRaw, + token_id: TokenId, + }, + /// Query the configuration parameters for a token + #[returns(Option)] + TokenConfig { token_id: TokenId }, } diff --git a/contracts/interchain-token-service/src/primitives.rs b/contracts/interchain-token-service/src/primitives.rs index 55c8d3e7f..4e5dcdeb3 100644 --- a/contracts/interchain-token-service/src/primitives.rs +++ b/contracts/interchain-token-service/src/primitives.rs @@ -40,41 +40,71 @@ pub enum TokenManagerType { #[derive(Eq, strum::AsRefStr)] pub enum Message { /// Transfer ITS tokens between different chains - InterchainTransfer { - /// The unique identifier of the token being transferred - token_id: TokenId, - /// The address that called the ITS contract on the source chain - source_address: nonempty::HexBinary, - /// The address that the token will be sent to on the destination chain - /// If data is not empty, this address will given the token and executed as a contract on the destination chain - destination_address: nonempty::HexBinary, - /// The amount of tokens to transfer - amount: nonempty::Uint256, - /// An optional payload to be provided to the destination address, if `data` is not empty - data: Option, - }, + InterchainTransfer(InterchainTransfer), /// Deploy a new interchain token on the destination chain - DeployInterchainToken { - /// The unique identifier of the token to be deployed - token_id: TokenId, - /// The name of the token - name: nonempty::String, - /// The symbol of the token - symbol: nonempty::String, - /// The number of decimal places the token supports - decimals: u8, - /// An additional minter of the token (optional). ITS on the external chain is always a minter. - minter: Option, - }, + DeployInterchainToken(DeployInterchainToken), /// Deploy a new token manager on the destination chain - DeployTokenManager { - /// The unique identifier of the token that the token manager will manage - token_id: TokenId, - /// The type of token manager to deploy - token_manager_type: TokenManagerType, - /// The parameters to be provided to the token manager contract - params: nonempty::HexBinary, - }, + DeployTokenManager(DeployTokenManager), +} + +#[cw_serde] +#[derive(Eq)] +pub struct InterchainTransfer { + /// The unique identifier of the token being transferred + pub token_id: TokenId, + /// The address that called the ITS contract on the source chain + pub source_address: nonempty::HexBinary, + /// The address that the token will be sent to on the destination chain + /// If data is not empty, this address will give the token and executed as a contract on the destination chain + pub destination_address: nonempty::HexBinary, + /// The amount of tokens to transfer + pub amount: nonempty::Uint256, + /// An optional payload to be provided to the destination address, if `data` is not empty + pub data: Option, +} + +impl From for Message { + fn from(value: InterchainTransfer) -> Self { + Message::InterchainTransfer(value) + } +} + +#[cw_serde] +#[derive(Eq)] +pub struct DeployInterchainToken { + /// The unique identifier of the token to be deployed + pub token_id: TokenId, + /// The name of the token + pub name: nonempty::String, + /// The symbol of the token + pub symbol: nonempty::String, + /// The number of decimal places the token supports + pub decimals: u8, + /// An additional minter of the token (optional). ITS on the external chain is always a minter. + pub minter: Option, +} + +impl From for Message { + fn from(value: DeployInterchainToken) -> Self { + Message::DeployInterchainToken(value) + } +} + +#[cw_serde] +#[derive(Eq)] +pub struct DeployTokenManager { + /// The unique identifier of the token that the token manager will manage + pub token_id: TokenId, + /// The type of token manager to deploy + pub token_manager_type: TokenManagerType, + /// The parameters to be provided to the token manager contract + pub params: nonempty::HexBinary, +} + +impl From for Message { + fn from(value: DeployTokenManager) -> Self { + Message::DeployTokenManager(value) + } } /// A message sent between ITS edge contracts and the ITS hub contract (defined in this crate). @@ -103,14 +133,18 @@ impl HubMessage { HubMessage::ReceiveFromHub { message, .. } => message, } } + + pub fn token_id(&self) -> TokenId { + self.message().token_id() + } } impl Message { pub fn token_id(&self) -> TokenId { match self { - Message::InterchainTransfer { token_id, .. } - | Message::DeployInterchainToken { token_id, .. } - | Message::DeployTokenManager { token_id, .. } => token_id.clone(), + Message::InterchainTransfer(InterchainTransfer { token_id, .. }) + | Message::DeployInterchainToken(DeployInterchainToken { token_id, .. }) + | Message::DeployTokenManager(DeployTokenManager { token_id, .. }) => token_id.clone(), } } } diff --git a/contracts/interchain-token-service/src/state.rs b/contracts/interchain-token-service/src/state.rs index 053aacfc1..96a3eb1e6 100644 --- a/contracts/interchain-token-service/src/state.rs +++ b/contracts/interchain-token-service/src/state.rs @@ -1,12 +1,14 @@ use std::collections::HashMap; -use axelar_wasm_std::{nonempty, IntoContractError}; +use axelar_wasm_std::{nonempty, FnExt, IntoContractError}; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{ensure, Addr, StdError, Storage}; +use cosmwasm_std::{ensure, Addr, OverflowError, StdError, Storage, Uint256}; use cw_storage_plus::{Item, Map}; use error_stack::{report, Result, ResultExt}; use router_api::{Address, ChainNameRaw}; +use crate::TokenId; + #[derive(thiserror::Error, Debug, IntoContractError)] pub enum Error { #[error("ITS contract got into an invalid state, its config is missing")] @@ -35,9 +37,77 @@ pub struct ChainConfig { frozen: bool, } +#[cw_serde] +pub enum TokenSupply { + /// The total token supply bridged to this chain. + /// ITS Hub will not allow bridging back more than this amount of the token from the corresponding chain. + Tracked(Uint256), + /// The token supply bridged to this chain is not tracked. + Untracked, +} + +impl TokenSupply { + pub fn checked_add(self, amount: nonempty::Uint256) -> Result { + match self { + TokenSupply::Untracked => TokenSupply::Untracked, + TokenSupply::Tracked(supply) => { + TokenSupply::Tracked(supply.checked_add(amount.into())?) + } + } + .then(Ok) + } + pub fn checked_sub(self, amount: nonempty::Uint256) -> Result { + match self { + TokenSupply::Untracked => TokenSupply::Untracked, + TokenSupply::Tracked(supply) => { + TokenSupply::Tracked(supply.checked_sub(amount.into())?) + } + } + .then(Ok) + } +} + +/// Information about a token on a specific chain. +#[cw_serde] +pub struct TokenInstance { + pub supply: TokenSupply, +} + +impl TokenInstance { + pub fn new_on_origin() -> Self { + Self { + supply: TokenSupply::Untracked, + } + } + + pub fn new(deployment_type: &TokenDeploymentType) -> Self { + let supply = match deployment_type { + TokenDeploymentType::Trustless => TokenSupply::Tracked(Uint256::zero()), + _ => TokenSupply::Untracked, + }; + + Self { supply } + } +} + +/// The deployment type of the token. +pub enum TokenDeploymentType { + /// The token is trustless, i.e. only owned by ITS. + Trustless, + /// The token has a custom minter. + CustomMinter, +} + +#[cw_serde] +pub struct TokenConfig { + pub origin_chain: ChainNameRaw, +} + const CONFIG: Item = Item::new("config"); const ITS_CONTRACTS: Map<&ChainNameRaw, Address> = Map::new("its_contracts"); const CHAIN_CONFIGS: Map<&ChainNameRaw, ChainConfig> = Map::new("chain_configs"); +const TOKEN_INSTANCE: Map<&(ChainNameRaw, TokenId), TokenInstance> = Map::new("token_instance"); +const TOKEN_CONFIGS: Map<&TokenId, TokenConfig> = Map::new("token_configs"); pub fn load_config(storage: &dyn Storage) -> Config { CONFIG @@ -138,9 +208,7 @@ pub fn freeze_chain(storage: &mut dyn Storage, chain: &ChainNameRaw) -> Result Ok(ChainConfig { frozen: true, ..x }), - None => Err(StdError::NotFound { - kind: "chain not found".to_string(), - }), + None => Err(StdError::not_found("chain not found".to_string())), }) .change_context(Error::ChainNotFound(chain.to_owned())) } @@ -152,13 +220,51 @@ pub fn unfreeze_chain( CHAIN_CONFIGS .update(storage, chain, |elt| match elt { Some(x) => Ok(ChainConfig { frozen: false, ..x }), - None => Err(StdError::NotFound { - kind: "chain not found".to_string(), - }), + None => Err(StdError::not_found("chain not found".to_string())), }) .change_context(Error::ChainNotFound(chain.to_owned())) } +pub fn save_token_instance( + storage: &mut dyn Storage, + chain: ChainNameRaw, + token_id: TokenId, + token_instance: &TokenInstance, +) -> Result<(), Error> { + TOKEN_INSTANCE + .save(storage, &(chain, token_id), token_instance) + .change_context(Error::Storage) +} + +pub fn may_load_token_instance( + storage: &dyn Storage, + chain: ChainNameRaw, + token_id: TokenId, +) -> Result, Error> { + TOKEN_INSTANCE + .may_load(storage, &(chain, token_id)) + .change_context(Error::Storage) +} + +pub fn may_load_token_config( + storage: &dyn Storage, + token_id: &TokenId, +) -> Result, Error> { + TOKEN_CONFIGS + .may_load(storage, token_id) + .change_context(Error::Storage) +} + +pub fn save_token_config( + storage: &mut dyn Storage, + token_id: &TokenId, + token_config: &TokenConfig, +) -> Result<(), Error> { + TOKEN_CONFIGS + .save(storage, token_id, token_config) + .change_context(Error::Storage) +} + #[cfg(test)] mod tests { use assert_ok::assert_ok; diff --git a/contracts/interchain-token-service/tests/execute.rs b/contracts/interchain-token-service/tests/execute.rs index cfaf94d9f..cbd202c81 100644 --- a/contracts/interchain-token-service/tests/execute.rs +++ b/contracts/interchain-token-service/tests/execute.rs @@ -2,16 +2,19 @@ use std::str::FromStr; use assert_ok::assert_ok; use axelar_wasm_std::response::inspect_response_msg; -use axelar_wasm_std::{assert_err_contains, permission_control}; +use axelar_wasm_std::{assert_err_contains, nonempty, permission_control}; use axelarnet_gateway::msg::ExecuteMsg as AxelarnetGatewayExecuteMsg; use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cosmwasm_std::{HexBinary, Uint256}; use interchain_token_service::contract::{self, ExecuteError}; use interchain_token_service::events::Event; use interchain_token_service::msg::ExecuteMsg; -use interchain_token_service::{HubMessage, Message, TokenId, TokenManagerType}; +use interchain_token_service::{ + DeployInterchainToken, DeployTokenManager, HubMessage, InterchainTransfer, TokenId, + TokenManagerType, TokenSupply, +}; use router_api::{Address, ChainName, ChainNameRaw, CrossChainId}; -use utils::{make_deps, params, TestMessage}; +use utils::{params, TestMessage}; mod utils; @@ -80,72 +83,49 @@ fn deregistering_unknown_chain_fails() { #[test] fn execute_hub_message_succeeds() { - let mut deps = make_deps(); - utils::instantiate_contract(deps.as_mut()).unwrap(); - - let TestMessage { - router_message, - source_its_chain, - source_its_contract, - destination_its_chain, - destination_its_contract, - .. - } = TestMessage::dummy(); - - utils::register_its_contract( - deps.as_mut(), - source_its_chain.clone(), - source_its_contract.clone(), - ) - .unwrap(); - utils::register_its_contract( - deps.as_mut(), - destination_its_chain.clone(), - destination_its_contract.clone(), - ) - .unwrap(); - - let max_uint = Uint256::from_str("120000000000000000000000000") - .unwrap() - .try_into() - .unwrap(); - let decimals = 18; - - assert_ok!(utils::set_chain_config( - deps.as_mut(), - source_its_chain.clone(), - max_uint, - decimals - )); - - assert_ok!(utils::set_chain_config( - deps.as_mut(), - destination_its_chain.clone(), - max_uint, - decimals - )); + let ( + mut deps, + TestMessage { + router_message, + source_its_chain, + source_its_contract, + destination_its_chain, + destination_its_contract, + .. + }, + ) = utils::setup(); - let token_id = TokenId::new([1; 32]); let test_messages = vec![ - Message::InterchainTransfer { - token_id: token_id.clone(), - source_address: HexBinary::from([1; 32]).try_into().unwrap(), - destination_address: HexBinary::from([2; 32]).try_into().unwrap(), - amount: 1u64.try_into().unwrap(), - data: Some(HexBinary::from([1, 2, 3, 4]).try_into().unwrap()), - }, - Message::DeployInterchainToken { - token_id: token_id.clone(), + DeployInterchainToken { + token_id: TokenId::new([1; 32]), name: "Test".try_into().unwrap(), symbol: "TST".try_into().unwrap(), decimals: 18, minter: Some(HexBinary::from([1; 32]).try_into().unwrap()), - }, - Message::DeployTokenManager { - token_id: token_id.clone(), + } + .into(), + InterchainTransfer { + token_id: TokenId::new([1; 32]), + source_address: HexBinary::from([1; 32]).try_into().unwrap(), + destination_address: HexBinary::from([2; 32]).try_into().unwrap(), + amount: 1u64.try_into().unwrap(), + data: Some(HexBinary::from([1, 2, 3, 4]).try_into().unwrap()), + } + .into(), + DeployTokenManager { + token_id: TokenId::new([2; 32]), token_manager_type: TokenManagerType::MintBurn, params: HexBinary::from([1, 2, 3, 4]).try_into().unwrap(), - }, + } + .into(), + InterchainTransfer { + token_id: TokenId::new([2; 32]), + source_address: HexBinary::from([1; 32]).try_into().unwrap(), + destination_address: HexBinary::from([2; 32]).try_into().unwrap(), + amount: 1u64.try_into().unwrap(), + data: Some(HexBinary::from([1, 2, 3, 4]).try_into().unwrap()), + } + .into(), ]; let responses: Vec<_> = test_messages @@ -155,18 +135,17 @@ fn execute_hub_message_succeeds() { destination_chain: destination_its_chain.clone(), message, }; - let payload = hub_message.clone().abi_encode(); let receive_payload = HubMessage::ReceiveFromHub { source_chain: source_its_chain.clone(), message: hub_message.message().clone(), } .abi_encode(); - let response = assert_ok!(utils::execute( + let response = assert_ok!(utils::execute_hub_message( deps.as_mut(), router_message.cc_id.clone(), source_its_contract.clone(), - payload, + hub_message.clone(), )); let msg: AxelarnetGatewayExecuteMsg = assert_ok!(inspect_response_msg(response.clone())); @@ -573,3 +552,423 @@ fn set_chain_config_should_fail_if_chain_config_is_already_set() { ExecuteError::ChainConfigAlreadySet(_) ) } + +#[test] +fn deploy_interchain_token_tracks_supply() { + let ( + mut deps, + TestMessage { + router_message, + source_its_chain, + source_its_contract, + destination_its_chain, + destination_its_contract, + hub_message, + }, + ) = utils::setup(); + + let token_id = hub_message.token_id(); + let amount = nonempty::Uint256::try_from(400u64).unwrap(); + + assert_ok!(utils::execute_hub_message( + deps.as_mut(), + router_message.cc_id.clone(), + source_its_contract.clone(), + hub_message, + )); + + let msg = HubMessage::SendToHub { + destination_chain: destination_its_chain.clone(), + message: InterchainTransfer { + token_id: token_id.clone(), + source_address: HexBinary::from([1; 32]).try_into().unwrap(), + destination_address: HexBinary::from([2; 32]).try_into().unwrap(), + amount, + data: None, + } + .into(), + }; + assert_ok!(utils::execute_hub_message( + deps.as_mut(), + router_message.cc_id.clone(), + source_its_contract.clone(), + msg, + )); + + assert_eq!( + assert_ok!(utils::query_token_instance( + deps.as_ref(), + source_its_chain.clone(), + token_id.clone() + )) + .unwrap() + .supply, + TokenSupply::Untracked, + ); + assert_eq!( + assert_ok!(utils::query_token_instance( + deps.as_ref(), + destination_its_chain.clone(), + token_id.clone() + )) + .unwrap() + .supply, + TokenSupply::Tracked(amount.into()) + ); + + // Send the same amount back + let msg = HubMessage::SendToHub { + destination_chain: source_its_chain.clone(), + message: InterchainTransfer { + token_id: token_id.clone(), + source_address: HexBinary::from([1; 32]).try_into().unwrap(), + destination_address: HexBinary::from([2; 32]).try_into().unwrap(), + amount, + data: None, + } + .into(), + }; + assert_ok!(utils::execute_hub_message( + deps.as_mut(), + CrossChainId::new( + destination_its_chain.clone(), + router_message.cc_id.message_id.clone() + ) + .unwrap(), + destination_its_contract, + msg, + )); + + assert_eq!( + assert_ok!(utils::query_token_instance( + deps.as_ref(), + source_its_chain.clone(), + token_id.clone() + )) + .unwrap() + .supply, + TokenSupply::Untracked + ); + assert_eq!( + assert_ok!(utils::query_token_instance( + deps.as_ref(), + destination_its_chain, + token_id.clone() + )) + .unwrap() + .supply, + TokenSupply::Tracked(Uint256::zero()) + ); +} + +#[test] +fn deploy_interchain_token_with_minter_does_not_track_supply() { + let ( + mut deps, + TestMessage { + router_message, + source_its_chain, + source_its_contract, + destination_its_chain, + destination_its_contract, + .. + }, + ) = utils::setup(); + + let token_id = TokenId::new([1u8; 32]); + let amount = nonempty::Uint256::try_from(400u64).unwrap(); + + let msg = HubMessage::SendToHub { + destination_chain: destination_its_chain.clone(), + message: DeployInterchainToken { + token_id: token_id.clone(), + name: "Test".try_into().unwrap(), + symbol: "TST".try_into().unwrap(), + decimals: 18, + minter: Some(HexBinary::from([1; 32]).try_into().unwrap()), + } + .into(), + }; + assert_ok!(utils::execute_hub_message( + deps.as_mut(), + router_message.cc_id.clone(), + source_its_contract.clone(), + msg, + )); + for chain in [source_its_chain.clone(), destination_its_chain.clone()] { + assert_eq!( + assert_ok!(utils::query_token_instance( + deps.as_ref(), + chain.clone(), + token_id.clone() + )) + .unwrap() + .supply, + TokenSupply::Untracked, + ); + } + + let msg = HubMessage::SendToHub { + destination_chain: destination_its_chain.clone(), + message: InterchainTransfer { + token_id: token_id.clone(), + source_address: HexBinary::from([1; 32]).try_into().unwrap(), + destination_address: HexBinary::from([2; 32]).try_into().unwrap(), + amount, + data: None, + } + .into(), + }; + assert_ok!(utils::execute_hub_message( + deps.as_mut(), + router_message.cc_id.clone(), + source_its_contract.clone(), + msg, + )); + + // Send a larger amount back + let msg = HubMessage::SendToHub { + destination_chain: source_its_chain.clone(), + message: InterchainTransfer { + token_id: token_id.clone(), + source_address: HexBinary::from([1; 32]).try_into().unwrap(), + destination_address: HexBinary::from([2; 32]).try_into().unwrap(), + amount: amount.strict_add(Uint256::one()).try_into().unwrap(), + data: None, + } + .into(), + }; + assert_ok!(utils::execute_hub_message( + deps.as_mut(), + CrossChainId::new( + destination_its_chain.clone(), + router_message.cc_id.message_id.clone() + ) + .unwrap(), + destination_its_contract, + msg, + )); + + for chain in [source_its_chain.clone(), destination_its_chain.clone()] { + assert_eq!( + assert_ok!(utils::query_token_instance( + deps.as_ref(), + chain.clone(), + token_id.clone() + )) + .unwrap() + .supply, + TokenSupply::Untracked, + ); + } +} + +#[test] +fn interchain_transfer_exceeds_supply_fails() { + let ( + mut deps, + TestMessage { + router_message, + source_its_chain, + source_its_contract, + destination_its_chain, + destination_its_contract, + hub_message: msg, + }, + ) = utils::setup(); + + let token_id = msg.token_id(); + let amount = nonempty::Uint256::try_from(400u64).unwrap(); + + assert_ok!(utils::execute_hub_message( + deps.as_mut(), + router_message.cc_id.clone(), + source_its_contract.clone(), + msg, + )); + + let msg = HubMessage::SendToHub { + destination_chain: source_its_chain.clone(), + message: InterchainTransfer { + token_id: token_id.clone(), + source_address: HexBinary::from([1; 32]).try_into().unwrap(), + destination_address: HexBinary::from([2; 32]).try_into().unwrap(), + amount: 1u64.try_into().unwrap(), + data: None, + } + .into(), + }; + assert_err_contains!( + utils::execute_hub_message( + deps.as_mut(), + CrossChainId::new( + destination_its_chain.clone(), + router_message.cc_id.message_id.clone() + ) + .unwrap(), + destination_its_contract.clone(), + msg, + ), + ExecuteError, + ExecuteError::TokenSupplyInvariantViolated { .. } + ); + + let msg = HubMessage::SendToHub { + destination_chain: destination_its_chain.clone(), + message: InterchainTransfer { + token_id: token_id.clone(), + source_address: HexBinary::from([1; 32]).try_into().unwrap(), + destination_address: HexBinary::from([2; 32]).try_into().unwrap(), + amount, + data: None, + } + .into(), + }; + assert_ok!(utils::execute_hub_message( + deps.as_mut(), + router_message.cc_id.clone(), + source_its_contract.clone(), + msg, + )); + + let msg = HubMessage::SendToHub { + destination_chain: source_its_chain.clone(), + message: InterchainTransfer { + token_id: token_id.clone(), + source_address: HexBinary::from([1; 32]).try_into().unwrap(), + destination_address: HexBinary::from([2; 32]).try_into().unwrap(), + amount: amount.strict_add(Uint256::one()).try_into().unwrap(), + data: None, + } + .into(), + }; + assert_err_contains!( + utils::execute_hub_message( + deps.as_mut(), + CrossChainId::new(destination_its_chain, router_message.cc_id.message_id).unwrap(), + destination_its_contract, + msg, + ), + ExecuteError, + ExecuteError::TokenSupplyInvariantViolated { .. } + ); +} + +#[test] +fn deploy_interchain_token_submitted_twice_fails() { + let ( + mut deps, + TestMessage { + router_message, + source_its_contract, + hub_message: msg, + .. + }, + ) = utils::setup(); + + assert_ok!(utils::execute_hub_message( + deps.as_mut(), + router_message.cc_id.clone(), + source_its_contract.clone(), + msg.clone(), + )); + + assert_err_contains!( + utils::execute_hub_message( + deps.as_mut(), + router_message.cc_id.clone(), + source_its_contract.clone(), + msg, + ), + ExecuteError, + ExecuteError::TokenAlreadyDeployed { .. } + ); +} + +#[test] +fn deploy_interchain_token_from_non_origin_chain_fails() { + let ( + mut deps, + TestMessage { + router_message, + source_its_contract, + hub_message: msg, + .. + }, + ) = utils::setup(); + + assert_ok!(utils::execute_hub_message( + deps.as_mut(), + router_message.cc_id.clone(), + source_its_contract.clone(), + msg.clone(), + )); + + // Deploy the same token from a different origin chain to a different destination chain now + let another_source_chain: ChainNameRaw = "another-source-chain".parse().unwrap(); + utils::register_chain( + &mut deps, + another_source_chain.clone(), + source_its_contract.clone(), + ); + let another_destination_chain: ChainNameRaw = "another-dest-chain".parse().unwrap(); + utils::register_chain( + &mut deps, + another_destination_chain.clone(), + source_its_contract.clone(), + ); + + let new_destination_msg = HubMessage::SendToHub { + destination_chain: another_source_chain.clone(), + message: msg.message().clone(), + }; + + assert_err_contains!( + utils::execute_hub_message( + deps.as_mut(), + CrossChainId::new(another_source_chain, router_message.cc_id.message_id).unwrap(), + source_its_contract, + new_destination_msg, + ), + ExecuteError, + ExecuteError::TokenDeployedFromNonOriginChain { .. } + ); +} + +#[test] +fn deploy_interchain_token_to_multiple_destination_succeeds() { + let ( + mut deps, + TestMessage { + router_message, + source_its_contract, + hub_message: msg, + .. + }, + ) = utils::setup(); + + assert_ok!(utils::execute_hub_message( + deps.as_mut(), + router_message.cc_id.clone(), + source_its_contract.clone(), + msg.clone(), + )); + + let another_chain: ChainNameRaw = "another-chain".parse().unwrap(); + utils::register_chain( + &mut deps, + another_chain.clone(), + source_its_contract.clone(), + ); + + let msg = HubMessage::SendToHub { + destination_chain: another_chain, + message: msg.message().clone(), + }; + assert_ok!(utils::execute_hub_message( + deps.as_mut(), + router_message.cc_id.clone(), + source_its_contract.clone(), + msg.clone(), + )); +} diff --git a/contracts/interchain-token-service/tests/query.rs b/contracts/interchain-token-service/tests/query.rs index 93f96af01..c50c996f0 100644 --- a/contracts/interchain-token-service/tests/query.rs +++ b/contracts/interchain-token-service/tests/query.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use assert_ok::assert_ok; use cosmwasm_std::testing::mock_dependencies; +use interchain_token_service::TokenId; use router_api::{Address, ChainNameRaw}; mod utils; @@ -67,3 +68,15 @@ fn query_all_its_contracts() { let queried_addresses = assert_ok!(utils::query_all_its_contracts(deps.as_ref())); assert_eq!(queried_addresses, its_contracts); } + +#[test] +fn query_token_chain_config() { + let mut deps = mock_dependencies(); + utils::instantiate_contract(deps.as_mut()).unwrap(); + + let chain: ChainNameRaw = "ethereum".parse().unwrap(); + let token_id: TokenId = TokenId::new([1; 32]); + + let config = utils::query_token_instance(deps.as_ref(), chain, token_id).unwrap(); + assert_eq!(config, None); +} diff --git a/contracts/interchain-token-service/tests/testdata/execute_hub_message_succeeds.golden b/contracts/interchain-token-service/tests/testdata/execute_hub_message_succeeds.golden index 0985ec368..69e88351f 100644 --- a/contracts/interchain-token-service/tests/testdata/execute_hub_message_succeeds.golden +++ b/contracts/interchain-token-service/tests/testdata/execute_hub_message_succeeds.golden @@ -1,4 +1,63 @@ [ + { + "messages": [ + { + "id": 0, + "msg": { + "wasm": { + "execute": { + "contract_addr": "gateway", + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDU0NjU3Mzc0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNTQ1MzU0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn19", + "funds": [] + } + } + }, + "gas_limit": null, + "reply_on": "never" + } + ], + "attributes": [], + "events": [ + { + "type": "message_received", + "attributes": [ + { + "key": "cc_id", + "value": "source-its-chain_message-id" + }, + { + "key": "destination_chain", + "value": "dest-its-chain" + }, + { + "key": "message_type", + "value": "DeployInterchainToken" + }, + { + "key": "token_id", + "value": "0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "key": "name", + "value": "Test" + }, + { + "key": "symbol", + "value": "TST" + }, + { + "key": "decimals", + "value": "18" + }, + { + "key": "minter", + "value": "0101010101010101010101010101010101010101010101010101010101010101" + } + ] + } + ], + "data": null + }, { "messages": [ { @@ -66,7 +125,7 @@ "wasm": { "execute": { "contract_addr": "gateway", - "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDU0NjU3Mzc0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNTQ1MzU0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn19", + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDQwMTAyMDMwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn19", "funds": [] } } @@ -90,27 +149,19 @@ }, { "key": "message_type", - "value": "DeployInterchainToken" + "value": "DeployTokenManager" }, { "key": "token_id", - "value": "0101010101010101010101010101010101010101010101010101010101010101" - }, - { - "key": "name", - "value": "Test" - }, - { - "key": "symbol", - "value": "TST" + "value": "0202020202020202020202020202020202020202020202020202020202020202" }, { - "key": "decimals", - "value": "18" + "key": "token_manager_type", + "value": "MintBurn" }, { - "key": "minter", - "value": "0101010101010101010101010101010101010101010101010101010101010101" + "key": "params", + "value": "01020304" } ] } @@ -125,7 +176,7 @@ "wasm": { "execute": { "contract_addr": "gateway", - "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDQwMTAyMDMwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn19", + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDQwMTAyMDMwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn19", "funds": [] } } @@ -149,18 +200,26 @@ }, { "key": "message_type", - "value": "DeployTokenManager" + "value": "InterchainTransfer" }, { "key": "token_id", + "value": "0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "key": "source_address", "value": "0101010101010101010101010101010101010101010101010101010101010101" }, { - "key": "token_manager_type", - "value": "MintBurn" + "key": "destination_address", + "value": "0202020202020202020202020202020202020202020202020202020202020202" }, { - "key": "params", + "key": "amount", + "value": "1" + }, + { + "key": "data", "value": "01020304" } ] diff --git a/contracts/interchain-token-service/tests/utils/execute.rs b/contracts/interchain-token-service/tests/utils/execute.rs index 77d1ba538..5bcca0cd8 100644 --- a/contracts/interchain-token-service/tests/utils/execute.rs +++ b/contracts/interchain-token-service/tests/utils/execute.rs @@ -8,12 +8,13 @@ use axelar_wasm_std::nonempty; use cosmwasm_std::testing::{mock_env, mock_info, MockApi, MockQuerier, MockStorage}; use cosmwasm_std::{ from_json, to_json_binary, Addr, DepsMut, HexBinary, MemoryStorage, OwnedDeps, Response, - WasmQuery, + Uint256, WasmQuery, }; -use interchain_token_service::contract; use interchain_token_service::msg::ExecuteMsg; +use interchain_token_service::{contract, HubMessage}; use router_api::{Address, ChainName, ChainNameRaw, CrossChainId}; +use super::{instantiate_contract, TestMessage}; use crate::utils::params; pub fn execute( @@ -34,6 +35,15 @@ pub fn execute( ) } +pub fn execute_hub_message( + deps: DepsMut, + cc_id: CrossChainId, + source_address: Address, + message: HubMessage, +) -> Result { + execute(deps, cc_id, source_address, message.abi_encode()) +} + pub fn register_its_contract( deps: DepsMut, chain: ChainNameRaw, @@ -115,3 +125,47 @@ pub fn make_deps() -> OwnedDeps>, + chain: ChainNameRaw, + its_contract: Address, +) { + register_its_contract(deps.as_mut(), chain.clone(), its_contract).unwrap(); + set_chain_config( + deps.as_mut(), + chain, + Uint256::MAX.try_into().unwrap(), + u8::MAX, + ) + .unwrap(); +} + +pub fn setup() -> ( + OwnedDeps>, + TestMessage, +) { + let mut deps = make_deps(); + instantiate_contract(deps.as_mut()).unwrap(); + + let TestMessage { + source_its_chain, + source_its_contract, + destination_its_chain, + destination_its_contract, + .. + } = TestMessage::dummy(); + + register_chain( + &mut deps, + source_its_chain.clone(), + source_its_contract.clone(), + ); + register_chain( + &mut deps, + destination_its_chain.clone(), + destination_its_contract.clone(), + ); + + (deps, TestMessage::dummy()) +} diff --git a/contracts/interchain-token-service/tests/utils/messages.rs b/contracts/interchain-token-service/tests/utils/messages.rs index 041cfbb01..48e1fba1d 100644 --- a/contracts/interchain-token-service/tests/utils/messages.rs +++ b/contracts/interchain-token-service/tests/utils/messages.rs @@ -1,15 +1,15 @@ -use cosmwasm_std::HexBinary; -use interchain_token_service::{HubMessage, Message, TokenId}; +use interchain_token_service::{DeployInterchainToken, HubMessage, Message, TokenId}; use router_api::{Address, ChainNameRaw, CrossChainId}; pub fn dummy_message() -> Message { - Message::InterchainTransfer { + DeployInterchainToken { token_id: TokenId::new([2; 32]), - source_address: HexBinary::from_hex("1234").unwrap().try_into().unwrap(), - destination_address: HexBinary::from_hex("5678").unwrap().try_into().unwrap(), - amount: 1000u64.try_into().unwrap(), - data: Some(HexBinary::from_hex("abcd").unwrap().try_into().unwrap()), + name: "Test".try_into().unwrap(), + symbol: "TST".try_into().unwrap(), + decimals: 18, + minter: None, } + .into() } pub struct TestMessage { diff --git a/contracts/interchain-token-service/tests/utils/query.rs b/contracts/interchain-token-service/tests/utils/query.rs index 19739a010..1ea3e1465 100644 --- a/contracts/interchain-token-service/tests/utils/query.rs +++ b/contracts/interchain-token-service/tests/utils/query.rs @@ -5,6 +5,7 @@ use cosmwasm_std::testing::mock_env; use cosmwasm_std::{from_json, Deps}; use interchain_token_service::contract::query; use interchain_token_service::msg::QueryMsg; +use interchain_token_service::{TokenConfig, TokenId, TokenInstance}; use router_api::{Address, ChainNameRaw}; pub fn query_its_contract( @@ -21,3 +22,24 @@ pub fn query_all_its_contracts( let bin = query(deps, mock_env(), QueryMsg::AllItsContracts)?; Ok(from_json(bin)?) } + +pub fn query_token_instance( + deps: Deps, + chain: ChainNameRaw, + token_id: TokenId, +) -> Result, ContractError> { + let bin = query( + deps, + mock_env(), + QueryMsg::TokenInstance { chain, token_id }, + )?; + Ok(from_json(bin)?) +} + +pub fn query_token_config( + deps: Deps, + token_id: TokenId, +) -> Result, ContractError> { + let bin = query(deps, mock_env(), QueryMsg::TokenConfig { token_id })?; + Ok(from_json(bin)?) +} diff --git a/packages/router-api/src/primitives.rs b/packages/router-api/src/primitives.rs index 315450baa..57712bf48 100644 --- a/packages/router-api/src/primitives.rs +++ b/packages/router-api/src/primitives.rs @@ -314,6 +314,13 @@ impl ChainNameRaw { /// Maximum length of a chain name (in bytes). /// This MUST NOT be changed without a corresponding change to the ChainName validation in axelar-core. const MAX_LEN: usize = 20; + + /// Special care must be taken when using this function. Normalization means a loss of information + /// and can lead to the chain not being found in the database. This function should only be used if absolutely necessary. + pub fn normalize(&self) -> ChainName { + // assert: if ChainNameRaw is valid, ChainName is also valid, just lower-cased + ChainName::try_from(self.as_ref()).expect("invalid chain name") + } } impl FromStr for ChainNameRaw { From afe253d11bdfeb19cf856e17d9bd3955266f1abf Mon Sep 17 00:00:00 2001 From: Sammy Date: Mon, 4 Nov 2024 10:34:36 -0500 Subject: [PATCH 07/21] feat(interchain-token-service): calculate and save token decimals on token deployment (#674) --- .../src/contract/execute.rs | 239 +++++++++++++----- .../src/primitives.rs | 4 +- .../interchain-token-service/src/state.rs | 23 +- .../interchain-token-service/tests/execute.rs | 188 ++++++++++++-- ...cimals_when_max_uints_are_different.golden | 67 +++++ ...ecimals_when_max_uints_are_the_same.golden | 67 +++++ 6 files changed, 498 insertions(+), 90 deletions(-) create mode 100644 contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_different.golden create mode 100644 contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_the_same.golden diff --git a/contracts/interchain-token-service/src/contract/execute.rs b/contracts/interchain-token-service/src/contract/execute.rs index a5dcd6a46..f35d677ba 100644 --- a/contracts/interchain-token-service/src/contract/execute.rs +++ b/contracts/interchain-token-service/src/contract/execute.rs @@ -51,6 +51,15 @@ pub enum Error { origin_chain: ChainNameRaw, chain: ChainNameRaw, }, + #[error( + "token {token_id} deployed from chain {chain} with different decimals than original deployment" + )] + TokenDeployedDecimalsMismatch { + token_id: TokenId, + chain: ChainNameRaw, + expected: Option, + actual: Option, + }, #[error("token supply invariant violated for token {token_id} on chain {chain}")] TokenSupplyInvariantViolated { token_id: TokenId, @@ -93,11 +102,11 @@ fn execute_message_on_hub( let destination_address = load_its_contract(deps.storage, &destination_chain) .change_context_lazy(|| Error::UnknownChain(destination_chain.clone()))?; - apply_to_hub( + let message = apply_to_hub( deps.storage, cc_id.source_chain.clone(), destination_chain.clone(), - &message, + message, )?; let destination_payload = HubMessage::ReceiveFromHub { @@ -127,36 +136,29 @@ fn apply_to_hub( storage: &mut dyn Storage, source_chain: ChainNameRaw, destination_chain: ChainNameRaw, - message: &Message, -) -> Result<(), Error> { + message: Message, +) -> Result { ensure_chain_not_frozen(storage, &source_chain)?; ensure_chain_not_frozen(storage, &destination_chain)?; match message { Message::InterchainTransfer(transfer) => { - apply_transfer(storage, source_chain, destination_chain, transfer)?; + apply_transfer(storage, source_chain, destination_chain, &transfer) + .map(|_| Message::InterchainTransfer(transfer))? } Message::DeployInterchainToken(deploy_token) => { - apply_token_deployment( - storage, - source_chain, - destination_chain, - deploy_token.token_id.clone(), - &deploy_token.deployment_type(), - )?; - } - Message::DeployTokenManager(deploy_manager) => { - apply_token_deployment( - storage, - source_chain, - destination_chain, - deploy_manager.token_id.clone(), - &deploy_manager.deployment_type(), - )?; + apply_token_deployment(storage, &source_chain, &destination_chain, deploy_token) + .map(Message::DeployInterchainToken)? } + Message::DeployTokenManager(deploy_manager) => apply_token_manager_deployment( + storage, + &source_chain, + &destination_chain, + deploy_manager, + ) + .map(Message::DeployTokenManager)?, } - - Ok(()) + .then(Result::Ok) } fn ensure_chain_not_frozen(storage: &dyn Storage, chain: &ChainNameRaw) -> Result<(), Error> { @@ -255,6 +257,37 @@ pub fn set_chain_config( } } +/// Calculates the destination token decimals. +/// +/// The destination chain's token decimals are calculated and saved as following: +/// 1) If the source chain's `max_uint` is less than or equal to the destination chain's `max_uint`, +/// the source chain's token decimals are used. +/// 2) Otherwise, the minimum of the source chain's token decimals and the source chain's +/// `max_target_decimals` is used. +fn destination_token_decimals( + storage: &mut dyn Storage, + source_chain: &ChainNameRaw, + destination_chain: &ChainNameRaw, + source_chain_decimals: u8, +) -> Result { + let source_chain_config = + state::load_chain_config(storage, source_chain).change_context(Error::State)?; + let destination_chain_config = + state::load_chain_config(storage, destination_chain).change_context(Error::State)?; + + if source_chain_config + .max_uint + .le(&destination_chain_config.max_uint) + { + source_chain_decimals + } else { + source_chain_config + .max_target_decimals + .min(source_chain_decimals) + } + .then(Result::Ok) +} + fn apply_transfer( storage: &mut dyn Storage, source_chain: ChainNameRaw, @@ -271,23 +304,18 @@ fn subtract_amount_from_source( transfer: &InterchainTransfer, ) -> Result<(), Error> { let mut source_instance = - try_load_token_instance(storage, source_chain.clone(), transfer.token_id.clone())?; + try_load_token_instance(storage, source_chain.clone(), transfer.token_id)?; source_instance.supply = source_instance .supply .checked_sub(transfer.amount) .change_context_lazy(|| Error::TokenSupplyInvariantViolated { - token_id: transfer.token_id.clone(), + token_id: transfer.token_id, chain: source_chain.clone(), })?; - state::save_token_instance( - storage, - source_chain, - transfer.token_id.clone(), - &source_instance, - ) - .change_context(Error::State) + state::save_token_instance(storage, source_chain, transfer.token_id, &source_instance) + .change_context(Error::State) } fn add_amount_to_destination( @@ -295,24 +323,21 @@ fn add_amount_to_destination( destination_chain: ChainNameRaw, transfer: &InterchainTransfer, ) -> Result<(), Error> { - let mut destination_instance = try_load_token_instance( - storage, - destination_chain.clone(), - transfer.token_id.clone(), - )?; + let mut destination_instance = + try_load_token_instance(storage, destination_chain.clone(), transfer.token_id)?; destination_instance.supply = destination_instance .supply .checked_add(transfer.amount) .change_context_lazy(|| Error::TokenSupplyInvariantViolated { - token_id: transfer.token_id.clone(), + token_id: transfer.token_id, chain: destination_chain.clone(), })?; state::save_token_instance( storage, destination_chain, - transfer.token_id.clone(), + transfer.token_id, &destination_instance, ) .change_context(Error::State) @@ -320,71 +345,149 @@ fn add_amount_to_destination( fn apply_token_deployment( storage: &mut dyn Storage, - source_chain: ChainNameRaw, - destination_chain: ChainNameRaw, + source_chain: &ChainNameRaw, + destination_chain: &ChainNameRaw, + deploy_token: DeployInterchainToken, +) -> Result { + let destination_token_decimals = destination_token_decimals( + storage, + source_chain, + destination_chain, + deploy_token.decimals, + )?; + + save_token_instances( + storage, + source_chain, + destination_chain, + Some(deploy_token.decimals), + Some(destination_token_decimals), + deploy_token.token_id, + &deploy_token.deployment_type(), + ) + .map(|_| DeployInterchainToken { + decimals: destination_token_decimals, + + ..deploy_token + }) +} + +fn apply_token_manager_deployment( + storage: &mut dyn Storage, + source_chain: &ChainNameRaw, + destination_chain: &ChainNameRaw, + deploy_token_manager: DeployTokenManager, +) -> Result { + save_token_instances( + storage, + source_chain, + destination_chain, + None, + None, + deploy_token_manager.token_id, + &deploy_token_manager.deployment_type(), + ) + .map(|_| deploy_token_manager) +} + +fn save_token_instances( + storage: &mut dyn Storage, + source_chain: &ChainNameRaw, + destination_chain: &ChainNameRaw, + source_token_decimals: Option, + destination_token_decimals: Option, token_id: TokenId, deployment_type: &TokenDeploymentType, ) -> Result<(), Error> { - ensure_token_not_deployed_on_destination(storage, token_id.clone(), destination_chain.clone())?; + ensure_token_not_deployed_on_destination(storage, token_id, destination_chain.clone())?; let token_config = state::may_load_token_config(storage, &token_id).change_context(Error::State)?; if let Some(TokenConfig { origin_chain, .. }) = token_config { - ensure_origin_matches_source_chain(source_chain, origin_chain, token_id.clone())?; + ensure_matching_original_deployment( + origin_chain, + source_chain, + token_id, + storage, + source_token_decimals, + )?; } else { - initialize_token_on_origin(storage, source_chain, token_id.clone())?; + initialize_token_on_origin(storage, source_chain, token_id, source_token_decimals)?; } - let destination_instance = TokenInstance::new(deployment_type); - - state::save_token_instance(storage, destination_chain, token_id, &destination_instance) - .change_context(Error::State) -} + let destination_instance = TokenInstance::new(deployment_type, destination_token_decimals); -fn try_load_token_instance( - storage: &mut dyn Storage, - chain: ChainNameRaw, - token_id: TokenId, -) -> Result { - state::may_load_token_instance(storage, chain.clone(), token_id.clone()) - .change_context(Error::State)? - .ok_or(report!(Error::TokenNotDeployed { - token_id: token_id.clone(), - chain, - })) + state::save_token_instance( + storage, + destination_chain.clone(), + token_id, + &destination_instance, + ) + .change_context(Error::State) } -fn ensure_origin_matches_source_chain( - source_chain: ChainNameRaw, +fn ensure_matching_original_deployment( origin_chain: ChainNameRaw, + source_chain: &ChainNameRaw, token_id: TokenId, + storage: &mut dyn Storage, + source_token_decimals: Option, ) -> Result<(), Error> { ensure!( - origin_chain == source_chain, + origin_chain == *source_chain, Error::TokenDeployedFromNonOriginChain { token_id, - origin_chain, + origin_chain: origin_chain.to_owned(), + chain: source_chain.clone(), + } + ); + + let token_instance = state::may_load_token_instance(storage, origin_chain.clone(), token_id) + .change_context(Error::State)? + .ok_or(report!(Error::TokenNotDeployed { + token_id, + chain: origin_chain.clone() + }))?; + ensure!( + token_instance.decimals == source_token_decimals, + Error::TokenDeployedDecimalsMismatch { + token_id, chain: source_chain.clone(), + expected: token_instance.decimals, + actual: source_token_decimals } ); Ok(()) } +fn try_load_token_instance( + storage: &mut dyn Storage, + chain: ChainNameRaw, + token_id: TokenId, +) -> Result { + state::may_load_token_instance(storage, chain.clone(), token_id) + .change_context(Error::State)? + .ok_or(report!(Error::TokenNotDeployed { token_id, chain })) +} + fn initialize_token_on_origin( storage: &mut dyn Storage, - source_chain: ChainNameRaw, + source_chain: &ChainNameRaw, token_id: TokenId, + decimals: Option, ) -> Result<(), Error> { // Token is being deployed for the first time let token_config = TokenConfig { origin_chain: source_chain.clone(), }; - let instance = TokenInstance::new_on_origin(); + let instance = TokenInstance::new_on_origin(decimals); state::save_token_config(storage, &token_id, &token_config) - .and_then(|_| state::save_token_instance(storage, source_chain, token_id, &instance)) + .and_then(|_| { + state::save_token_instance(storage, source_chain.clone(), token_id, &instance) + }) .change_context(Error::State)?; Ok(()) } @@ -396,7 +499,7 @@ fn ensure_token_not_deployed_on_destination( destination_chain: ChainNameRaw, ) -> Result<(), Error> { let token_instance = - state::may_load_token_instance(storage, destination_chain.clone(), token_id.clone()) + state::may_load_token_instance(storage, destination_chain.clone(), token_id) .change_context(Error::State)?; ensure!( diff --git a/contracts/interchain-token-service/src/primitives.rs b/contracts/interchain-token-service/src/primitives.rs index 4e5dcdeb3..ef3780028 100644 --- a/contracts/interchain-token-service/src/primitives.rs +++ b/contracts/interchain-token-service/src/primitives.rs @@ -8,7 +8,7 @@ use strum::FromRepr; /// A unique 32-byte identifier for linked cross-chain tokens across ITS contracts. #[cw_serde] -#[derive(Eq, Hash)] +#[derive(Eq, Hash, Copy)] pub struct TokenId( #[serde(with = "axelar_wasm_std::hex")] #[schemars(with = "String")] @@ -144,7 +144,7 @@ impl Message { match self { Message::InterchainTransfer(InterchainTransfer { token_id, .. }) | Message::DeployInterchainToken(DeployInterchainToken { token_id, .. }) - | Message::DeployTokenManager(DeployTokenManager { token_id, .. }) => token_id.clone(), + | Message::DeployTokenManager(DeployTokenManager { token_id, .. }) => *token_id, } } } diff --git a/contracts/interchain-token-service/src/state.rs b/contracts/interchain-token-service/src/state.rs index 96a3eb1e6..40fddaa49 100644 --- a/contracts/interchain-token-service/src/state.rs +++ b/contracts/interchain-token-service/src/state.rs @@ -19,6 +19,8 @@ pub enum Error { ItsContractAlreadyRegistered(ChainNameRaw), #[error("chain not found {0}")] ChainNotFound(ChainNameRaw), + #[error("chain config for chain {0} not found")] + ChainConfigNotFound(ChainNameRaw), // This is a generic error to use when cw_storage_plus returns an error that is unexpected and // should never happen, such as an error encountered when saving data. #[error("storage error")] @@ -32,8 +34,8 @@ pub struct Config { #[cw_serde] pub struct ChainConfig { - max_uint: nonempty::Uint256, - max_target_decimals: u8, + pub max_uint: nonempty::Uint256, + pub max_target_decimals: u8, frozen: bool, } @@ -71,22 +73,24 @@ impl TokenSupply { #[cw_serde] pub struct TokenInstance { pub supply: TokenSupply, + pub decimals: Option, } impl TokenInstance { - pub fn new_on_origin() -> Self { + pub fn new_on_origin(decimals: Option) -> Self { Self { supply: TokenSupply::Untracked, + decimals, } } - pub fn new(deployment_type: &TokenDeploymentType) -> Self { + pub fn new(deployment_type: &TokenDeploymentType, decimals: Option) -> Self { let supply = match deployment_type { TokenDeploymentType::Trustless => TokenSupply::Tracked(Uint256::zero()), _ => TokenSupply::Untracked, }; - Self { supply } + Self { supply, decimals } } } @@ -128,6 +132,15 @@ pub fn may_load_chain_config( .change_context(Error::Storage) } +pub fn load_chain_config( + storage: &dyn Storage, + chain: &ChainNameRaw, +) -> Result { + may_load_chain_config(storage, chain) + .change_context(Error::Storage)? + .ok_or_else(|| report!(Error::ChainConfigNotFound(chain.to_owned()))) +} + pub fn save_chain_config( storage: &mut dyn Storage, chain: &ChainNameRaw, diff --git a/contracts/interchain-token-service/tests/execute.rs b/contracts/interchain-token-service/tests/execute.rs index cbd202c81..3fd9ff549 100644 --- a/contracts/interchain-token-service/tests/execute.rs +++ b/contracts/interchain-token-service/tests/execute.rs @@ -14,7 +14,8 @@ use interchain_token_service::{ TokenManagerType, TokenSupply, }; use router_api::{Address, ChainName, ChainNameRaw, CrossChainId}; -use utils::{params, TestMessage}; +use serde_json::json; +use utils::{instantiate_contract, make_deps, params, TestMessage}; mod utils; @@ -173,6 +174,163 @@ fn execute_hub_message_succeeds() { goldie::assert_json!(responses); } +#[test] +fn execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_different() +{ + let mut deps = make_deps(); + instantiate_contract(deps.as_mut()).unwrap(); + + let TestMessage { + router_message, + source_its_chain, + source_its_contract, + destination_its_chain, + destination_its_contract, + .. + } = TestMessage::dummy(); + + utils::register_its_contract( + deps.as_mut(), + source_its_chain.clone(), + source_its_contract.clone(), + ) + .unwrap(); + utils::register_its_contract( + deps.as_mut(), + destination_its_chain.clone(), + destination_its_contract.clone(), + ) + .unwrap(); + utils::set_chain_config( + deps.as_mut(), + source_its_chain.clone(), + Uint256::MAX.try_into().unwrap(), + 6, + ) + .unwrap(); + utils::set_chain_config( + deps.as_mut(), + destination_its_chain.clone(), + Uint256::from_u128(2) + .pow(64) + .strict_sub(Uint256::one()) + .try_into() + .unwrap(), + 6, + ) + .unwrap(); + + let token_id = TokenId::new([1; 32]); + let message = DeployInterchainToken { + token_id, + name: "Test".try_into().unwrap(), + symbol: "TST".try_into().unwrap(), + decimals: 18, + minter: None, + } + .into(); + let hub_message = HubMessage::SendToHub { + destination_chain: destination_its_chain.clone(), + message, + }; + let response = assert_ok!(utils::execute_hub_message( + deps.as_mut(), + router_message.cc_id.clone(), + source_its_contract.clone(), + hub_message.clone(), + )); + let source_token_instance = assert_ok!(utils::query_token_instance( + deps.as_ref(), + source_its_chain.clone(), + token_id + )); + let destination_token_instance = assert_ok!(utils::query_token_instance( + deps.as_ref(), + destination_its_chain.clone(), + token_id + )); + + goldie::assert_json!( + json!({ "response": response, "source_token_instance": source_token_instance, "destination_token_instance": destination_token_instance }) + ); +} + +#[test] +fn execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_the_same() { + let mut deps = make_deps(); + instantiate_contract(deps.as_mut()).unwrap(); + + let TestMessage { + router_message, + source_its_chain, + source_its_contract, + destination_its_chain, + destination_its_contract, + .. + } = TestMessage::dummy(); + + utils::register_its_contract( + deps.as_mut(), + source_its_chain.clone(), + source_its_contract.clone(), + ) + .unwrap(); + utils::register_its_contract( + deps.as_mut(), + destination_its_chain.clone(), + destination_its_contract.clone(), + ) + .unwrap(); + utils::set_chain_config( + deps.as_mut(), + source_its_chain.clone(), + Uint256::MAX.try_into().unwrap(), + 6, + ) + .unwrap(); + utils::set_chain_config( + deps.as_mut(), + destination_its_chain.clone(), + Uint256::MAX.try_into().unwrap(), + 6, + ) + .unwrap(); + + let token_id = TokenId::new([1; 32]); + let message = DeployInterchainToken { + token_id, + name: "Test".try_into().unwrap(), + symbol: "TST".try_into().unwrap(), + decimals: 18, + minter: None, + } + .into(); + let hub_message = HubMessage::SendToHub { + destination_chain: destination_its_chain.clone(), + message, + }; + let response = assert_ok!(utils::execute_hub_message( + deps.as_mut(), + router_message.cc_id.clone(), + source_its_contract.clone(), + hub_message.clone(), + )); + let source_token_instance = assert_ok!(utils::query_token_instance( + deps.as_ref(), + source_its_chain.clone(), + token_id + )); + let destination_token_instance = assert_ok!(utils::query_token_instance( + deps.as_ref(), + destination_its_chain.clone(), + token_id + )); + + goldie::assert_json!( + json!({ "response": response, "source_token_instance": source_token_instance, "destination_token_instance": destination_token_instance }) + ); +} + #[test] fn execute_its_when_not_gateway_sender_fails() { let mut deps = mock_dependencies(); @@ -580,7 +738,7 @@ fn deploy_interchain_token_tracks_supply() { let msg = HubMessage::SendToHub { destination_chain: destination_its_chain.clone(), message: InterchainTransfer { - token_id: token_id.clone(), + token_id, source_address: HexBinary::from([1; 32]).try_into().unwrap(), destination_address: HexBinary::from([2; 32]).try_into().unwrap(), amount, @@ -599,7 +757,7 @@ fn deploy_interchain_token_tracks_supply() { assert_ok!(utils::query_token_instance( deps.as_ref(), source_its_chain.clone(), - token_id.clone() + token_id )) .unwrap() .supply, @@ -609,7 +767,7 @@ fn deploy_interchain_token_tracks_supply() { assert_ok!(utils::query_token_instance( deps.as_ref(), destination_its_chain.clone(), - token_id.clone() + token_id )) .unwrap() .supply, @@ -620,7 +778,7 @@ fn deploy_interchain_token_tracks_supply() { let msg = HubMessage::SendToHub { destination_chain: source_its_chain.clone(), message: InterchainTransfer { - token_id: token_id.clone(), + token_id, source_address: HexBinary::from([1; 32]).try_into().unwrap(), destination_address: HexBinary::from([2; 32]).try_into().unwrap(), amount, @@ -643,7 +801,7 @@ fn deploy_interchain_token_tracks_supply() { assert_ok!(utils::query_token_instance( deps.as_ref(), source_its_chain.clone(), - token_id.clone() + token_id )) .unwrap() .supply, @@ -653,7 +811,7 @@ fn deploy_interchain_token_tracks_supply() { assert_ok!(utils::query_token_instance( deps.as_ref(), destination_its_chain, - token_id.clone() + token_id )) .unwrap() .supply, @@ -681,7 +839,7 @@ fn deploy_interchain_token_with_minter_does_not_track_supply() { let msg = HubMessage::SendToHub { destination_chain: destination_its_chain.clone(), message: DeployInterchainToken { - token_id: token_id.clone(), + token_id, name: "Test".try_into().unwrap(), symbol: "TST".try_into().unwrap(), decimals: 18, @@ -700,7 +858,7 @@ fn deploy_interchain_token_with_minter_does_not_track_supply() { assert_ok!(utils::query_token_instance( deps.as_ref(), chain.clone(), - token_id.clone() + token_id )) .unwrap() .supply, @@ -711,7 +869,7 @@ fn deploy_interchain_token_with_minter_does_not_track_supply() { let msg = HubMessage::SendToHub { destination_chain: destination_its_chain.clone(), message: InterchainTransfer { - token_id: token_id.clone(), + token_id, source_address: HexBinary::from([1; 32]).try_into().unwrap(), destination_address: HexBinary::from([2; 32]).try_into().unwrap(), amount, @@ -730,7 +888,7 @@ fn deploy_interchain_token_with_minter_does_not_track_supply() { let msg = HubMessage::SendToHub { destination_chain: source_its_chain.clone(), message: InterchainTransfer { - token_id: token_id.clone(), + token_id, source_address: HexBinary::from([1; 32]).try_into().unwrap(), destination_address: HexBinary::from([2; 32]).try_into().unwrap(), amount: amount.strict_add(Uint256::one()).try_into().unwrap(), @@ -754,7 +912,7 @@ fn deploy_interchain_token_with_minter_does_not_track_supply() { assert_ok!(utils::query_token_instance( deps.as_ref(), chain.clone(), - token_id.clone() + token_id )) .unwrap() .supply, @@ -790,7 +948,7 @@ fn interchain_transfer_exceeds_supply_fails() { let msg = HubMessage::SendToHub { destination_chain: source_its_chain.clone(), message: InterchainTransfer { - token_id: token_id.clone(), + token_id, source_address: HexBinary::from([1; 32]).try_into().unwrap(), destination_address: HexBinary::from([2; 32]).try_into().unwrap(), amount: 1u64.try_into().unwrap(), @@ -816,7 +974,7 @@ fn interchain_transfer_exceeds_supply_fails() { let msg = HubMessage::SendToHub { destination_chain: destination_its_chain.clone(), message: InterchainTransfer { - token_id: token_id.clone(), + token_id, source_address: HexBinary::from([1; 32]).try_into().unwrap(), destination_address: HexBinary::from([2; 32]).try_into().unwrap(), amount, @@ -834,7 +992,7 @@ fn interchain_transfer_exceeds_supply_fails() { let msg = HubMessage::SendToHub { destination_chain: source_its_chain.clone(), message: InterchainTransfer { - token_id: token_id.clone(), + token_id, source_address: HexBinary::from([1; 32]).try_into().unwrap(), destination_address: HexBinary::from([2; 32]).try_into().unwrap(), amount: amount.strict_add(Uint256::one()).try_into().unwrap(), diff --git a/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_different.golden b/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_different.golden new file mode 100644 index 000000000..530997071 --- /dev/null +++ b/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_different.golden @@ -0,0 +1,67 @@ +{ + "destination_token_instance": { + "decimals": 6, + "supply": { + "tracked": "0" + } + }, + "response": { + "attributes": [], + "data": null, + "events": [ + { + "attributes": [ + { + "key": "cc_id", + "value": "source-its-chain_message-id" + }, + { + "key": "destination_chain", + "value": "dest-its-chain" + }, + { + "key": "message_type", + "value": "DeployInterchainToken" + }, + { + "key": "token_id", + "value": "0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "key": "name", + "value": "Test" + }, + { + "key": "symbol", + "value": "TST" + }, + { + "key": "decimals", + "value": "6" + } + ], + "type": "message_received" + } + ], + "messages": [ + { + "gas_limit": null, + "id": 0, + "msg": { + "wasm": { + "execute": { + "contract_addr": "gateway", + "funds": [], + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDU0NjU3Mzc0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNTQ1MzU0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifX0=" + } + } + }, + "reply_on": "never" + } + ] + }, + "source_token_instance": { + "decimals": 18, + "supply": "untracked" + } +} \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_the_same.golden b/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_the_same.golden new file mode 100644 index 000000000..43c0ceafa --- /dev/null +++ b/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_the_same.golden @@ -0,0 +1,67 @@ +{ + "destination_token_instance": { + "decimals": 18, + "supply": { + "tracked": "0" + } + }, + "response": { + "attributes": [], + "data": null, + "events": [ + { + "attributes": [ + { + "key": "cc_id", + "value": "source-its-chain_message-id" + }, + { + "key": "destination_chain", + "value": "dest-its-chain" + }, + { + "key": "message_type", + "value": "DeployInterchainToken" + }, + { + "key": "token_id", + "value": "0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "key": "name", + "value": "Test" + }, + { + "key": "symbol", + "value": "TST" + }, + { + "key": "decimals", + "value": "18" + } + ], + "type": "message_received" + } + ], + "messages": [ + { + "gas_limit": null, + "id": 0, + "msg": { + "wasm": { + "execute": { + "contract_addr": "gateway", + "funds": [], + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDU0NjU3Mzc0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNTQ1MzU0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifX0=" + } + } + }, + "reply_on": "never" + } + ] + }, + "source_token_instance": { + "decimals": 18, + "supply": "untracked" + } +} \ No newline at end of file From d1b9ec615fcd977e1f10f3856ad649f16f3e508f Mon Sep 17 00:00:00 2001 From: Sammy Date: Mon, 4 Nov 2024 10:57:51 -0500 Subject: [PATCH 08/21] feat(interchain-token-service): apply scaling factor on token transfer based on token decimals (#677) --- .../src/contract/execute.rs | 115 ++++++++++- .../interchain-token-service/src/state.rs | 2 +- .../interchain-token-service/tests/execute.rs | 182 +++++++++++------- ..._when_source_decimals_are_different.golden | 112 +++++++++++ .../tests/utils/execute.rs | 46 +++++ packages/axelar-wasm-std/src/nonempty/uint.rs | 6 + 6 files changed, 382 insertions(+), 81 deletions(-) create mode 100644 contracts/interchain-token-service/tests/testdata/execute_message_interchain_transfer_should_scale_the_amount_when_source_decimals_are_different.golden diff --git a/contracts/interchain-token-service/src/contract/execute.rs b/contracts/interchain-token-service/src/contract/execute.rs index f35d677ba..9178a8f85 100644 --- a/contracts/interchain-token-service/src/contract/execute.rs +++ b/contracts/interchain-token-service/src/contract/execute.rs @@ -1,5 +1,5 @@ use axelar_wasm_std::{killswitch, nonempty, FnExt, IntoContractError}; -use cosmwasm_std::{DepsMut, HexBinary, QuerierWrapper, Response, Storage}; +use cosmwasm_std::{DepsMut, HexBinary, QuerierWrapper, Response, Storage, Uint256}; use error_stack::{bail, ensure, report, Result, ResultExt}; use router_api::{Address, ChainNameRaw, CrossChainId}; @@ -31,6 +31,14 @@ pub enum Error { ExecutionDisabled, #[error("chain {0} is frozen")] ChainFrozen(ChainNameRaw), + #[error( + "invalid transfer amount {amount} from chain {source_chain} to chain {destination_chain}" + )] + InvalidTransferAmount { + source_chain: ChainNameRaw, + destination_chain: ChainNameRaw, + amount: nonempty::Uint256, + }, #[error("state error")] State, #[error("chain config for {0} already set")] @@ -144,7 +152,7 @@ fn apply_to_hub( match message { Message::InterchainTransfer(transfer) => { apply_transfer(storage, source_chain, destination_chain, &transfer) - .map(|_| Message::InterchainTransfer(transfer))? + .map(Message::InterchainTransfer)? } Message::DeployInterchainToken(deploy_token) => { apply_token_deployment(storage, &source_chain, &destination_chain, deploy_token) @@ -257,6 +265,83 @@ pub fn set_chain_config( } } +/// Calculates the destination on token transfer amount. +/// +/// The amount is calculated based on the token decimals on the source and destination chains. +/// The calculation is done as following: +/// 1) `destination_amount` = `source_amount` * 10 ^ (`destination_chain_decimals` - `source_chain_decimals`) +/// 3) If new_amount is greater than the destination chain's `max_uint`, the translation +/// fails. +/// 4) If new_amount is zero, the translation fails. +fn destination_amount( + storage: &dyn Storage, + source_chain: &ChainNameRaw, + destination_chain: &ChainNameRaw, + token_id: TokenId, + source_amount: nonempty::Uint256, +) -> Result { + let source_token = try_load_token_instance(storage, source_chain.clone(), token_id)?; + let destination_token = try_load_token_instance(storage, destination_chain.clone(), token_id)?; + let (source_decimals, destination_decimals) = + match (source_token.decimals, destination_token.decimals) { + (Some(source_decimals), Some(destination_decimals)) + if source_decimals == destination_decimals => + { + return Ok(source_amount) + } + (Some(source_decimals), Some(destination_decimals)) => { + (source_decimals, destination_decimals) + } + (None, None) => return Ok(source_amount), + _ => unreachable!( + "decimals should be set in both the source and destination, or set in neither" + ), // This should never happen + }; + let destination_max_uint = state::load_chain_config(storage, destination_chain) + .change_context(Error::State)? + .max_uint; + + // It's intentionally written in this way since the end result may still be fine even if + // 1) amount * (10 ^ (dest_chain_decimals)) overflows + // 2) amount / (10 ^ (src_chain_decimals)) is zero + let scaling_factor = Uint256::from_u128(10) + .checked_pow(source_decimals.abs_diff(destination_decimals).into()) + .change_context_lazy(|| Error::InvalidTransferAmount { + source_chain: source_chain.to_owned(), + destination_chain: destination_chain.to_owned(), + amount: source_amount, + })?; + let destination_amount = if source_decimals > destination_decimals { + source_amount + .checked_div(scaling_factor) + .expect("scaling_factor must be non-zero") + } else { + source_amount + .checked_mul(scaling_factor) + .change_context_lazy(|| Error::InvalidTransferAmount { + source_chain: source_chain.to_owned(), + destination_chain: destination_chain.to_owned(), + amount: source_amount, + })? + }; + + if destination_amount.gt(&destination_max_uint) { + bail!(Error::InvalidTransferAmount { + source_chain: source_chain.to_owned(), + destination_chain: destination_chain.to_owned(), + amount: source_amount, + }) + } + + nonempty::Uint256::try_from(destination_amount).change_context_lazy(|| { + Error::InvalidTransferAmount { + source_chain: source_chain.to_owned(), + destination_chain: destination_chain.to_owned(), + amount: source_amount, + } + }) +} + /// Calculates the destination token decimals. /// /// The destination chain's token decimals are calculated and saved as following: @@ -293,9 +378,25 @@ fn apply_transfer( source_chain: ChainNameRaw, destination_chain: ChainNameRaw, transfer: &InterchainTransfer, -) -> Result<(), Error> { +) -> Result { + let destination_amount = destination_amount( + storage, + &source_chain, + &destination_chain, + transfer.token_id, + transfer.amount, + )?; + subtract_amount_from_source(storage, source_chain, transfer)?; - add_amount_to_destination(storage, destination_chain, transfer) + + let destination_transfer = InterchainTransfer { + amount: destination_amount, + + ..transfer.clone() + }; + add_amount_to_destination(storage, destination_chain, &destination_transfer)?; + + Ok(destination_transfer) } fn subtract_amount_from_source( @@ -406,10 +507,10 @@ fn save_token_instances( if let Some(TokenConfig { origin_chain, .. }) = token_config { ensure_matching_original_deployment( + storage, origin_chain, source_chain, token_id, - storage, source_token_decimals, )?; } else { @@ -428,10 +529,10 @@ fn save_token_instances( } fn ensure_matching_original_deployment( + storage: &dyn Storage, origin_chain: ChainNameRaw, source_chain: &ChainNameRaw, token_id: TokenId, - storage: &mut dyn Storage, source_token_decimals: Option, ) -> Result<(), Error> { ensure!( @@ -463,7 +564,7 @@ fn ensure_matching_original_deployment( } fn try_load_token_instance( - storage: &mut dyn Storage, + storage: &dyn Storage, chain: ChainNameRaw, token_id: TokenId, ) -> Result { diff --git a/contracts/interchain-token-service/src/state.rs b/contracts/interchain-token-service/src/state.rs index 40fddaa49..0ded455cf 100644 --- a/contracts/interchain-token-service/src/state.rs +++ b/contracts/interchain-token-service/src/state.rs @@ -138,7 +138,7 @@ pub fn load_chain_config( ) -> Result { may_load_chain_config(storage, chain) .change_context(Error::Storage)? - .ok_or_else(|| report!(Error::ChainConfigNotFound(chain.to_owned()))) + .ok_or_else(|| report!(Error::ChainNotFound(chain.to_owned()))) } pub fn save_chain_config( diff --git a/contracts/interchain-token-service/tests/execute.rs b/contracts/interchain-token-service/tests/execute.rs index 3fd9ff549..3ee38fde1 100644 --- a/contracts/interchain-token-service/tests/execute.rs +++ b/contracts/interchain-token-service/tests/execute.rs @@ -15,7 +15,7 @@ use interchain_token_service::{ }; use router_api::{Address, ChainName, ChainNameRaw, CrossChainId}; use serde_json::json; -use utils::{instantiate_contract, make_deps, params, TestMessage}; +use utils::{params, TestMessage}; mod utils; @@ -175,50 +175,109 @@ fn execute_hub_message_succeeds() { } #[test] -fn execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_different() +fn execute_message_interchain_transfer_should_scale_the_amount_when_source_decimals_are_different() { - let mut deps = make_deps(); - instantiate_contract(deps.as_mut()).unwrap(); - - let TestMessage { - router_message, - source_its_chain, - source_its_contract, - destination_its_chain, - destination_its_contract, - .. - } = TestMessage::dummy(); - - utils::register_its_contract( + let ( + mut deps, + TestMessage { + router_message, + source_its_contract, + source_its_chain, + destination_its_chain, + destination_its_contract, + .. + }, + ) = utils::setup_with_chain_configs( + Uint256::MAX.try_into().unwrap(), + 6, + u64::MAX.try_into().unwrap(), + 6, + ); + let token_id = TokenId::new([1; 32]); + let deploy_token = DeployInterchainToken { + token_id, + name: "Test".try_into().unwrap(), + symbol: "TST".try_into().unwrap(), + decimals: 18, + minter: None, + } + .into(); + let hub_message = HubMessage::SendToHub { + destination_chain: destination_its_chain.clone(), + message: deploy_token, + }; + assert_ok!(utils::execute_hub_message( deps.as_mut(), - source_its_chain.clone(), + router_message.cc_id.clone(), source_its_contract.clone(), - ) - .unwrap(); - utils::register_its_contract( + hub_message, + )); + + // send from source to destination + let transfer = InterchainTransfer { + token_id, + source_address: HexBinary::from([1; 32]).try_into().unwrap(), + destination_address: HexBinary::from([2; 32]).try_into().unwrap(), + amount: Uint256::from_u128(1_000_000_000_000_000_000u128) + .try_into() + .unwrap(), + data: None, + }; + let hub_message = HubMessage::SendToHub { + destination_chain: destination_its_chain.clone(), + message: transfer.into(), + }; + let response_to_destination = assert_ok!(utils::execute_hub_message( deps.as_mut(), - destination_its_chain.clone(), - destination_its_contract.clone(), - ) - .unwrap(); - utils::set_chain_config( + router_message.cc_id.clone(), + source_its_contract, + hub_message, + )); + + // send back from destination to source + let transfer = InterchainTransfer { + token_id, + source_address: HexBinary::from([2; 32]).try_into().unwrap(), + destination_address: HexBinary::from([1; 32]).try_into().unwrap(), + amount: Uint256::from_u128(1_000_000u128).try_into().unwrap(), + data: None, + }; + let mut cc_id = router_message.cc_id.clone(); + cc_id.source_chain = destination_its_chain.clone(); + let hub_message = HubMessage::SendToHub { + destination_chain: source_its_chain, + message: transfer.into(), + }; + let response_to_source = assert_ok!(utils::execute_hub_message( deps.as_mut(), - source_its_chain.clone(), + cc_id, + destination_its_contract, + hub_message, + )); + + goldie::assert_json!( + json!({"response_to_destination": response_to_destination, "response_to_source": response_to_source}) + ); +} + +#[test] +fn execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_different() +{ + let ( + mut deps, + TestMessage { + router_message, + source_its_contract, + source_its_chain, + destination_its_chain, + .. + }, + ) = utils::setup_with_chain_configs( Uint256::MAX.try_into().unwrap(), 6, - ) - .unwrap(); - utils::set_chain_config( - deps.as_mut(), - destination_its_chain.clone(), - Uint256::from_u128(2) - .pow(64) - .strict_sub(Uint256::one()) - .try_into() - .unwrap(), + u64::MAX.try_into().unwrap(), 6, - ) - .unwrap(); + ); let token_id = TokenId::new([1; 32]); let message = DeployInterchainToken { @@ -237,7 +296,7 @@ fn execute_message_deploy_interchain_token_should_translate_decimals_when_max_ui deps.as_mut(), router_message.cc_id.clone(), source_its_contract.clone(), - hub_message.clone(), + hub_message, )); let source_token_instance = assert_ok!(utils::query_token_instance( deps.as_ref(), @@ -257,44 +316,21 @@ fn execute_message_deploy_interchain_token_should_translate_decimals_when_max_ui #[test] fn execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_the_same() { - let mut deps = make_deps(); - instantiate_contract(deps.as_mut()).unwrap(); - - let TestMessage { - router_message, - source_its_chain, - source_its_contract, - destination_its_chain, - destination_its_contract, - .. - } = TestMessage::dummy(); - - utils::register_its_contract( - deps.as_mut(), - source_its_chain.clone(), - source_its_contract.clone(), - ) - .unwrap(); - utils::register_its_contract( - deps.as_mut(), - destination_its_chain.clone(), - destination_its_contract.clone(), - ) - .unwrap(); - utils::set_chain_config( - deps.as_mut(), - source_its_chain.clone(), + let ( + mut deps, + TestMessage { + router_message, + source_its_chain, + source_its_contract, + destination_its_chain, + .. + }, + ) = utils::setup_with_chain_configs( Uint256::MAX.try_into().unwrap(), 6, - ) - .unwrap(); - utils::set_chain_config( - deps.as_mut(), - destination_its_chain.clone(), Uint256::MAX.try_into().unwrap(), 6, - ) - .unwrap(); + ); let token_id = TokenId::new([1; 32]); let message = DeployInterchainToken { @@ -313,7 +349,7 @@ fn execute_message_deploy_interchain_token_should_translate_decimals_when_max_ui deps.as_mut(), router_message.cc_id.clone(), source_its_contract.clone(), - hub_message.clone(), + hub_message, )); let source_token_instance = assert_ok!(utils::query_token_instance( deps.as_ref(), diff --git a/contracts/interchain-token-service/tests/testdata/execute_message_interchain_transfer_should_scale_the_amount_when_source_decimals_are_different.golden b/contracts/interchain-token-service/tests/testdata/execute_message_interchain_transfer_should_scale_the_amount_when_source_decimals_are_different.golden new file mode 100644 index 000000000..0a984fd44 --- /dev/null +++ b/contracts/interchain-token-service/tests/testdata/execute_message_interchain_transfer_should_scale_the_amount_when_source_decimals_are_different.golden @@ -0,0 +1,112 @@ +{ + "response_to_destination": { + "attributes": [], + "data": null, + "events": [ + { + "attributes": [ + { + "key": "cc_id", + "value": "source-its-chain_message-id" + }, + { + "key": "destination_chain", + "value": "dest-its-chain" + }, + { + "key": "message_type", + "value": "InterchainTransfer" + }, + { + "key": "token_id", + "value": "0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "key": "source_address", + "value": "0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "key": "destination_address", + "value": "0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "key": "amount", + "value": "1000000" + } + ], + "type": "message_received" + } + ], + "messages": [ + { + "gas_limit": null, + "id": 0, + "msg": { + "wasm": { + "execute": { + "contract_addr": "gateway", + "funds": [], + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwZjQyNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifX0=" + } + } + }, + "reply_on": "never" + } + ] + }, + "response_to_source": { + "attributes": [], + "data": null, + "events": [ + { + "attributes": [ + { + "key": "cc_id", + "value": "dest-its-chain_message-id" + }, + { + "key": "destination_chain", + "value": "source-its-chain" + }, + { + "key": "message_type", + "value": "InterchainTransfer" + }, + { + "key": "token_id", + "value": "0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "key": "source_address", + "value": "0202020202020202020202020202020202020202020202020202020202020202" + }, + { + "key": "destination_address", + "value": "0101010101010101010101010101010101010101010101010101010101010101" + }, + { + "key": "amount", + "value": "1000000000000000000" + } + ], + "type": "message_received" + } + ], + "messages": [ + { + "gas_limit": null, + "id": 0, + "msg": { + "wasm": { + "execute": { + "contract_addr": "gateway", + "funds": [], + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoic291cmNlLWl0cy1jaGFpbiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJzb3VyY2UtaXRzLWNvbnRyYWN0IiwicGF5bG9hZCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDBhMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGU2NDY1NzM3NDJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDE2MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDBjMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwZGUwYjZiM2E3NjQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDE0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn19" + } + } + }, + "reply_on": "never" + } + ] + } +} \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/utils/execute.rs b/contracts/interchain-token-service/tests/utils/execute.rs index 5bcca0cd8..e6e15a979 100644 --- a/contracts/interchain-token-service/tests/utils/execute.rs +++ b/contracts/interchain-token-service/tests/utils/execute.rs @@ -141,6 +141,52 @@ pub fn register_chain( .unwrap(); } +pub fn setup_with_chain_configs( + source_max_uint: nonempty::Uint256, + source_max_target_decimals: u8, + destination_max_uint: nonempty::Uint256, + destination_max_target_decimals: u8, +) -> ( + OwnedDeps>, + TestMessage, +) { + let mut deps = make_deps(); + instantiate_contract(deps.as_mut()).unwrap(); + + let TestMessage { + source_its_chain, + source_its_contract, + destination_its_chain, + destination_its_contract, + .. + } = TestMessage::dummy(); + + register_its_contract(deps.as_mut(), source_its_chain.clone(), source_its_contract).unwrap(); + set_chain_config( + deps.as_mut(), + source_its_chain, + source_max_uint, + source_max_target_decimals, + ) + .unwrap(); + + register_its_contract( + deps.as_mut(), + destination_its_chain.clone(), + destination_its_contract, + ) + .unwrap(); + set_chain_config( + deps.as_mut(), + destination_its_chain, + destination_max_uint, + destination_max_target_decimals, + ) + .unwrap(); + + (deps, TestMessage::dummy()) +} + pub fn setup() -> ( OwnedDeps>, TestMessage, diff --git a/packages/axelar-wasm-std/src/nonempty/uint.rs b/packages/axelar-wasm-std/src/nonempty/uint.rs index db52cb7d9..675a88d12 100644 --- a/packages/axelar-wasm-std/src/nonempty/uint.rs +++ b/packages/axelar-wasm-std/src/nonempty/uint.rs @@ -56,6 +56,12 @@ impl fmt::Display for Uint64 { #[serde(try_from = "cosmwasm_std::Uint256")] pub struct Uint256(cosmwasm_std::Uint256); +impl fmt::Display for Uint256 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + impl TryFrom for Uint256 { type Error = Error; From badaf8708c6b2fd21944dd13e26c0d5b26a7a027 Mon Sep 17 00:00:00 2001 From: CJ Cobb <46455409+cjcobb23@users.noreply.github.com> Date: Tue, 5 Nov 2024 14:33:31 -0500 Subject: [PATCH 09/21] feat(minor-interchain-token-service)!: remove deploy token manager msg type (#680) --- contracts/interchain-token-service/src/abi.rs | 137 +----------------- .../src/contract/execute.rs | 34 +---- .../interchain-token-service/src/events.rs | 30 +--- .../src/primitives.rs | 22 +-- ...ploy_interchain_token_encode_decode.golden | 8 +- .../interchain_transfer_encode_decode.golden | 6 +- ...essage_received_with_all_attributes.golden | 29 ---- ...sage_received_with_empty_attributes.golden | 29 ---- .../interchain-token-service/tests/execute.rs | 17 +-- .../execute_hub_message_succeeds.golden | 114 +-------------- ...cimals_when_max_uints_are_different.golden | 54 +++---- ...ecimals_when_max_uints_are_the_same.golden | 54 +++---- ..._when_source_decimals_are_different.golden | 56 +++---- 13 files changed, 104 insertions(+), 486 deletions(-) diff --git a/contracts/interchain-token-service/src/abi.rs b/contracts/interchain-token-service/src/abi.rs index dabec3048..2dc0c0069 100644 --- a/contracts/interchain-token-service/src/abi.rs +++ b/contracts/interchain-token-service/src/abi.rs @@ -16,7 +16,6 @@ sol! { enum MessageType { InterchainTransfer, DeployInterchainToken, - DeployTokenManager, SendToHub, ReceiveFromHub, } @@ -39,13 +38,6 @@ sol! { bytes minter; } - struct DeployTokenManager { - uint256 messageType; - bytes32 tokenId; - uint256 tokenManagerType; - bytes params; - } - struct SendToHub { uint256 messageType; /// True destination chain name when sending a message from ITS edge source contract -> ITS Hub @@ -110,17 +102,6 @@ impl Message { minter: into_vec(minter).into(), } .abi_encode_params(), - Message::DeployTokenManager(primitives::DeployTokenManager { - token_id, - token_manager_type, - params, - }) => DeployTokenManager { - messageType: MessageType::DeployTokenManager.into(), - tokenId: FixedBytes::<32>::new(token_id.into()), - tokenManagerType: token_manager_type.into(), - params: Vec::::from(params).into(), - } - .abi_encode_params(), } .into() } @@ -164,24 +145,6 @@ impl Message { } .into() } - MessageType::DeployTokenManager => { - let decoded = DeployTokenManager::abi_decode_params(payload, true) - .map_err(Error::AbiDecodeFailed)?; - - let token_manager_type = u8::try_from(decoded.tokenManagerType) - .change_context(Error::InvalidTokenManagerType)? - .then(TokenManagerType::from_repr) - .ok_or_else(|| Error::InvalidTokenManagerType)?; - - primitives::DeployTokenManager { - token_id: TokenId::new(decoded.tokenId.into()), - token_manager_type, - params: Vec::::from(decoded.params) - .try_into() - .map_err(Error::NonEmpty)?, - } - .into() - } _ => bail!(Error::InvalidMessageType), }; @@ -286,8 +249,8 @@ mod tests { use router_api::ChainNameRaw; use super::{DeployInterchainToken, InterchainTransfer}; - use crate::abi::{DeployTokenManager, Error, MessageType, SendToHub}; - use crate::{primitives, HubMessage, TokenManagerType}; + use crate::abi::{Error, MessageType, SendToHub}; + use crate::{primitives, HubMessage}; fn from_hex(hex: &str) -> nonempty::HexBinary { HexBinary::from_hex(hex).unwrap().try_into().unwrap() @@ -406,13 +369,6 @@ mod tests { minter: vec![].into(), } .abi_encode_params(), - DeployTokenManager { - messageType: MessageType::DeployTokenManager.into(), - tokenId: FixedBytes::<32>::new([1u8; 32]), - tokenManagerType: TokenManagerType::NativeInterchainToken.into(), - params: vec![].into(), - } - .abi_encode_params(), ]; for message in test_cases { @@ -515,70 +471,12 @@ mod tests { } } - #[test] - fn deploy_token_manager_encode_decode() { - let remote_chain = ChainNameRaw::from_str("chain").unwrap(); - - let cases = vec![ - HubMessage::SendToHub { - destination_chain: remote_chain.clone(), - message: primitives::DeployTokenManager { - token_id: [0u8; 32].into(), - token_manager_type: TokenManagerType::NativeInterchainToken, - params: from_hex("00"), - } - .into(), - }, - HubMessage::SendToHub { - destination_chain: remote_chain.clone(), - message: primitives::DeployTokenManager { - token_id: [1u8; 32].into(), - token_manager_type: TokenManagerType::Gateway, - params: from_hex("1234"), - } - .into(), - }, - HubMessage::ReceiveFromHub { - source_chain: remote_chain.clone(), - message: primitives::DeployTokenManager { - token_id: [0u8; 32].into(), - token_manager_type: TokenManagerType::NativeInterchainToken, - params: from_hex("00"), - } - .into(), - }, - HubMessage::ReceiveFromHub { - source_chain: remote_chain.clone(), - message: primitives::DeployTokenManager { - token_id: [1u8; 32].into(), - token_manager_type: TokenManagerType::Gateway, - params: from_hex("1234"), - } - .into(), - }, - ]; - - let encoded: Vec<_> = cases - .iter() - .map(|original| original.clone().abi_encode().to_hex()) - .collect(); - - goldie::assert_json!(encoded); - - for original in cases { - let encoded = original.clone().abi_encode(); - let decoded = assert_ok!(HubMessage::abi_decode(&encoded)); - assert_eq!(original, decoded); - } - } - #[test] fn invalid_hub_message_type() { let invalid_message_types = vec![ u8::MIN, MessageType::InterchainTransfer as u8, MessageType::DeployInterchainToken as u8, - MessageType::DeployTokenManager as u8, MessageType::ReceiveFromHub as u8 + 1, u8::MAX, ]; @@ -601,7 +499,6 @@ mod tests { let invalid_message_types = vec![ MessageType::SendToHub as u8, MessageType::ReceiveFromHub as u8, - MessageType::DeployTokenManager as u8 + 1, u8::MAX, ]; @@ -620,11 +517,13 @@ mod tests { #[test] fn invalid_destination_chain() { - let message = DeployTokenManager { - messageType: MessageType::DeployTokenManager.into(), + let message = DeployInterchainToken { + messageType: MessageType::DeployInterchainToken.into(), tokenId: FixedBytes::<32>::new([0u8; 32]), - tokenManagerType: TokenManagerType::NativeInterchainToken.into(), - params: vec![].into(), + name: "Test Token".into(), + symbol: "TST".into(), + decimals: 18, + minter: vec![].into(), }; let payload = SendToHub { @@ -638,26 +537,6 @@ mod tests { assert_err_contains!(result, Error, Error::InvalidChainName); } - #[test] - fn invalid_token_manager_type() { - let message = DeployTokenManager { - messageType: MessageType::DeployTokenManager.into(), - tokenId: FixedBytes::<32>::new([0u8; 32]), - tokenManagerType: U256::from(TokenManagerType::Gateway as u8 + 1), - params: vec![].into(), - }; - - let payload = SendToHub { - messageType: MessageType::SendToHub.into(), - destination_chain: "chain".into(), - message: message.abi_encode_params().into(), - } - .abi_encode_params(); - - let result = HubMessage::abi_decode(&payload); - assert_err_contains!(result, Error, Error::InvalidTokenManagerType); - } - #[test] fn encode_decode_large_data() { let large_data = vec![0u8; 1024 * 1024]; // 1MB of data diff --git a/contracts/interchain-token-service/src/contract/execute.rs b/contracts/interchain-token-service/src/contract/execute.rs index 9178a8f85..4346f4298 100644 --- a/contracts/interchain-token-service/src/contract/execute.rs +++ b/contracts/interchain-token-service/src/contract/execute.rs @@ -7,8 +7,7 @@ use crate::events::Event; use crate::primitives::HubMessage; use crate::state::{self, is_chain_frozen, load_config, load_its_contract, TokenDeploymentType}; use crate::{ - DeployInterchainToken, DeployTokenManager, InterchainTransfer, Message, TokenConfig, TokenId, - TokenInstance, + DeployInterchainToken, InterchainTransfer, Message, TokenConfig, TokenId, TokenInstance, }; #[derive(thiserror::Error, Debug, IntoContractError)] @@ -158,13 +157,6 @@ fn apply_to_hub( apply_token_deployment(storage, &source_chain, &destination_chain, deploy_token) .map(Message::DeployInterchainToken)? } - Message::DeployTokenManager(deploy_manager) => apply_token_manager_deployment( - storage, - &source_chain, - &destination_chain, - deploy_manager, - ) - .map(Message::DeployTokenManager)?, } .then(Result::Ok) } @@ -473,24 +465,6 @@ fn apply_token_deployment( }) } -fn apply_token_manager_deployment( - storage: &mut dyn Storage, - source_chain: &ChainNameRaw, - destination_chain: &ChainNameRaw, - deploy_token_manager: DeployTokenManager, -) -> Result { - save_token_instances( - storage, - source_chain, - destination_chain, - None, - None, - deploy_token_manager.token_id, - &deploy_token_manager.deployment_type(), - ) - .map(|_| deploy_token_manager) -} - fn save_token_instances( storage: &mut dyn Storage, source_chain: &ChainNameRaw, @@ -628,12 +602,6 @@ impl DeploymentType for DeployInterchainToken { } } -impl DeploymentType for DeployTokenManager { - fn deployment_type(&self) -> TokenDeploymentType { - TokenDeploymentType::CustomMinter - } -} - #[cfg(test)] mod tests { use assert_ok::assert_ok; diff --git a/contracts/interchain-token-service/src/events.rs b/contracts/interchain-token-service/src/events.rs index b13634dfc..1a0bbd859 100644 --- a/contracts/interchain-token-service/src/events.rs +++ b/contracts/interchain-token-service/src/events.rs @@ -2,7 +2,7 @@ use axelar_wasm_std::event::EventExt; use router_api::{Address, ChainNameRaw, CrossChainId}; use crate::primitives::Message; -use crate::{DeployInterchainToken, DeployTokenManager, InterchainTransfer}; +use crate::{DeployInterchainToken, InterchainTransfer}; pub enum Event { MessageReceived { @@ -80,17 +80,6 @@ fn make_message_event( .add_attribute("symbol", symbol) .add_attribute("decimals", decimals.to_string()) .add_attribute_if_some("minter", minter.map(|minter| minter.to_string())), - Message::DeployTokenManager(DeployTokenManager { - token_id, - token_manager_type, - params, - }) => event - .add_attribute("token_id", token_id.to_string()) - .add_attribute( - "token_manager_type", - token_manager_type.as_ref().to_string(), - ) - .add_attribute("params", params.to_string()), } } @@ -100,10 +89,7 @@ mod test { use router_api::CrossChainId; use crate::events::Event; - use crate::{ - DeployInterchainToken, DeployTokenManager, InterchainTransfer, Message, TokenId, - TokenManagerType, - }; + use crate::{DeployInterchainToken, InterchainTransfer, Message, TokenId}; #[test] fn message_received_with_all_attributes() { @@ -124,12 +110,6 @@ mod test { minter: Some(HexBinary::from([1; 32]).try_into().unwrap()), } .into(), - DeployTokenManager { - token_id: TokenId::new([1; 32]), - token_manager_type: TokenManagerType::MintBurn, - params: HexBinary::from([1, 2, 3, 4]).try_into().unwrap(), - } - .into(), ]; let events: Vec<_> = test_cases @@ -183,12 +163,6 @@ mod test { minter: None, } .into(), - DeployTokenManager { - token_id: TokenId::new([1; 32]), - token_manager_type: TokenManagerType::MintBurn, - params: HexBinary::from([0u8]).try_into().unwrap(), - } - .into(), ]; let events: Vec<_> = test_cases diff --git a/contracts/interchain-token-service/src/primitives.rs b/contracts/interchain-token-service/src/primitives.rs index ef3780028..447cbb131 100644 --- a/contracts/interchain-token-service/src/primitives.rs +++ b/contracts/interchain-token-service/src/primitives.rs @@ -43,8 +43,6 @@ pub enum Message { InterchainTransfer(InterchainTransfer), /// Deploy a new interchain token on the destination chain DeployInterchainToken(DeployInterchainToken), - /// Deploy a new token manager on the destination chain - DeployTokenManager(DeployTokenManager), } #[cw_serde] @@ -90,23 +88,6 @@ impl From for Message { } } -#[cw_serde] -#[derive(Eq)] -pub struct DeployTokenManager { - /// The unique identifier of the token that the token manager will manage - pub token_id: TokenId, - /// The type of token manager to deploy - pub token_manager_type: TokenManagerType, - /// The parameters to be provided to the token manager contract - pub params: nonempty::HexBinary, -} - -impl From for Message { - fn from(value: DeployTokenManager) -> Self { - Message::DeployTokenManager(value) - } -} - /// A message sent between ITS edge contracts and the ITS hub contract (defined in this crate). /// `HubMessage` is used to route an ITS [`Message`] between ITS edge contracts on different chains via the ITS Hub. #[cw_serde] @@ -143,8 +124,7 @@ impl Message { pub fn token_id(&self) -> TokenId { match self { Message::InterchainTransfer(InterchainTransfer { token_id, .. }) - | Message::DeployInterchainToken(DeployInterchainToken { token_id, .. }) - | Message::DeployTokenManager(DeployTokenManager { token_id, .. }) => *token_id, + | Message::DeployInterchainToken(DeployInterchainToken { token_id, .. }) => *token_id, } } } diff --git a/contracts/interchain-token-service/src/testdata/deploy_interchain_token_encode_decode.golden b/contracts/interchain-token-service/src/testdata/deploy_interchain_token_encode_decode.golden index 76198c155..7731f612c 100644 --- a/contracts/interchain-token-service/src/testdata/deploy_interchain_token_encode_decode.golden +++ b/contracts/interchain-token-service/src/testdata/deploy_interchain_token_encode_decode.golden @@ -1,8 +1,8 @@ [ + "0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000017400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000154000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001010101010101010101010101010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000a5465737420546f6b656e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003545354000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021234000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000012556e69636f646520546f6b656e20f09faa9900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007554e49f09f94a3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000017400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000154000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001010101010101010101010101010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000a5465737420546f6b656e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003545354000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021234000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000012556e69636f646520546f6b656e20f09faa9900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007554e49f09f94a3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000017400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000154000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001010101010101010101010101010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000a5465737420546f6b656e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003545354000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021234000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000012556e69636f646520546f6b656e20f09faa9900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007554e49f09f94a3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000012556e69636f646520546f6b656e20f09faa9900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007554e49f09f94a3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000" ] \ No newline at end of file diff --git a/contracts/interchain-token-service/src/testdata/interchain_transfer_encode_decode.golden b/contracts/interchain-token-service/src/testdata/interchain_transfer_encode_decode.golden index a29babff8..ba11f86ff 100644 --- a/contracts/interchain-token-service/src/testdata/interchain_transfer_encode_decode.golden +++ b/contracts/interchain-token-service/src/testdata/interchain_transfer_encode_decode.golden @@ -1,6 +1,6 @@ [ + "0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a500000000000000000000000000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a50000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a500000000000000000000000000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a50000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a500000000000000000000000000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a50000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a500000000000000000000000000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a50000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000" ] \ No newline at end of file diff --git a/contracts/interchain-token-service/src/testdata/message_received_with_all_attributes.golden b/contracts/interchain-token-service/src/testdata/message_received_with_all_attributes.golden index 0f3eb14ce..6ca4f7e4c 100644 --- a/contracts/interchain-token-service/src/testdata/message_received_with_all_attributes.golden +++ b/contracts/interchain-token-service/src/testdata/message_received_with_all_attributes.golden @@ -72,34 +72,5 @@ "value": "0101010101010101010101010101010101010101010101010101010101010101" } ] - }, - { - "type": "message_received", - "attributes": [ - { - "key": "cc_id", - "value": "source_hash" - }, - { - "key": "destination_chain", - "value": "destination" - }, - { - "key": "message_type", - "value": "DeployTokenManager" - }, - { - "key": "token_id", - "value": "0101010101010101010101010101010101010101010101010101010101010101" - }, - { - "key": "token_manager_type", - "value": "MintBurn" - }, - { - "key": "params", - "value": "01020304" - } - ] } ] \ No newline at end of file diff --git a/contracts/interchain-token-service/src/testdata/message_received_with_empty_attributes.golden b/contracts/interchain-token-service/src/testdata/message_received_with_empty_attributes.golden index dcc2a9da3..9c1879b17 100644 --- a/contracts/interchain-token-service/src/testdata/message_received_with_empty_attributes.golden +++ b/contracts/interchain-token-service/src/testdata/message_received_with_empty_attributes.golden @@ -130,34 +130,5 @@ "value": "0" } ] - }, - { - "type": "message_received", - "attributes": [ - { - "key": "cc_id", - "value": "source_hash" - }, - { - "key": "destination_chain", - "value": "destination" - }, - { - "key": "message_type", - "value": "DeployTokenManager" - }, - { - "key": "token_id", - "value": "0101010101010101010101010101010101010101010101010101010101010101" - }, - { - "key": "token_manager_type", - "value": "MintBurn" - }, - { - "key": "params", - "value": "00" - } - ] } ] \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/execute.rs b/contracts/interchain-token-service/tests/execute.rs index 3ee38fde1..2c8b012a4 100644 --- a/contracts/interchain-token-service/tests/execute.rs +++ b/contracts/interchain-token-service/tests/execute.rs @@ -10,8 +10,7 @@ use interchain_token_service::contract::{self, ExecuteError}; use interchain_token_service::events::Event; use interchain_token_service::msg::ExecuteMsg; use interchain_token_service::{ - DeployInterchainToken, DeployTokenManager, HubMessage, InterchainTransfer, TokenId, - TokenManagerType, TokenSupply, + DeployInterchainToken, HubMessage, InterchainTransfer, TokenId, TokenSupply, }; use router_api::{Address, ChainName, ChainNameRaw, CrossChainId}; use serde_json::json; @@ -113,20 +112,6 @@ fn execute_hub_message_succeeds() { data: Some(HexBinary::from([1, 2, 3, 4]).try_into().unwrap()), } .into(), - DeployTokenManager { - token_id: TokenId::new([2; 32]), - token_manager_type: TokenManagerType::MintBurn, - params: HexBinary::from([1, 2, 3, 4]).try_into().unwrap(), - } - .into(), - InterchainTransfer { - token_id: TokenId::new([2; 32]), - source_address: HexBinary::from([1; 32]).try_into().unwrap(), - destination_address: HexBinary::from([2; 32]).try_into().unwrap(), - amount: 1u64.try_into().unwrap(), - data: Some(HexBinary::from([1, 2, 3, 4]).try_into().unwrap()), - } - .into(), ]; let responses: Vec<_> = test_messages diff --git a/contracts/interchain-token-service/tests/testdata/execute_hub_message_succeeds.golden b/contracts/interchain-token-service/tests/testdata/execute_hub_message_succeeds.golden index 69e88351f..adb3b52f9 100644 --- a/contracts/interchain-token-service/tests/testdata/execute_hub_message_succeeds.golden +++ b/contracts/interchain-token-service/tests/testdata/execute_hub_message_succeeds.golden @@ -7,7 +7,7 @@ "wasm": { "execute": { "contract_addr": "gateway", - "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDU0NjU3Mzc0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNTQ1MzU0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn19", + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDU0NjU3Mzc0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNTQ1MzU0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn19", "funds": [] } } @@ -66,7 +66,7 @@ "wasm": { "execute": { "contract_addr": "gateway", - "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDQwMTAyMDMwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn19", + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDQwMTAyMDMwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn19", "funds": [] } } @@ -116,115 +116,5 @@ } ], "data": null - }, - { - "messages": [ - { - "id": 0, - "msg": { - "wasm": { - "execute": { - "contract_addr": "gateway", - "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA4MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDQwMTAyMDMwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn19", - "funds": [] - } - } - }, - "gas_limit": null, - "reply_on": "never" - } - ], - "attributes": [], - "events": [ - { - "type": "message_received", - "attributes": [ - { - "key": "cc_id", - "value": "source-its-chain_message-id" - }, - { - "key": "destination_chain", - "value": "dest-its-chain" - }, - { - "key": "message_type", - "value": "DeployTokenManager" - }, - { - "key": "token_id", - "value": "0202020202020202020202020202020202020202020202020202020202020202" - }, - { - "key": "token_manager_type", - "value": "MintBurn" - }, - { - "key": "params", - "value": "01020304" - } - ] - } - ], - "data": null - }, - { - "messages": [ - { - "id": 0, - "msg": { - "wasm": { - "execute": { - "contract_addr": "gateway", - "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDQwMTAyMDMwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn19", - "funds": [] - } - } - }, - "gas_limit": null, - "reply_on": "never" - } - ], - "attributes": [], - "events": [ - { - "type": "message_received", - "attributes": [ - { - "key": "cc_id", - "value": "source-its-chain_message-id" - }, - { - "key": "destination_chain", - "value": "dest-its-chain" - }, - { - "key": "message_type", - "value": "InterchainTransfer" - }, - { - "key": "token_id", - "value": "0202020202020202020202020202020202020202020202020202020202020202" - }, - { - "key": "source_address", - "value": "0101010101010101010101010101010101010101010101010101010101010101" - }, - { - "key": "destination_address", - "value": "0202020202020202020202020202020202020202020202020202020202020202" - }, - { - "key": "amount", - "value": "1" - }, - { - "key": "data", - "value": "01020304" - } - ] - } - ], - "data": null } ] \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_different.golden b/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_different.golden index 530997071..61a3e9a26 100644 --- a/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_different.golden +++ b/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_different.golden @@ -1,15 +1,25 @@ { - "destination_token_instance": { - "decimals": 6, - "supply": { - "tracked": "0" - } - }, "response": { + "messages": [ + { + "id": 0, + "msg": { + "wasm": { + "execute": { + "contract_addr": "gateway", + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDU0NjU3Mzc0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNTQ1MzU0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifX0=", + "funds": [] + } + } + }, + "gas_limit": null, + "reply_on": "never" + } + ], "attributes": [], - "data": null, "events": [ { + "type": "message_received", "attributes": [ { "key": "cc_id", @@ -39,29 +49,19 @@ "key": "decimals", "value": "6" } - ], - "type": "message_received" + ] } ], - "messages": [ - { - "gas_limit": null, - "id": 0, - "msg": { - "wasm": { - "execute": { - "contract_addr": "gateway", - "funds": [], - "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDU0NjU3Mzc0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNTQ1MzU0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifX0=" - } - } - }, - "reply_on": "never" - } - ] + "data": null }, "source_token_instance": { - "decimals": 18, - "supply": "untracked" + "supply": "untracked", + "decimals": 18 + }, + "destination_token_instance": { + "supply": { + "tracked": "0" + }, + "decimals": 6 } } \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_the_same.golden b/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_the_same.golden index 43c0ceafa..864041d28 100644 --- a/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_the_same.golden +++ b/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_the_same.golden @@ -1,15 +1,25 @@ { - "destination_token_instance": { - "decimals": 18, - "supply": { - "tracked": "0" - } - }, "response": { + "messages": [ + { + "id": 0, + "msg": { + "wasm": { + "execute": { + "contract_addr": "gateway", + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDU0NjU3Mzc0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNTQ1MzU0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifX0=", + "funds": [] + } + } + }, + "gas_limit": null, + "reply_on": "never" + } + ], "attributes": [], - "data": null, "events": [ { + "type": "message_received", "attributes": [ { "key": "cc_id", @@ -39,29 +49,19 @@ "key": "decimals", "value": "18" } - ], - "type": "message_received" + ] } ], - "messages": [ - { - "gas_limit": null, - "id": 0, - "msg": { - "wasm": { - "execute": { - "contract_addr": "gateway", - "funds": [], - "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDU0NjU3Mzc0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNTQ1MzU0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifX0=" - } - } - }, - "reply_on": "never" - } - ] + "data": null }, "source_token_instance": { - "decimals": 18, - "supply": "untracked" + "supply": "untracked", + "decimals": 18 + }, + "destination_token_instance": { + "supply": { + "tracked": "0" + }, + "decimals": 18 } } \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/execute_message_interchain_transfer_should_scale_the_amount_when_source_decimals_are_different.golden b/contracts/interchain-token-service/tests/testdata/execute_message_interchain_transfer_should_scale_the_amount_when_source_decimals_are_different.golden index 0a984fd44..c9e1f6144 100644 --- a/contracts/interchain-token-service/tests/testdata/execute_message_interchain_transfer_should_scale_the_amount_when_source_decimals_are_different.golden +++ b/contracts/interchain-token-service/tests/testdata/execute_message_interchain_transfer_should_scale_the_amount_when_source_decimals_are_different.golden @@ -1,9 +1,25 @@ { "response_to_destination": { + "messages": [ + { + "id": 0, + "msg": { + "wasm": { + "execute": { + "contract_addr": "gateway", + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwZjQyNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifX0=", + "funds": [] + } + } + }, + "gas_limit": null, + "reply_on": "never" + } + ], "attributes": [], - "data": null, "events": [ { + "type": "message_received", "attributes": [ { "key": "cc_id", @@ -33,32 +49,32 @@ "key": "amount", "value": "1000000" } - ], - "type": "message_received" + ] } ], + "data": null + }, + "response_to_source": { "messages": [ { - "gas_limit": null, "id": 0, "msg": { "wasm": { "execute": { "contract_addr": "gateway", - "funds": [], - "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwZjQyNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifX0=" + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoic291cmNlLWl0cy1jaGFpbiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJzb3VyY2UtaXRzLWNvbnRyYWN0IiwicGF5bG9hZCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDBhMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGU2NDY1NzM3NDJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDE2MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDBjMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwZGUwYjZiM2E3NjQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDE0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn19", + "funds": [] } } }, + "gas_limit": null, "reply_on": "never" } - ] - }, - "response_to_source": { + ], "attributes": [], - "data": null, "events": [ { + "type": "message_received", "attributes": [ { "key": "cc_id", @@ -88,25 +104,9 @@ "key": "amount", "value": "1000000000000000000" } - ], - "type": "message_received" + ] } ], - "messages": [ - { - "gas_limit": null, - "id": 0, - "msg": { - "wasm": { - "execute": { - "contract_addr": "gateway", - "funds": [], - "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoic291cmNlLWl0cy1jaGFpbiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJzb3VyY2UtaXRzLWNvbnRyYWN0IiwicGF5bG9hZCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDBhMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGU2NDY1NzM3NDJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDE2MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDBjMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwZGUwYjZiM2E3NjQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDE0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn19" - } - } - }, - "reply_on": "never" - } - ] + "data": null } } \ No newline at end of file From 98acb3bb3694707a38607e163a6da71b00e02be8 Mon Sep 17 00:00:00 2001 From: CJ Cobb <46455409+cjcobb23@users.noreply.github.com> Date: Wed, 6 Nov 2024 15:10:26 -0500 Subject: [PATCH 10/21] fix(minor-interchain-token-service): fix message type encoding (#683) --- contracts/interchain-token-service/src/abi.rs | 1 + ...ploy_interchain_token_encode_decode.golden | 8 +-- .../interchain_transfer_encode_decode.golden | 6 +- .../execute_hub_message_succeeds.golden | 4 +- ...cimals_when_max_uints_are_different.golden | 54 +++++++++--------- ...ecimals_when_max_uints_are_the_same.golden | 54 +++++++++--------- ..._when_source_decimals_are_different.golden | 56 +++++++++---------- 7 files changed, 92 insertions(+), 91 deletions(-) diff --git a/contracts/interchain-token-service/src/abi.rs b/contracts/interchain-token-service/src/abi.rs index 2dc0c0069..45fd54f2c 100644 --- a/contracts/interchain-token-service/src/abi.rs +++ b/contracts/interchain-token-service/src/abi.rs @@ -16,6 +16,7 @@ sol! { enum MessageType { InterchainTransfer, DeployInterchainToken, + DeployTokenManager, // note, this case is not supported by the ITS hub SendToHub, ReceiveFromHub, } diff --git a/contracts/interchain-token-service/src/testdata/deploy_interchain_token_encode_decode.golden b/contracts/interchain-token-service/src/testdata/deploy_interchain_token_encode_decode.golden index 7731f612c..76198c155 100644 --- a/contracts/interchain-token-service/src/testdata/deploy_interchain_token_encode_decode.golden +++ b/contracts/interchain-token-service/src/testdata/deploy_interchain_token_encode_decode.golden @@ -1,8 +1,8 @@ [ - "0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000017400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000154000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001010101010101010101010101010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000a5465737420546f6b656e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003545354000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021234000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000012556e69636f646520546f6b656e20f09faa9900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007554e49f09f94a3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000017400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000154000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001010101010101010101010101010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000a5465737420546f6b656e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003545354000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021234000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000012556e69636f646520546f6b656e20f09faa9900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007554e49f09f94a3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000012556e69636f646520546f6b656e20f09faa9900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007554e49f09f94a3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000017400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000154000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001010101010101010101010101010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000a5465737420546f6b656e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003545354000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000021234000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000012556e69636f646520546f6b656e20f09faa9900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007554e49f09f94a3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000" ] \ No newline at end of file diff --git a/contracts/interchain-token-service/src/testdata/interchain_transfer_encode_decode.golden b/contracts/interchain-token-service/src/testdata/interchain_transfer_encode_decode.golden index ba11f86ff..a29babff8 100644 --- a/contracts/interchain-token-service/src/testdata/interchain_transfer_encode_decode.golden +++ b/contracts/interchain-token-service/src/testdata/interchain_transfer_encode_decode.golden @@ -1,6 +1,6 @@ [ - "0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a500000000000000000000000000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a50000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a500000000000000000000000000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a50000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a500000000000000000000000000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a50000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000005636861696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a500000000000000000000000000000000000000000000000000000000000000000000000000000000000000144f4495243837681061c4743b74b3eedf548d56a50000000000000000000000000000000000000000000000000000000000000000000000000000000000000002abcd000000000000000000000000000000000000000000000000000000000000" ] \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/execute_hub_message_succeeds.golden b/contracts/interchain-token-service/tests/testdata/execute_hub_message_succeeds.golden index adb3b52f9..bcc500834 100644 --- a/contracts/interchain-token-service/tests/testdata/execute_hub_message_succeeds.golden +++ b/contracts/interchain-token-service/tests/testdata/execute_hub_message_succeeds.golden @@ -7,7 +7,7 @@ "wasm": { "execute": { "contract_addr": "gateway", - "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDU0NjU3Mzc0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNTQ1MzU0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn19", + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDU0NjU3Mzc0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNTQ1MzU0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxIn19", "funds": [] } } @@ -66,7 +66,7 @@ "wasm": { "execute": { "contract_addr": "gateway", - "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDQwMTAyMDMwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn19", + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDQwMTAyMDMwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn19", "funds": [] } } diff --git a/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_different.golden b/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_different.golden index 61a3e9a26..530997071 100644 --- a/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_different.golden +++ b/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_different.golden @@ -1,25 +1,15 @@ { + "destination_token_instance": { + "decimals": 6, + "supply": { + "tracked": "0" + } + }, "response": { - "messages": [ - { - "id": 0, - "msg": { - "wasm": { - "execute": { - "contract_addr": "gateway", - "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDU0NjU3Mzc0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNTQ1MzU0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifX0=", - "funds": [] - } - } - }, - "gas_limit": null, - "reply_on": "never" - } - ], "attributes": [], + "data": null, "events": [ { - "type": "message_received", "attributes": [ { "key": "cc_id", @@ -49,19 +39,29 @@ "key": "decimals", "value": "6" } - ] + ], + "type": "message_received" } ], - "data": null + "messages": [ + { + "gas_limit": null, + "id": 0, + "msg": { + "wasm": { + "execute": { + "contract_addr": "gateway", + "funds": [], + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDU0NjU3Mzc0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNTQ1MzU0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifX0=" + } + } + }, + "reply_on": "never" + } + ] }, "source_token_instance": { - "supply": "untracked", - "decimals": 18 - }, - "destination_token_instance": { - "supply": { - "tracked": "0" - }, - "decimals": 6 + "decimals": 18, + "supply": "untracked" } } \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_the_same.golden b/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_the_same.golden index 864041d28..43c0ceafa 100644 --- a/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_the_same.golden +++ b/contracts/interchain-token-service/tests/testdata/execute_message_deploy_interchain_token_should_translate_decimals_when_max_uints_are_the_same.golden @@ -1,25 +1,15 @@ { + "destination_token_instance": { + "decimals": 18, + "supply": { + "tracked": "0" + } + }, "response": { - "messages": [ - { - "id": 0, - "msg": { - "wasm": { - "execute": { - "contract_addr": "gateway", - "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDU0NjU3Mzc0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNTQ1MzU0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifX0=", - "funds": [] - } - } - }, - "gas_limit": null, - "reply_on": "never" - } - ], "attributes": [], + "data": null, "events": [ { - "type": "message_received", "attributes": [ { "key": "cc_id", @@ -49,19 +39,29 @@ "key": "decimals", "value": "18" } - ] + ], + "type": "message_received" } ], - "data": null + "messages": [ + { + "gas_limit": null, + "id": 0, + "msg": { + "wasm": { + "execute": { + "contract_addr": "gateway", + "funds": [], + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTIwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDU0NjU3Mzc0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNTQ1MzU0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifX0=" + } + } + }, + "reply_on": "never" + } + ] }, "source_token_instance": { - "supply": "untracked", - "decimals": 18 - }, - "destination_token_instance": { - "supply": { - "tracked": "0" - }, - "decimals": 18 + "decimals": 18, + "supply": "untracked" } } \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/execute_message_interchain_transfer_should_scale_the_amount_when_source_decimals_are_different.golden b/contracts/interchain-token-service/tests/testdata/execute_message_interchain_transfer_should_scale_the_amount_when_source_decimals_are_different.golden index c9e1f6144..0a984fd44 100644 --- a/contracts/interchain-token-service/tests/testdata/execute_message_interchain_transfer_should_scale_the_amount_when_source_decimals_are_different.golden +++ b/contracts/interchain-token-service/tests/testdata/execute_message_interchain_transfer_should_scale_the_amount_when_source_decimals_are_different.golden @@ -1,25 +1,9 @@ { "response_to_destination": { - "messages": [ - { - "id": 0, - "msg": { - "wasm": { - "execute": { - "contract_addr": "gateway", - "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwZjQyNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifX0=", - "funds": [] - } - } - }, - "gas_limit": null, - "reply_on": "never" - } - ], "attributes": [], + "data": null, "events": [ { - "type": "message_received", "attributes": [ { "key": "cc_id", @@ -49,32 +33,32 @@ "key": "amount", "value": "1000000" } - ] + ], + "type": "message_received" } ], - "data": null - }, - "response_to_source": { "messages": [ { + "gas_limit": null, "id": 0, "msg": { "wasm": { "execute": { "contract_addr": "gateway", - "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoic291cmNlLWl0cy1jaGFpbiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJzb3VyY2UtaXRzLWNvbnRyYWN0IiwicGF5bG9hZCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDBhMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGU2NDY1NzM3NDJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDE2MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDBjMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwZGUwYjZiM2E3NjQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDE0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn19", - "funds": [] + "funds": [], + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoiZGVzdC1pdHMtY2hhaW4iLCJkZXN0aW5hdGlvbl9hZGRyZXNzIjoiZGVzdC1pdHMtY29udHJhY3QiLCJwYXlsb2FkIjoiMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDczNmY3NTcyNjM2NTJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwZjQyNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDIwMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAifX0=" } } }, - "gas_limit": null, "reply_on": "never" } - ], + ] + }, + "response_to_source": { "attributes": [], + "data": null, "events": [ { - "type": "message_received", "attributes": [ { "key": "cc_id", @@ -104,9 +88,25 @@ "key": "amount", "value": "1000000000000000000" } - ] + ], + "type": "message_received" } ], - "data": null + "messages": [ + { + "gas_limit": null, + "id": 0, + "msg": { + "wasm": { + "execute": { + "contract_addr": "gateway", + "funds": [], + "msg": "eyJjYWxsX2NvbnRyYWN0Ijp7ImRlc3RpbmF0aW9uX2NoYWluIjoic291cmNlLWl0cy1jaGFpbiIsImRlc3RpbmF0aW9uX2FkZHJlc3MiOiJzb3VyY2UtaXRzLWNvbnRyYWN0IiwicGF5bG9hZCI6IjAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDBhMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGU2NDY1NzM3NDJkNjk3NDczMmQ2MzY4NjE2OTZlMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDE2MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDBjMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwZGUwYjZiM2E3NjQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDE0MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMjAwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDIwMjAyMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMTAxMDEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn19" + } + } + }, + "reply_on": "never" + } + ] } } \ No newline at end of file From 08bec3cee505b80ab35f5242765b54363f2b00a4 Mon Sep 17 00:00:00 2001 From: CJ Cobb <46455409+cjcobb23@users.noreply.github.com> Date: Thu, 7 Nov 2024 12:07:19 -0500 Subject: [PATCH 11/21] feat(minor-interchain-token-service): refactor chain registration (#681) --- .../interchain-token-service/src/contract.rs | 38 ++-- .../src/contract/execute.rs | 153 +++++++++----- contracts/interchain-token-service/src/msg.rs | 33 +-- .../interchain-token-service/src/state.rs | 104 +++++---- .../interchain-token-service/tests/execute.rs | 198 ++++++++++++++---- .../tests/instantiate.rs | 14 -- .../interchain-token-service/tests/query.rs | 30 ++- .../instantiate_with_args_succeeds.golden | 29 +-- .../tests/utils/execute.rs | 123 +++++------ .../tests/utils/instantiate.rs | 1 - 10 files changed, 432 insertions(+), 291 deletions(-) diff --git a/contracts/interchain-token-service/src/contract.rs b/contracts/interchain-token-service/src/contract.rs index e7d3aaf7a..bb4751358 100644 --- a/contracts/interchain-token-service/src/contract.rs +++ b/contracts/interchain-token-service/src/contract.rs @@ -9,7 +9,6 @@ use cosmwasm_std::{Addr, Binary, Deps, DepsMut, Empty, Env, MessageInfo, Respons use error_stack::{Report, ResultExt}; use execute::{freeze_chain, unfreeze_chain}; -use crate::events::Event; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; use crate::state; use crate::state::Config; @@ -26,10 +25,10 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub enum Error { #[error("failed to execute a cross-chain message")] Execute, - #[error("failed to register an its edge contract")] - RegisterItsContract, - #[error("failed to deregsiter an its edge contract")] - DeregisterItsContract, + #[error("failed to register chains")] + RegisterChains, + #[error("failed to update chain")] + UpdateChain, #[error("failed to freeze chain")] FreezeChain, #[error("failed to unfreeze chain")] @@ -79,17 +78,9 @@ pub fn instantiate( state::save_config(deps.storage, &Config { axelarnet_gateway })?; - for (chain, address) in msg.its_contracts.iter() { - state::save_its_contract(deps.storage, chain, address)?; - } - killswitch::init(deps.storage, killswitch::State::Disengaged)?; - Ok(Response::new().add_events( - msg.its_contracts - .into_iter() - .map(|(chain, address)| Event::ItsContractRegistered { chain, address }.into()), - )) + Ok(Response::new()) } #[cfg_attr(not(feature = "library"), entry_point)] @@ -106,13 +97,14 @@ pub fn execute( payload, }) => execute::execute_message(deps, cc_id, source_address, payload) .change_context(Error::Execute), - ExecuteMsg::RegisterItsContract { chain, address } => { - execute::register_its_contract(deps, chain, address) - .change_context(Error::RegisterItsContract) + ExecuteMsg::RegisterChains { chains } => { + execute::register_chains(deps, chains).change_context(Error::RegisterChains) } - ExecuteMsg::DeregisterItsContract { chain } => { - execute::deregister_its_contract(deps, chain) - .change_context(Error::DeregisterItsContract) + ExecuteMsg::UpdateChain { + chain, + its_edge_contract, + } => { + execute::update_chain(deps, chain, its_edge_contract).change_context(Error::UpdateChain) } ExecuteMsg::FreezeChain { chain } => { freeze_chain(deps, chain).change_context(Error::FreezeChain) @@ -126,12 +118,6 @@ pub fn execute( ExecuteMsg::EnableExecution => { execute::enable_execution(deps).change_context(Error::EnableExecution) } - ExecuteMsg::SetChainConfig { - chain, - max_uint, - max_target_decimals, - } => execute::set_chain_config(deps, chain, max_uint, max_target_decimals) - .change_context(Error::SetChainConfig), }? .then(Ok) } diff --git a/contracts/interchain-token-service/src/contract/execute.rs b/contracts/interchain-token-service/src/contract/execute.rs index 4346f4298..1ab5ee746 100644 --- a/contracts/interchain-token-service/src/contract/execute.rs +++ b/contracts/interchain-token-service/src/contract/execute.rs @@ -1,19 +1,20 @@ use axelar_wasm_std::{killswitch, nonempty, FnExt, IntoContractError}; use cosmwasm_std::{DepsMut, HexBinary, QuerierWrapper, Response, Storage, Uint256}; use error_stack::{bail, ensure, report, Result, ResultExt}; +use itertools::Itertools; use router_api::{Address, ChainNameRaw, CrossChainId}; use crate::events::Event; use crate::primitives::HubMessage; use crate::state::{self, is_chain_frozen, load_config, load_its_contract, TokenDeploymentType}; use crate::{ - DeployInterchainToken, InterchainTransfer, Message, TokenConfig, TokenId, TokenInstance, + msg, DeployInterchainToken, InterchainTransfer, Message, TokenConfig, TokenId, TokenInstance, }; #[derive(thiserror::Error, Debug, IntoContractError)] pub enum Error { - #[error("unknown chain {0}")] - UnknownChain(ChainNameRaw), + #[error("chain not found {0}")] + ChainNotFound(ChainNameRaw), #[error("unknown its address {0}")] UnknownItsContract(Address), #[error("failed to decode payload")] @@ -40,8 +41,8 @@ pub enum Error { }, #[error("state error")] State, - #[error("chain config for {0} already set")] - ChainConfigAlreadySet(ChainNameRaw), + #[error("chain {0} already registered")] + ChainAlreadyRegistered(ChainNameRaw), #[error("token {token_id} not deployed on chain {chain}")] TokenNotDeployed { token_id: TokenId, @@ -107,7 +108,7 @@ fn execute_message_on_hub( message: Message, ) -> Result { let destination_address = load_its_contract(deps.storage, &destination_chain) - .change_context_lazy(|| Error::UnknownChain(destination_chain.clone()))?; + .change_context_lazy(|| Error::ChainNotFound(destination_chain.clone()))?; let message = apply_to_hub( deps.storage, @@ -177,7 +178,7 @@ fn ensure_is_its_source_address( source_address: &Address, ) -> Result<(), Error> { let source_its_contract = load_its_contract(storage, source_chain) - .change_context_lazy(|| Error::UnknownChain(source_chain.clone()))?; + .change_context_lazy(|| Error::ChainNotFound(source_chain.clone()))?; ensure!( source_address == &source_its_contract, @@ -205,24 +206,6 @@ fn send_to_destination( Ok(Response::new().add_message(call_contract_msg)) } -pub fn register_its_contract( - deps: DepsMut, - chain: ChainNameRaw, - address: Address, -) -> Result { - state::save_its_contract(deps.storage, &chain, &address) - .change_context_lazy(|| Error::FailedItsContractRegistration(chain.clone()))?; - - Ok(Response::new().add_event(Event::ItsContractRegistered { chain, address }.into())) -} - -pub fn deregister_its_contract(deps: DepsMut, chain: ChainNameRaw) -> Result { - state::remove_its_contract(deps.storage, &chain) - .change_context_lazy(|| Error::FailedItsContractDeregistration(chain.clone()))?; - - Ok(Response::new().add_event(Event::ItsContractDeregistered { chain }.into())) -} - pub fn freeze_chain(deps: DepsMut, chain: ChainNameRaw) -> Result { state::freeze_chain(deps.storage, &chain).change_context(Error::State)?; @@ -243,20 +226,32 @@ pub fn enable_execution(deps: DepsMut) -> Result { killswitch::disengage(deps.storage, Event::ExecutionEnabled).change_context(Error::State) } -pub fn set_chain_config( - deps: DepsMut, - chain: ChainNameRaw, - max_uint: nonempty::Uint256, - max_target_decimals: u8, -) -> Result { - match state::may_load_chain_config(deps.storage, &chain).change_context(Error::State)? { - Some(_) => bail!(Error::ChainConfigAlreadySet(chain)), - None => state::save_chain_config(deps.storage, &chain, max_uint, max_target_decimals) +pub fn register_chains(deps: DepsMut, chains: Vec) -> Result { + chains + .into_iter() + .map(|chain_config| register_chain(deps.storage, chain_config)) + .try_collect::<_, Vec, _>()? + .then(|_| Ok(Response::new())) +} + +fn register_chain(storage: &mut dyn Storage, config: msg::ChainConfig) -> Result { + match state::may_load_chain_config(storage, &config.chain).change_context(Error::State)? { + Some(_) => bail!(Error::ChainAlreadyRegistered(config.chain)), + None => state::save_chain_config(storage, &config.chain.clone(), config) .change_context(Error::State)? .then(|_| Ok(Response::new())), } } +pub fn update_chain( + deps: DepsMut, + chain: ChainNameRaw, + its_address: Address, +) -> Result { + state::update_its_contract(deps.storage, &chain, its_address).change_context(Error::State)?; + Ok(Response::new()) +} + /// Calculates the destination on token transfer amount. /// /// The amount is calculated based on the token decimals on the source and destination chains. @@ -612,11 +607,11 @@ mod tests { use router_api::{ChainNameRaw, CrossChainId}; use crate::contract::execute::{ - disable_execution, enable_execution, execute_message, freeze_chain, register_its_contract, - set_chain_config, unfreeze_chain, Error, + disable_execution, enable_execution, execute_message, freeze_chain, register_chain, + register_chains, unfreeze_chain, update_chain, Error, }; use crate::state::{self, Config}; - use crate::{DeployInterchainToken, HubMessage, InterchainTransfer}; + use crate::{msg, DeployInterchainToken, HubMessage, InterchainTransfer}; const SOLANA: &str = "solana"; const ETHEREUM: &str = "ethereum"; @@ -843,6 +838,72 @@ mod tests { )); } + #[test] + fn register_chain_fails_if_already_registered() { + let mut deps = mock_dependencies(); + assert_ok!(register_chain( + deps.as_mut().storage, + msg::ChainConfig { + chain: SOLANA.parse().unwrap(), + its_edge_contract: ITS_ADDRESS.to_string().try_into().unwrap(), + max_uint: Uint256::one().try_into().unwrap(), + max_target_decimals: 16u8 + } + )); + assert_err_contains!( + register_chain( + deps.as_mut().storage, + msg::ChainConfig { + chain: SOLANA.parse().unwrap(), + its_edge_contract: ITS_ADDRESS.to_string().try_into().unwrap(), + max_uint: Uint256::one().try_into().unwrap(), + max_target_decimals: 16u8 + } + ), + Error, + Error::ChainAlreadyRegistered(..) + ); + } + + #[test] + fn register_chains_fails_if_any_already_registered() { + let mut deps = mock_dependencies(); + let chains = vec![ + msg::ChainConfig { + chain: SOLANA.parse().unwrap(), + its_edge_contract: ITS_ADDRESS.to_string().try_into().unwrap(), + max_uint: Uint256::MAX.try_into().unwrap(), + max_target_decimals: 16u8, + }, + msg::ChainConfig { + chain: XRPL.parse().unwrap(), + its_edge_contract: ITS_ADDRESS.to_string().try_into().unwrap(), + max_uint: Uint256::MAX.try_into().unwrap(), + max_target_decimals: 16u8, + }, + ]; + assert_ok!(register_chains(deps.as_mut(), chains[0..1].to_vec())); + assert_err_contains!( + register_chains(deps.as_mut(), chains,), + Error, + Error::ChainAlreadyRegistered(..) + ); + } + + #[test] + fn update_chain_fails_if_not_registered() { + let mut deps = mock_dependencies(); + assert_err_contains!( + update_chain( + deps.as_mut(), + SOLANA.parse().unwrap(), + ITS_ADDRESS.parse().unwrap() + ), + Error, + Error::State + ); + } + fn init(deps: &mut OwnedDeps) { assert_ok!(permission_control::set_admin( deps.as_mut().storage, @@ -867,16 +928,14 @@ mod tests { for chain_name in [SOLANA, ETHEREUM, XRPL] { let chain = ChainNameRaw::try_from(chain_name).unwrap(); - assert_ok!(register_its_contract( - deps.as_mut(), - chain.clone(), - ITS_ADDRESS.to_string().try_into().unwrap(), - )); - assert_ok!(set_chain_config( - deps.as_mut(), - chain, - Uint256::one().try_into().unwrap(), - 16u8 + assert_ok!(register_chain( + deps.as_mut().storage, + msg::ChainConfig { + chain: chain.clone(), + its_edge_contract: ITS_ADDRESS.to_string().try_into().unwrap(), + max_uint: Uint256::one().try_into().unwrap(), + max_target_decimals: 16u8 + } )); } } diff --git a/contracts/interchain-token-service/src/msg.rs b/contracts/interchain-token-service/src/msg.rs index b38732379..a52e55029 100644 --- a/contracts/interchain-token-service/src/msg.rs +++ b/contracts/interchain-token-service/src/msg.rs @@ -15,8 +15,6 @@ pub struct InstantiateMsg { pub admin_address: String, /// The address of the axelarnet-gateway contract on Amplifier pub axelarnet_gateway_address: String, - /// Addresses of the ITS edge contracts on connected chains - pub its_contracts: HashMap, } #[cw_serde] @@ -25,18 +23,20 @@ pub enum ExecuteMsg { /// Execute a cross-chain message received by the axelarnet-gateway from another chain #[permission(Specific(gateway))] Execute(AxelarExecutableMsg), - /// Register the ITS contract address of another chain. Each chain's ITS contract has to be whitelisted before + + /// For each chain, register the ITS contract and set config parameters. + /// Each chain's ITS contract has to be whitelisted before /// ITS Hub can send cross-chain messages to it, or receive messages from it. /// If an ITS contract is already set for the chain, an error is returned. #[permission(Governance)] - RegisterItsContract { + RegisterChains { chains: Vec }, + + /// Update the address of the ITS contract registered to the specified chain + #[permission(Governance)] + UpdateChain { chain: ChainNameRaw, - address: Address, + its_edge_contract: Address, }, - /// Deregister the ITS contract address for the given chain. - /// The admin is allowed to remove the ITS address of a chain for emergencies. - #[permission(Elevated)] - DeregisterItsContract { chain: ChainNameRaw }, /// Freeze execution of ITS messages for a particular chain #[permission(Elevated)] @@ -51,13 +51,14 @@ pub enum ExecuteMsg { #[permission(Elevated)] EnableExecution, - /// Set the chain configuration for a chain. - #[permission(Governance)] - SetChainConfig { - chain: ChainNameRaw, - max_uint: nonempty::Uint256, // The maximum uint value that is supported by the chain's token standard - max_target_decimals: u8, // The maximum number of decimals that is preserved when deploying a token to another chain where smaller uint values are used - }, +} + +#[cw_serde] +pub struct ChainConfig { + pub chain: ChainNameRaw, + pub its_edge_contract: Address, + pub max_uint: nonempty::Uint256, // The maximum uint value that is supported by the chain's token standard + pub max_target_decimals: u8, // The maximum number of decimals that is preserved when deploying a token to another chain where smaller uint values are used } #[cw_serde] diff --git a/contracts/interchain-token-service/src/state.rs b/contracts/interchain-token-service/src/state.rs index 0ded455cf..f48e973b4 100644 --- a/contracts/interchain-token-service/src/state.rs +++ b/contracts/interchain-token-service/src/state.rs @@ -2,12 +2,12 @@ use std::collections::HashMap; use axelar_wasm_std::{nonempty, FnExt, IntoContractError}; use cosmwasm_schema::cw_serde; -use cosmwasm_std::{ensure, Addr, OverflowError, StdError, Storage, Uint256}; +use cosmwasm_std::{Addr, OverflowError, StdError, Storage, Uint256}; use cw_storage_plus::{Item, Map}; use error_stack::{report, Result, ResultExt}; use router_api::{Address, ChainNameRaw}; -use crate::TokenId; +use crate::{msg, TokenId}; #[derive(thiserror::Error, Debug, IntoContractError)] pub enum Error { @@ -36,9 +36,21 @@ pub struct Config { pub struct ChainConfig { pub max_uint: nonempty::Uint256, pub max_target_decimals: u8, + pub its_address: Address, frozen: bool, } +impl From for ChainConfig { + fn from(value: msg::ChainConfig) -> Self { + Self { + max_uint: value.max_uint, + max_target_decimals: value.max_target_decimals, + its_address: value.its_edge_contract, + frozen: false, + } + } +} + #[cw_serde] pub enum TokenSupply { /// The total token supply bridged to this chain. @@ -108,7 +120,6 @@ pub struct TokenConfig { } const CONFIG: Item = Item::new("config"); -const ITS_CONTRACTS: Map<&ChainNameRaw, Address> = Map::new("its_contracts"); const CHAIN_CONFIGS: Map<&ChainNameRaw, ChainConfig> = Map::new("chain_configs"); const TOKEN_INSTANCE: Map<&(ChainNameRaw, TokenId), TokenInstance> = Map::new("token_instance"); const TOKEN_CONFIGS: Map<&TokenId, TokenConfig> = Map::new("token_configs"); @@ -144,28 +155,36 @@ pub fn load_chain_config( pub fn save_chain_config( storage: &mut dyn Storage, chain: &ChainNameRaw, - max_uint: nonempty::Uint256, - max_target_decimals: u8, + config: impl Into, ) -> Result<(), Error> { CHAIN_CONFIGS - .save( - storage, - chain, - &ChainConfig { - max_uint, - max_target_decimals, - frozen: false, - }, - ) + .save(storage, chain, &config.into()) .change_context(Error::Storage) } +pub fn update_its_contract( + storage: &mut dyn Storage, + chain: &ChainNameRaw, + its_address: Address, +) -> Result { + CHAIN_CONFIGS + .update(storage, chain, |config| match config { + Some(config) => Ok(ChainConfig { + its_address, + ..config + }), + None => Err(StdError::not_found("config not found")), + }) + .change_context(Error::ChainNotFound(chain.to_owned())) +} + pub fn may_load_its_contract( storage: &dyn Storage, chain: &ChainNameRaw, ) -> Result, Error> { - ITS_CONTRACTS + CHAIN_CONFIGS .may_load(storage, chain) + .map(|res| res.map(|config| config.its_address)) .change_context(Error::Storage) } @@ -175,38 +194,15 @@ pub fn load_its_contract(storage: &dyn Storage, chain: &ChainNameRaw) -> Result< .ok_or_else(|| report!(Error::ItsContractNotFound(chain.clone()))) } -pub fn save_its_contract( - storage: &mut dyn Storage, - chain: &ChainNameRaw, - address: &Address, -) -> Result<(), Error> { - ensure!( - may_load_its_contract(storage, chain)?.is_none(), - Error::ItsContractAlreadyRegistered(chain.clone()) - ); - - ITS_CONTRACTS - .save(storage, chain, address) - .change_context(Error::Storage) -} - -pub fn remove_its_contract(storage: &mut dyn Storage, chain: &ChainNameRaw) -> Result<(), Error> { - ensure!( - may_load_its_contract(storage, chain)?.is_some(), - Error::ItsContractNotFound(chain.clone()) - ); - - ITS_CONTRACTS.remove(storage, chain); - - Ok(()) -} - pub fn load_all_its_contracts( storage: &dyn Storage, ) -> Result, Error> { - ITS_CONTRACTS + CHAIN_CONFIGS .range(storage, None, None, cosmwasm_std::Order::Ascending) - .map(|res| res.change_context(Error::Storage)) + .map(|res| { + res.map(|(chain, config)| (chain, config.its_address)) + .change_context(Error::Storage) + }) .collect::, _>>() } @@ -324,8 +320,26 @@ mod tests { HashMap::new() ); - assert_ok!(save_its_contract(deps.as_mut().storage, &chain1, &address1)); - assert_ok!(save_its_contract(deps.as_mut().storage, &chain2, &address2)); + assert_ok!(save_chain_config( + deps.as_mut().storage, + &chain1.clone(), + msg::ChainConfig { + chain: chain1.clone(), + its_edge_contract: address1.clone(), + max_uint: Uint256::MAX.try_into().unwrap(), + max_target_decimals: 16u8 + } + )); + assert_ok!(save_chain_config( + deps.as_mut().storage, + &chain2.clone(), + msg::ChainConfig { + chain: chain2.clone(), + its_edge_contract: address2.clone(), + max_uint: Uint256::MAX.try_into().unwrap(), + max_target_decimals: 16u8 + } + )); assert_eq!( assert_ok!(load_its_contract(deps.as_ref().storage, &chain1)), address1 diff --git a/contracts/interchain-token-service/tests/execute.rs b/contracts/interchain-token-service/tests/execute.rs index 2c8b012a4..f1b9677d1 100644 --- a/contracts/interchain-token-service/tests/execute.rs +++ b/contracts/interchain-token-service/tests/execute.rs @@ -8,18 +8,20 @@ use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cosmwasm_std::{HexBinary, Uint256}; use interchain_token_service::contract::{self, ExecuteError}; use interchain_token_service::events::Event; -use interchain_token_service::msg::ExecuteMsg; +use interchain_token_service::msg::{self, ExecuteMsg}; use interchain_token_service::{ DeployInterchainToken, HubMessage, InterchainTransfer, TokenId, TokenSupply, }; use router_api::{Address, ChainName, ChainNameRaw, CrossChainId}; use serde_json::json; -use utils::{params, TestMessage}; +use utils::{params, register_chains, TestMessage}; mod utils; +use crate::contract::Error; + #[test] -fn register_deregister_its_contract_succeeds() { +fn register_update_its_contract_succeeds() { let mut deps = mock_dependencies(); utils::instantiate_contract(deps.as_mut()).unwrap(); @@ -28,24 +30,30 @@ fn register_deregister_its_contract_succeeds() { .parse() .unwrap(); - let register_response = assert_ok!(utils::register_its_contract( + assert_ok!(utils::register_chain( deps.as_mut(), chain.clone(), - address.clone() + address.clone(), + Uint256::MAX.try_into().unwrap(), + u8::MAX )); let res = assert_ok!(utils::query_its_contract(deps.as_ref(), chain.clone())); assert_eq!(res, Some(address)); - let deregister_response = - assert_ok!(utils::deregister_its_contract(deps.as_mut(), chain.clone())); + let new_address: Address = "0x9999999990123456789012345678901234567890" + .parse() + .unwrap(); + assert_ok!(utils::update_chain( + deps.as_mut(), + chain.clone(), + new_address.clone() + )); let res = assert_ok!(utils::query_its_contract(deps.as_ref(), chain.clone())); - assert_eq!(res, None); - - goldie::assert_json!([register_response, deregister_response]); + assert_eq!(res, Some(new_address)); } #[test] -fn reregistering_its_contract_fails() { +fn reregistering_same_chain_fails() { let mut deps = mock_dependencies(); utils::instantiate_contract(deps.as_mut()).unwrap(); @@ -54,30 +62,87 @@ fn reregistering_its_contract_fails() { .parse() .unwrap(); - assert_ok!(utils::register_its_contract( + assert_ok!(utils::register_chain( deps.as_mut(), chain.clone(), - address.clone() + address.clone(), + Uint256::MAX.try_into().unwrap(), + u8::MAX )); assert_err_contains!( - utils::register_its_contract(deps.as_mut(), chain, address), - ExecuteError, - ExecuteError::FailedItsContractRegistration(..) + utils::register_chain( + deps.as_mut(), + chain.clone(), + address.clone(), + Uint256::MAX.try_into().unwrap(), + u8::MAX + ), + Error, + Error::RegisterChains ); } #[test] -fn deregistering_unknown_chain_fails() { +fn update_unknown_chain_fails() { let mut deps = mock_dependencies(); utils::instantiate_contract(deps.as_mut()).unwrap(); let chain: ChainNameRaw = "ethereum".parse().unwrap(); assert_err_contains!( - utils::deregister_its_contract(deps.as_mut(), chain), - ExecuteError, - ExecuteError::FailedItsContractDeregistration(..) + utils::update_chain( + deps.as_mut(), + chain, + "0x1234567890123456789012345678901234567890" + .parse() + .unwrap() + ), + Error, + Error::UpdateChain + ); +} + +#[test] +fn register_multiple_chains_succeeds() { + let mut deps = mock_dependencies(); + utils::instantiate_contract(deps.as_mut()).unwrap(); + let chains: Vec = (0..10) + .map(|i| msg::ChainConfig { + chain: i.to_string().parse().unwrap(), + its_edge_contract: i.to_string().parse().unwrap(), + max_target_decimals: 18u8, + max_uint: Uint256::MAX.try_into().unwrap(), + }) + .collect(); + assert_ok!(register_chains(deps.as_mut(), chains.clone())); + + for chain in chains { + let res = assert_ok!(utils::query_its_contract( + deps.as_ref(), + chain.chain.clone() + )); + assert_eq!(res, Some(chain.its_edge_contract)); + } +} + +#[test] +fn register_multiple_chains_fails_if_one_invalid() { + let mut deps = mock_dependencies(); + utils::instantiate_contract(deps.as_mut()).unwrap(); + let chains: Vec = (0..10) + .map(|i| msg::ChainConfig { + chain: i.to_string().parse().unwrap(), + its_edge_contract: i.to_string().parse().unwrap(), + max_target_decimals: 18u8, + max_uint: Uint256::MAX.try_into().unwrap(), + }) + .collect(); + assert_ok!(register_chains(deps.as_mut(), chains[0..1].to_vec())); + assert_err_contains!( + register_chains(deps.as_mut(), chains.clone()), + Error, + Error::RegisterChains ); } @@ -387,7 +452,14 @@ fn execute_message_when_unknown_source_address_fails() { .. } = TestMessage::dummy(); - utils::register_its_contract(deps.as_mut(), source_its_chain, source_its_contract).unwrap(); + utils::register_chain( + deps.as_mut(), + source_its_chain, + source_its_contract, + Uint256::MAX.try_into().unwrap(), + u8::MAX, + ) + .unwrap(); let unknown_address: Address = "unknown-address".parse().unwrap(); let result = utils::execute( @@ -415,8 +487,14 @@ fn execute_message_when_invalid_payload_fails() { .. } = TestMessage::dummy(); - utils::register_its_contract(deps.as_mut(), source_its_chain, source_its_contract.clone()) - .unwrap(); + utils::register_chain( + deps.as_mut(), + source_its_chain, + source_its_contract.clone(), + Uint256::MAX.try_into().unwrap(), + u8::MAX, + ) + .unwrap(); let invalid_payload = HexBinary::from_hex("1234").unwrap(); let result = utils::execute( @@ -448,10 +526,16 @@ fn execute_message_when_unknown_chain_fails() { source_its_contract.clone(), hub_message.clone().abi_encode(), ); - assert_err_contains!(result, ExecuteError, ExecuteError::UnknownChain(chain) if chain == &source_its_chain); + assert_err_contains!(result, ExecuteError, ExecuteError::ChainNotFound(chain) if chain == &source_its_chain); - utils::register_its_contract(deps.as_mut(), source_its_chain, source_its_contract.clone()) - .unwrap(); + utils::register_chain( + deps.as_mut(), + source_its_chain, + source_its_contract.clone(), + Uint256::MAX.try_into().unwrap(), + u8::MAX, + ) + .unwrap(); let result = utils::execute( deps.as_mut(), @@ -459,7 +543,7 @@ fn execute_message_when_unknown_chain_fails() { source_its_contract, hub_message.abi_encode(), ); - assert_err_contains!(result, ExecuteError, ExecuteError::UnknownChain(chain) if chain == &destination_its_chain); + assert_err_contains!(result, ExecuteError, ExecuteError::ChainNotFound(chain) if chain == &destination_its_chain); } #[test] @@ -475,10 +559,12 @@ fn execute_message_when_invalid_message_type_fails() { .. } = TestMessage::dummy(); - utils::register_its_contract( + utils::register_chain( deps.as_mut(), source_its_chain.clone(), source_its_contract.clone(), + Uint256::MAX.try_into().unwrap(), + u8::MAX, ) .unwrap(); @@ -547,9 +633,14 @@ fn admin_or_governance_can_freeze_chain() { .unwrap(); let decimals = 18; - assert_ok!(utils::set_chain_config( + let address: Address = "0x1234567890123456789012345678901234567890" + .parse() + .unwrap(); + + assert_ok!(utils::register_chain( deps.as_mut(), chain, + address, max_uint, decimals )); @@ -585,9 +676,14 @@ fn admin_or_governance_can_unfreeze_chain() { .unwrap(); let decimals = 18; - assert_ok!(utils::set_chain_config( + let address: Address = "0x1234567890123456789012345678901234567890" + .parse() + .unwrap(); + + assert_ok!(utils::register_chain( deps.as_mut(), chain, + address, max_uint, decimals )); @@ -696,12 +792,17 @@ fn set_chain_config_should_succeed() { .unwrap(); let decimals = 18; + let address: Address = "0x1234567890123456789012345678901234567890" + .parse() + .unwrap(); + let mut deps = mock_dependencies(); utils::instantiate_contract(deps.as_mut()).unwrap(); - assert_ok!(utils::set_chain_config( + assert_ok!(utils::register_chain( deps.as_mut(), chain, + address, max_uint, decimals )); @@ -716,19 +817,24 @@ fn set_chain_config_should_fail_if_chain_config_is_already_set() { .unwrap(); let decimals = 18; + let address: Address = "0x1234567890123456789012345678901234567890" + .parse() + .unwrap(); + let mut deps = mock_dependencies(); utils::instantiate_contract(deps.as_mut()).unwrap(); - assert_ok!(utils::set_chain_config( + assert_ok!(utils::register_chain( deps.as_mut(), chain.clone(), + address.clone(), max_uint, decimals )); assert_err_contains!( - utils::set_chain_config(deps.as_mut(), chain, max_uint, decimals), + utils::register_chain(deps.as_mut(), chain, address, max_uint, decimals), ExecuteError, - ExecuteError::ChainConfigAlreadySet(_) + ExecuteError::ChainAlreadyRegistered(_) ) } @@ -1085,17 +1191,21 @@ fn deploy_interchain_token_from_non_origin_chain_fails() { // Deploy the same token from a different origin chain to a different destination chain now let another_source_chain: ChainNameRaw = "another-source-chain".parse().unwrap(); - utils::register_chain( - &mut deps, + assert_ok!(utils::register_chain( + deps.as_mut(), another_source_chain.clone(), source_its_contract.clone(), - ); + Uint256::MAX.try_into().unwrap(), + u8::MAX, + )); let another_destination_chain: ChainNameRaw = "another-dest-chain".parse().unwrap(); - utils::register_chain( - &mut deps, + assert_ok!(utils::register_chain( + deps.as_mut(), another_destination_chain.clone(), source_its_contract.clone(), - ); + Uint256::MAX.try_into().unwrap(), + u8::MAX, + )); let new_destination_msg = HubMessage::SendToHub { destination_chain: another_source_chain.clone(), @@ -1134,11 +1244,13 @@ fn deploy_interchain_token_to_multiple_destination_succeeds() { )); let another_chain: ChainNameRaw = "another-chain".parse().unwrap(); - utils::register_chain( - &mut deps, + assert_ok!(utils::register_chain( + deps.as_mut(), another_chain.clone(), source_its_contract.clone(), - ); + Uint256::MAX.try_into().unwrap(), + u8::MAX, + )); let msg = HubMessage::SendToHub { destination_chain: another_chain, diff --git a/contracts/interchain-token-service/tests/instantiate.rs b/contracts/interchain-token-service/tests/instantiate.rs index a29bd37ee..556ffaf9f 100644 --- a/contracts/interchain-token-service/tests/instantiate.rs +++ b/contracts/interchain-token-service/tests/instantiate.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use assert_ok::assert_ok; use axelar_wasm_std::permission_control::Permission; use axelar_wasm_std::{assert_err_contains, permission_control}; @@ -22,13 +20,6 @@ fn instantiate_succeeds() { fn instantiate_with_args_succeeds() { let mut deps = mock_dependencies(); - let its_contracts = vec![ - ("ethereum".parse().unwrap(), "eth-address".parse().unwrap()), - ("optimism".parse().unwrap(), "op-address".parse().unwrap()), - ] - .into_iter() - .collect::>(); - let mut response = assert_ok!(contract::instantiate( deps.as_mut(), mock_env(), @@ -37,7 +28,6 @@ fn instantiate_with_args_succeeds() { governance_address: params::GOVERNANCE.to_string(), admin_address: params::ADMIN.to_string(), axelarnet_gateway_address: params::GATEWAY.to_string(), - its_contracts: its_contracts.clone(), }, )); @@ -67,9 +57,6 @@ fn instantiate_with_args_succeeds() { )), Permission::Governance.into() ); - - let stored_its_contracts = assert_ok!(utils::query_all_its_contracts(deps.as_ref())); - assert_eq!(stored_its_contracts, its_contracts); } #[test] @@ -79,7 +66,6 @@ fn invalid_gateway_address() { governance_address: utils::params::GOVERNANCE.to_string(), admin_address: utils::params::ADMIN.to_string(), axelarnet_gateway_address: "".to_string(), - its_contracts: Default::default(), }; assert_err_contains!( contract::instantiate(deps.as_mut(), mock_env(), mock_info("sender", &[]), msg), diff --git a/contracts/interchain-token-service/tests/query.rs b/contracts/interchain-token-service/tests/query.rs index c50c996f0..40be3d129 100644 --- a/contracts/interchain-token-service/tests/query.rs +++ b/contracts/interchain-token-service/tests/query.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use assert_ok::assert_ok; use cosmwasm_std::testing::mock_dependencies; +use cosmwasm_std::Uint256; use interchain_token_service::TokenId; use router_api::{Address, ChainNameRaw}; @@ -17,7 +18,14 @@ fn query_its_contract() { .parse() .unwrap(); - utils::register_its_contract(deps.as_mut(), chain.clone(), address.clone()).unwrap(); + utils::register_chain( + deps.as_mut(), + chain.clone(), + address.clone(), + Uint256::MAX.try_into().unwrap(), + u8::MAX, + ) + .unwrap(); let queried_address = assert_ok!(utils::query_its_contract(deps.as_ref(), chain.clone())); assert_eq!(queried_address, Some(address)); @@ -29,10 +37,17 @@ fn query_its_contract() { )); assert_eq!(queried_address, None); - assert_ok!(utils::deregister_its_contract(deps.as_mut(), chain.clone())); + let new_address: Address = "0x9999999990123456789012345678901234567890" + .parse() + .unwrap(); + assert_ok!(utils::update_chain( + deps.as_mut(), + chain.clone(), + new_address.clone() + )); let queried_address = assert_ok!(utils::query_its_contract(deps.as_ref(), chain.clone())); - assert_eq!(queried_address, None); + assert_eq!(queried_address, Some(new_address)); let non_existent_chain: ChainNameRaw = "non-existent-chain".parse().unwrap(); let queried_address = assert_ok!(utils::query_its_contract(deps.as_ref(), non_existent_chain)); @@ -62,7 +77,14 @@ fn query_all_its_contracts() { .collect::>(); for (chain, address) in its_contracts.iter() { - utils::register_its_contract(deps.as_mut(), chain.clone(), address.clone()).unwrap(); + utils::register_chain( + deps.as_mut(), + chain.clone(), + address.clone(), + Uint256::MAX.try_into().unwrap(), + u8::MAX, + ) + .unwrap(); } let queried_addresses = assert_ok!(utils::query_all_its_contracts(deps.as_ref())); diff --git a/contracts/interchain-token-service/tests/testdata/instantiate_with_args_succeeds.golden b/contracts/interchain-token-service/tests/testdata/instantiate_with_args_succeeds.golden index 50b1e2056..c9a213425 100644 --- a/contracts/interchain-token-service/tests/testdata/instantiate_with_args_succeeds.golden +++ b/contracts/interchain-token-service/tests/testdata/instantiate_with_args_succeeds.golden @@ -1,33 +1,6 @@ { "messages": [], "attributes": [], - "events": [ - { - "type": "its_contract_registered", - "attributes": [ - { - "key": "chain", - "value": "ethereum" - }, - { - "key": "address", - "value": "eth-address" - } - ] - }, - { - "type": "its_contract_registered", - "attributes": [ - { - "key": "chain", - "value": "optimism" - }, - { - "key": "address", - "value": "op-address" - } - ] - } - ], + "events": [], "data": null } \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/utils/execute.rs b/contracts/interchain-token-service/tests/utils/execute.rs index e6e15a979..0c7c4ac97 100644 --- a/contracts/interchain-token-service/tests/utils/execute.rs +++ b/contracts/interchain-token-service/tests/utils/execute.rs @@ -10,7 +10,7 @@ use cosmwasm_std::{ from_json, to_json_binary, Addr, DepsMut, HexBinary, MemoryStorage, OwnedDeps, Response, Uint256, WasmQuery, }; -use interchain_token_service::msg::ExecuteMsg; +use interchain_token_service::msg::{self, ExecuteMsg}; use interchain_token_service::{contract, HubMessage}; use router_api::{Address, ChainName, ChainNameRaw, CrossChainId}; @@ -44,49 +44,6 @@ pub fn execute_hub_message( execute(deps, cc_id, source_address, message.abi_encode()) } -pub fn register_its_contract( - deps: DepsMut, - chain: ChainNameRaw, - address: Address, -) -> Result { - contract::execute( - deps, - mock_env(), - mock_info(params::GOVERNANCE, &[]), - ExecuteMsg::RegisterItsContract { chain, address }, - ) -} - -pub fn deregister_its_contract( - deps: DepsMut, - chain: ChainNameRaw, -) -> Result { - contract::execute( - deps, - mock_env(), - mock_info(params::ADMIN, &[]), - ExecuteMsg::DeregisterItsContract { chain }, - ) -} - -pub fn set_chain_config( - deps: DepsMut, - chain: ChainNameRaw, - max_uint: nonempty::Uint256, - max_target_decimals: u8, -) -> Result { - contract::execute( - deps, - mock_env(), - mock_info(params::GOVERNANCE, &[]), - ExecuteMsg::SetChainConfig { - chain, - max_uint, - max_target_decimals, - }, - ) -} - pub fn make_deps() -> OwnedDeps> { let addr = Addr::unchecked(params::GATEWAY); let mut deps = OwnedDeps { @@ -127,18 +84,49 @@ pub fn make_deps() -> OwnedDeps>, + deps: DepsMut, chain: ChainNameRaw, - its_contract: Address, -) { - register_its_contract(deps.as_mut(), chain.clone(), its_contract).unwrap(); - set_chain_config( - deps.as_mut(), - chain, - Uint256::MAX.try_into().unwrap(), - u8::MAX, + its_edge_contract: Address, + max_uint: nonempty::Uint256, + max_target_decimals: u8, +) -> Result { + register_chains( + deps, + vec![msg::ChainConfig { + chain, + its_edge_contract, + max_uint, + max_target_decimals, + }], + ) +} + +pub fn register_chains( + deps: DepsMut, + chains: Vec, +) -> Result { + contract::execute( + deps, + mock_env(), + mock_info(params::GOVERNANCE, &[]), + ExecuteMsg::RegisterChains { chains }, + ) +} + +pub fn update_chain( + deps: DepsMut, + chain: ChainNameRaw, + its_edge_contract: Address, +) -> Result { + contract::execute( + deps, + mock_env(), + mock_info(params::GOVERNANCE, &[]), + ExecuteMsg::UpdateChain { + chain, + its_edge_contract, + }, ) - .unwrap(); } pub fn setup_with_chain_configs( @@ -161,24 +149,19 @@ pub fn setup_with_chain_configs( .. } = TestMessage::dummy(); - register_its_contract(deps.as_mut(), source_its_chain.clone(), source_its_contract).unwrap(); - set_chain_config( + register_chain( deps.as_mut(), source_its_chain, + source_its_contract, source_max_uint, source_max_target_decimals, ) .unwrap(); - register_its_contract( - deps.as_mut(), - destination_its_chain.clone(), - destination_its_contract, - ) - .unwrap(); - set_chain_config( + register_chain( deps.as_mut(), destination_its_chain, + destination_its_contract, destination_max_uint, destination_max_target_decimals, ) @@ -203,15 +186,21 @@ pub fn setup() -> ( } = TestMessage::dummy(); register_chain( - &mut deps, + deps.as_mut(), source_its_chain.clone(), source_its_contract.clone(), - ); + Uint256::MAX.try_into().unwrap(), + u8::MAX, + ) + .unwrap(); register_chain( - &mut deps, + deps.as_mut(), destination_its_chain.clone(), destination_its_contract.clone(), - ); + Uint256::MAX.try_into().unwrap(), + u8::MAX, + ) + .unwrap(); (deps, TestMessage::dummy()) } diff --git a/contracts/interchain-token-service/tests/utils/instantiate.rs b/contracts/interchain-token-service/tests/utils/instantiate.rs index e409dc675..28c811604 100644 --- a/contracts/interchain-token-service/tests/utils/instantiate.rs +++ b/contracts/interchain-token-service/tests/utils/instantiate.rs @@ -15,7 +15,6 @@ pub fn instantiate_contract(deps: DepsMut) -> Result { governance_address: params::GOVERNANCE.to_string(), admin_address: params::ADMIN.to_string(), axelarnet_gateway_address: params::GATEWAY.to_string(), - its_contracts: Default::default(), }, ) } From 6dd3b413ba2f6986ac5b5f50e0be5a9174ef9611 Mon Sep 17 00:00:00 2001 From: Sammy Date: Fri, 8 Nov 2024 13:09:14 -0500 Subject: [PATCH 12/21] refactor(interchain-token-service): create message execution interceptors (#682) --- .../src/contract/execute/interceptors.rs | 684 ++++++++++++++++++ .../contract/{execute.rs => execute/mod.rs} | 402 ++-------- .../interchain-token-service/src/state.rs | 4 +- 3 files changed, 732 insertions(+), 358 deletions(-) create mode 100644 contracts/interchain-token-service/src/contract/execute/interceptors.rs rename contracts/interchain-token-service/src/contract/{execute.rs => execute/mod.rs} (63%) diff --git a/contracts/interchain-token-service/src/contract/execute/interceptors.rs b/contracts/interchain-token-service/src/contract/execute/interceptors.rs new file mode 100644 index 000000000..247997007 --- /dev/null +++ b/contracts/interchain-token-service/src/contract/execute/interceptors.rs @@ -0,0 +1,684 @@ +use axelar_wasm_std::{nonempty, FnExt}; +use cosmwasm_std::{Storage, Uint256}; +use error_stack::{bail, ensure, report, Result, ResultExt}; +use router_api::ChainNameRaw; + +use super::Error; +use crate::state::{self, TokenDeploymentType}; +use crate::{DeployInterchainToken, InterchainTransfer, TokenConfig, TokenId, TokenInstance}; + +pub fn subtract_supply_amount( + storage: &mut dyn Storage, + chain: &ChainNameRaw, + transfer: &InterchainTransfer, +) -> Result<(), Error> { + let mut token = try_load_token_instance(storage, chain.clone(), transfer.token_id)?; + + token.supply = token + .supply + .checked_sub(transfer.amount) + .change_context_lazy(|| Error::TokenSupplyInvariantViolated { + token_id: transfer.token_id, + chain: chain.clone(), + })?; + + state::save_token_instance(storage, chain.clone(), transfer.token_id, &token) + .change_context(Error::State) +} + +pub fn add_supply_amount( + storage: &mut dyn Storage, + chain: &ChainNameRaw, + transfer: &InterchainTransfer, +) -> Result<(), Error> { + let mut token = try_load_token_instance(storage, chain.clone(), transfer.token_id)?; + + token.supply = token + .supply + .checked_add(transfer.amount) + .change_context_lazy(|| Error::TokenSupplyInvariantViolated { + token_id: transfer.token_id, + chain: chain.clone(), + })?; + + state::save_token_instance(storage, chain.clone(), transfer.token_id, &token) + .change_context(Error::State) +} + +pub fn apply_scaling_factor_to_amount( + storage: &dyn Storage, + source_chain: &ChainNameRaw, + destination_chain: &ChainNameRaw, + mut transfer: InterchainTransfer, +) -> Result { + transfer.amount = destination_amount( + storage, + source_chain, + destination_chain, + transfer.token_id, + transfer.amount, + )?; + + Ok(transfer) +} + +pub fn deploy_token_to_source_chain( + storage: &mut dyn Storage, + chain: &ChainNameRaw, + deploy_token: &DeployInterchainToken, +) -> Result<(), Error> { + match state::may_load_token_config(storage, &deploy_token.token_id) + .change_context(Error::State)? + { + Some(TokenConfig { origin_chain, .. }) => { + ensure_matching_original_deployment( + storage, + origin_chain, + chain, + deploy_token.token_id, + Some(deploy_token.decimals), + )?; + } + None => { + // Token is being deployed for the first time + let token_config = TokenConfig { + origin_chain: chain.clone(), + }; + state::save_token_config(storage, deploy_token.token_id, &token_config) + .and_then(|_| { + state::save_token_instance( + storage, + chain.clone(), + deploy_token.token_id, + &TokenInstance::new_on_origin(Some(deploy_token.decimals)), + ) + }) + .change_context(Error::State)?; + } + } + + Ok(()) +} + +pub fn deploy_token_to_destination_chain( + storage: &mut dyn Storage, + chain: &ChainNameRaw, + deploy_token: &DeployInterchainToken, +) -> Result<(), Error> { + ensure!( + state::may_load_token_instance(storage, chain.clone(), deploy_token.token_id) + .change_context(Error::State)? + .is_none(), + Error::TokenAlreadyDeployed { + token_id: deploy_token.token_id, + chain: chain.to_owned(), + } + ); + + state::save_token_instance( + storage, + chain.clone(), + deploy_token.token_id, + &TokenInstance::new(&deploy_token.deployment_type(), Some(deploy_token.decimals)), + ) + .change_context(Error::State) + .map(|_| ()) +} + +pub fn calculate_scaling_factor( + storage: &dyn Storage, + source_chain: &ChainNameRaw, + destination_chain: &ChainNameRaw, + mut deploy_token: DeployInterchainToken, +) -> Result { + deploy_token.decimals = destination_token_decimals( + storage, + source_chain, + destination_chain, + deploy_token.decimals, + )?; + + Ok(deploy_token) +} + +fn ensure_matching_original_deployment( + storage: &dyn Storage, + origin_chain: ChainNameRaw, + chain: &ChainNameRaw, + token_id: TokenId, + decimals: Option, +) -> Result<(), Error> { + ensure!( + origin_chain == *chain, + Error::TokenDeployedFromNonOriginChain { + token_id, + origin_chain: origin_chain.to_owned(), + chain: chain.clone(), + } + ); + + let token_instance = state::may_load_token_instance(storage, origin_chain.clone(), token_id) + .change_context(Error::State)? + .ok_or(report!(Error::TokenNotDeployed { + token_id, + chain: origin_chain.clone() + }))?; + ensure!( + token_instance.decimals == decimals, + Error::TokenDeployedDecimalsMismatch { + token_id, + chain: chain.clone(), + expected: token_instance.decimals, + actual: decimals + } + ); + + Ok(()) +} + +fn try_load_token_instance( + storage: &dyn Storage, + chain: ChainNameRaw, + token_id: TokenId, +) -> Result { + state::may_load_token_instance(storage, chain.clone(), token_id) + .change_context(Error::State)? + .ok_or(report!(Error::TokenNotDeployed { token_id, chain })) +} + +/// Calculates the destination token decimals. +/// +/// The destination chain's token decimals are calculated and saved as following: +/// 1) If the source chain's `max_uint` is less than or equal to the destination chain's `max_uint`, +/// the source chain's token decimals are used. +/// 2) Otherwise, the minimum of the source chain's token decimals and the source chain's +/// `max_target_decimals` is used. +fn destination_token_decimals( + storage: &dyn Storage, + source_chain: &ChainNameRaw, + destination_chain: &ChainNameRaw, + source_chain_decimals: u8, +) -> Result { + let source_chain_config = + state::load_chain_config(storage, source_chain).change_context(Error::State)?; + let destination_chain_config = + state::load_chain_config(storage, destination_chain).change_context(Error::State)?; + + if source_chain_config + .max_uint + .le(&destination_chain_config.max_uint) + { + source_chain_decimals + } else { + source_chain_config + .max_target_decimals + .min(source_chain_decimals) + } + .then(Result::Ok) +} + +/// Calculates the destination on token transfer amount. +/// +/// The amount is calculated based on the token decimals on the source and destination chains. +/// The calculation is done as following: +/// 1) `destination_amount` = `source_amount` * 10 ^ (`destination_chain_decimals` - `source_chain_decimals`) +/// 3) If new_amount is greater than the destination chain's `max_uint`, the translation +/// fails. +/// 4) If new_amount is zero, the translation fails. +fn destination_amount( + storage: &dyn Storage, + source_chain: &ChainNameRaw, + destination_chain: &ChainNameRaw, + token_id: TokenId, + source_amount: nonempty::Uint256, +) -> Result { + let source_token = try_load_token_instance(storage, source_chain.clone(), token_id)?; + let destination_token = try_load_token_instance(storage, destination_chain.clone(), token_id)?; + + let (source_decimals, destination_decimals) = + match (source_token.decimals, destination_token.decimals) { + (Some(source_decimals), Some(destination_decimals)) + if source_decimals == destination_decimals => + { + return Ok(source_amount) + } + (Some(source_decimals), Some(destination_decimals)) => { + (source_decimals, destination_decimals) + } + (None, None) => return Ok(source_amount), + _ => unreachable!( + "decimals should be set in both the source and destination, or set in neither" + ), // This should never happen + }; + let destination_max_uint = state::load_chain_config(storage, destination_chain) + .change_context(Error::State)? + .max_uint; + + // It's intentionally written in this way since the end result may still be fine even if + // 1) amount * (10 ^ (dest_chain_decimals)) overflows + // 2) amount / (10 ^ (src_chain_decimals)) is zero + let scaling_factor = Uint256::from_u128(10) + .checked_pow(source_decimals.abs_diff(destination_decimals).into()) + .change_context_lazy(|| Error::InvalidTransferAmount { + source_chain: source_chain.to_owned(), + destination_chain: destination_chain.to_owned(), + amount: source_amount, + })?; + let destination_amount = if source_decimals > destination_decimals { + source_amount + .checked_div(scaling_factor) + .expect("scaling_factor must be non-zero") + } else { + source_amount + .checked_mul(scaling_factor) + .change_context_lazy(|| Error::InvalidTransferAmount { + source_chain: source_chain.to_owned(), + destination_chain: destination_chain.to_owned(), + amount: source_amount, + })? + }; + + if destination_amount.gt(&destination_max_uint) { + bail!(Error::InvalidTransferAmount { + source_chain: source_chain.to_owned(), + destination_chain: destination_chain.to_owned(), + amount: source_amount, + }) + } + + nonempty::Uint256::try_from(destination_amount).change_context_lazy(|| { + Error::InvalidTransferAmount { + source_chain: source_chain.to_owned(), + destination_chain: destination_chain.to_owned(), + amount: source_amount, + } + }) +} + +trait DeploymentType { + fn deployment_type(&self) -> TokenDeploymentType; +} + +impl DeploymentType for DeployInterchainToken { + fn deployment_type(&self) -> TokenDeploymentType { + if self.minter.is_some() { + TokenDeploymentType::CustomMinter + } else { + TokenDeploymentType::Trustless + } + } +} + +#[cfg(test)] +mod test { + use assert_ok::assert_ok; + use axelar_wasm_std::assert_err_contains; + use cosmwasm_std::testing::MockStorage; + use cosmwasm_std::Uint256; + use router_api::ChainNameRaw; + + use super::Error; + use crate::contract::execute::interceptors; + use crate::state::{self, TokenDeploymentType}; + use crate::{msg, DeployInterchainToken, InterchainTransfer, TokenInstance}; + + #[test] + fn apply_scaling_factor_to_amount_when_source_decimals_are_bigger() { + let mut storage = MockStorage::new(); + let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); + let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); + let transfer = InterchainTransfer { + token_id: [1u8; 32].into(), + source_address: b"source_address".to_vec().try_into().unwrap(), + destination_address: b"destination_address".to_vec().try_into().unwrap(), + amount: Uint256::from(1_000_000_000_000u128).try_into().unwrap(), + data: None, + }; + + state::save_token_instance( + &mut storage, + source_chain.clone(), + transfer.token_id, + &TokenInstance::new_on_origin(Some(18)), + ) + .unwrap(); + state::save_token_instance( + &mut storage, + destination_chain.clone(), + transfer.token_id, + &TokenInstance::new(&TokenDeploymentType::Trustless, Some(12)), + ) + .unwrap(); + state::save_chain_config( + &mut storage, + &destination_chain, + msg::ChainConfig { + chain: destination_chain.clone(), + its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), + max_uint: Uint256::from(1_000_000_000u128).try_into().unwrap(), + max_target_decimals: 6, + }, + ) + .unwrap(); + + let transfer = assert_ok!(interceptors::apply_scaling_factor_to_amount( + &storage, + &source_chain, + &destination_chain, + transfer, + )); + assert_eq!( + transfer.amount, + Uint256::from(1_000_000u128).try_into().unwrap() + ); + } + + #[test] + fn apply_scaling_factor_to_amount_when_source_decimals_are_smaller() { + let mut storage = MockStorage::new(); + let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); + let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); + let transfer = InterchainTransfer { + token_id: [1u8; 32].into(), + source_address: b"source_address".to_vec().try_into().unwrap(), + destination_address: b"destination_address".to_vec().try_into().unwrap(), + amount: Uint256::from(1_000_000u128).try_into().unwrap(), + data: None, + }; + + state::save_token_instance( + &mut storage, + source_chain.clone(), + transfer.token_id, + &TokenInstance::new_on_origin(Some(12)), + ) + .unwrap(); + state::save_token_instance( + &mut storage, + destination_chain.clone(), + transfer.token_id, + &TokenInstance::new(&TokenDeploymentType::Trustless, Some(18)), + ) + .unwrap(); + state::save_chain_config( + &mut storage, + &destination_chain, + msg::ChainConfig { + chain: destination_chain.clone(), + its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), + max_uint: Uint256::from(1_000_000_000_000_000u128).try_into().unwrap(), + max_target_decimals: 6, + }, + ) + .unwrap(); + + let transfer = assert_ok!(interceptors::apply_scaling_factor_to_amount( + &storage, + &source_chain, + &destination_chain, + transfer, + )); + assert_eq!( + transfer.amount, + Uint256::from(1_000_000_000_000u128).try_into().unwrap() + ); + } + + #[test] + fn apply_scaling_factor_to_amount_when_source_decimals_are_same() { + let mut storage = MockStorage::new(); + let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); + let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); + let transfer = InterchainTransfer { + token_id: [1u8; 32].into(), + source_address: b"source_address".to_vec().try_into().unwrap(), + destination_address: b"destination_address".to_vec().try_into().unwrap(), + amount: Uint256::from(1_000_000u128).try_into().unwrap(), + data: None, + }; + + state::save_token_instance( + &mut storage, + source_chain.clone(), + transfer.token_id, + &TokenInstance::new_on_origin(Some(12)), + ) + .unwrap(); + state::save_token_instance( + &mut storage, + destination_chain.clone(), + transfer.token_id, + &TokenInstance::new(&TokenDeploymentType::Trustless, Some(12)), + ) + .unwrap(); + state::save_chain_config( + &mut storage, + &destination_chain, + msg::ChainConfig { + chain: destination_chain.clone(), + its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), + max_uint: Uint256::from(1_000_000_000_000_000u128).try_into().unwrap(), + max_target_decimals: 6, + }, + ) + .unwrap(); + + let transfer = assert_ok!(interceptors::apply_scaling_factor_to_amount( + &storage, + &source_chain, + &destination_chain, + transfer, + )); + assert_eq!( + transfer.amount, + Uint256::from(1_000_000u128).try_into().unwrap() + ); + } + + #[test] + fn apply_scaling_factor_to_amount_when_result_overflows() { + let mut storage = MockStorage::new(); + let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); + let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); + let transfer = InterchainTransfer { + token_id: [1u8; 32].into(), + source_address: b"source_address".to_vec().try_into().unwrap(), + destination_address: b"destination_address".to_vec().try_into().unwrap(), + amount: Uint256::from(1_000_000_000_000u128).try_into().unwrap(), + data: None, + }; + + state::save_token_instance( + &mut storage, + source_chain.clone(), + transfer.token_id, + &TokenInstance::new_on_origin(Some(18)), + ) + .unwrap(); + state::save_token_instance( + &mut storage, + destination_chain.clone(), + transfer.token_id, + &TokenInstance::new(&TokenDeploymentType::Trustless, Some(12)), + ) + .unwrap(); + state::save_chain_config( + &mut storage, + &destination_chain, + msg::ChainConfig { + chain: destination_chain.clone(), + its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), + max_uint: Uint256::from(100_000u128).try_into().unwrap(), + max_target_decimals: 6, + }, + ) + .unwrap(); + + assert_err_contains!( + interceptors::apply_scaling_factor_to_amount( + &storage, + &source_chain, + &destination_chain, + transfer, + ), + Error, + Error::InvalidTransferAmount { .. } + ); + } + + #[test] + fn apply_scaling_factor_to_amount_when_result_underflows() { + let mut storage = MockStorage::new(); + let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); + let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); + let transfer = InterchainTransfer { + token_id: [1u8; 32].into(), + source_address: b"source_address".to_vec().try_into().unwrap(), + destination_address: b"destination_address".to_vec().try_into().unwrap(), + amount: Uint256::from(100_000u128).try_into().unwrap(), + data: None, + }; + + state::save_token_instance( + &mut storage, + source_chain.clone(), + transfer.token_id, + &TokenInstance::new_on_origin(Some(18)), + ) + .unwrap(); + state::save_token_instance( + &mut storage, + destination_chain.clone(), + transfer.token_id, + &TokenInstance::new(&TokenDeploymentType::Trustless, Some(12)), + ) + .unwrap(); + state::save_chain_config( + &mut storage, + &destination_chain, + msg::ChainConfig { + chain: destination_chain.clone(), + its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), + max_uint: Uint256::from(100_000u128).try_into().unwrap(), + max_target_decimals: 6, + }, + ) + .unwrap(); + + assert_err_contains!( + interceptors::apply_scaling_factor_to_amount( + &storage, + &source_chain, + &destination_chain, + transfer, + ), + Error, + Error::InvalidTransferAmount { .. } + ); + } + + #[test] + fn calculate_scaling_factor_when_source_max_uint_is_bigger() { + let mut storage = MockStorage::new(); + let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); + let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); + let deploy_token = DeployInterchainToken { + token_id: [1u8; 32].into(), + name: "token".to_string().try_into().unwrap(), + symbol: "TKN".to_string().try_into().unwrap(), + decimals: 9, + minter: None, + }; + + state::save_chain_config( + &mut storage, + &source_chain, + msg::ChainConfig { + chain: source_chain.clone(), + its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), + max_uint: Uint256::from(1_000_000_000_000_000u128).try_into().unwrap(), + max_target_decimals: 6, + }, + ) + .unwrap(); + state::save_chain_config( + &mut storage, + &destination_chain, + msg::ChainConfig { + chain: destination_chain.clone(), + its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), + max_uint: Uint256::from(1_000_000_000u128).try_into().unwrap(), + max_target_decimals: 6, + }, + ) + .unwrap(); + + let deploy_token = assert_ok!(interceptors::calculate_scaling_factor( + &storage, + &source_chain, + &destination_chain, + deploy_token, + )); + assert_eq!(deploy_token.decimals, 6); + + let deploy_token = DeployInterchainToken { + token_id: [1u8; 32].into(), + name: "token".to_string().try_into().unwrap(), + symbol: "TKN".to_string().try_into().unwrap(), + decimals: 3, + minter: None, + }; + let deploy_token = assert_ok!(interceptors::calculate_scaling_factor( + &storage, + &source_chain, + &destination_chain, + deploy_token, + )); + assert_eq!(deploy_token.decimals, 3); + } + + #[test] + fn calculate_scaling_factor_when_source_max_uint_is_smaller() { + let mut storage = MockStorage::new(); + let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); + let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); + let deploy_token = DeployInterchainToken { + token_id: [1u8; 32].into(), + name: "token".to_string().try_into().unwrap(), + symbol: "TKN".to_string().try_into().unwrap(), + decimals: 9, + minter: None, + }; + + state::save_chain_config( + &mut storage, + &source_chain, + msg::ChainConfig { + chain: source_chain.clone(), + its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), + max_uint: Uint256::from(1_000_000_000u128).try_into().unwrap(), + max_target_decimals: 6, + }, + ) + .unwrap(); + state::save_chain_config( + &mut storage, + &destination_chain, + msg::ChainConfig { + chain: destination_chain.clone(), + its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), + max_uint: Uint256::from(1_000_000_000_000_000u128).try_into().unwrap(), + max_target_decimals: 6, + }, + ) + .unwrap(); + + let deploy_token = assert_ok!(interceptors::calculate_scaling_factor( + &storage, + &source_chain, + &destination_chain, + deploy_token, + )); + assert_eq!(deploy_token.decimals, 9); + } +} diff --git a/contracts/interchain-token-service/src/contract/execute.rs b/contracts/interchain-token-service/src/contract/execute/mod.rs similarity index 63% rename from contracts/interchain-token-service/src/contract/execute.rs rename to contracts/interchain-token-service/src/contract/execute/mod.rs index 1ab5ee746..502eeac08 100644 --- a/contracts/interchain-token-service/src/contract/execute.rs +++ b/contracts/interchain-token-service/src/contract/execute/mod.rs @@ -1,15 +1,14 @@ use axelar_wasm_std::{killswitch, nonempty, FnExt, IntoContractError}; -use cosmwasm_std::{DepsMut, HexBinary, QuerierWrapper, Response, Storage, Uint256}; +use cosmwasm_std::{DepsMut, HexBinary, QuerierWrapper, Response, Storage}; use error_stack::{bail, ensure, report, Result, ResultExt}; use itertools::Itertools; use router_api::{Address, ChainNameRaw, CrossChainId}; use crate::events::Event; use crate::primitives::HubMessage; -use crate::state::{self, is_chain_frozen, load_config, load_its_contract, TokenDeploymentType}; -use crate::{ - msg, DeployInterchainToken, InterchainTransfer, Message, TokenConfig, TokenId, TokenInstance, -}; +use crate::{msg, state, DeployInterchainToken, InterchainTransfer, Message, TokenId}; + +mod interceptors; #[derive(thiserror::Error, Debug, IntoContractError)] pub enum Error { @@ -107,7 +106,7 @@ fn execute_message_on_hub( destination_chain: ChainNameRaw, message: Message, ) -> Result { - let destination_address = load_its_contract(deps.storage, &destination_chain) + let destination_address = state::load_its_contract(deps.storage, &destination_chain) .change_context_lazy(|| Error::ChainNotFound(destination_chain.clone()))?; let message = apply_to_hub( @@ -151,20 +150,56 @@ fn apply_to_hub( match message { Message::InterchainTransfer(transfer) => { - apply_transfer(storage, source_chain, destination_chain, &transfer) + apply_to_transfer(storage, source_chain, destination_chain, transfer) .map(Message::InterchainTransfer)? } Message::DeployInterchainToken(deploy_token) => { - apply_token_deployment(storage, &source_chain, &destination_chain, deploy_token) + apply_to_token_deployment(storage, &source_chain, &destination_chain, deploy_token) .map(Message::DeployInterchainToken)? } } .then(Result::Ok) } +fn apply_to_transfer( + storage: &mut dyn Storage, + source_chain: ChainNameRaw, + destination_chain: ChainNameRaw, + transfer: InterchainTransfer, +) -> Result { + interceptors::subtract_supply_amount(storage, &source_chain, &transfer)?; + let transfer = interceptors::apply_scaling_factor_to_amount( + storage, + &source_chain, + &destination_chain, + transfer, + )?; + interceptors::add_supply_amount(storage, &destination_chain, &transfer)?; + + Ok(transfer) +} + +fn apply_to_token_deployment( + storage: &mut dyn Storage, + source_chain: &ChainNameRaw, + destination_chain: &ChainNameRaw, + deploy_token: DeployInterchainToken, +) -> Result { + interceptors::deploy_token_to_source_chain(storage, source_chain, &deploy_token)?; + let deploy_token = interceptors::calculate_scaling_factor( + storage, + source_chain, + destination_chain, + deploy_token, + )?; + interceptors::deploy_token_to_destination_chain(storage, destination_chain, &deploy_token)?; + + Ok(deploy_token) +} + fn ensure_chain_not_frozen(storage: &dyn Storage, chain: &ChainNameRaw) -> Result<(), Error> { ensure!( - !is_chain_frozen(storage, chain).change_context(Error::State)?, + !state::is_chain_frozen(storage, chain).change_context(Error::State)?, Error::ChainFrozen(chain.to_owned()) ); @@ -177,7 +212,7 @@ fn ensure_is_its_source_address( source_chain: &ChainNameRaw, source_address: &Address, ) -> Result<(), Error> { - let source_its_contract = load_its_contract(storage, source_chain) + let source_its_contract = state::load_its_contract(storage, source_chain) .change_context_lazy(|| Error::ChainNotFound(source_chain.clone()))?; ensure!( @@ -195,7 +230,7 @@ fn send_to_destination( destination_address: Address, payload: HexBinary, ) -> Result { - let config = load_config(storage); + let config = state::load_config(storage); let gateway: axelarnet_gateway::Client = client::ContractClient::new(querier, &config.axelarnet_gateway).into(); @@ -252,351 +287,6 @@ pub fn update_chain( Ok(Response::new()) } -/// Calculates the destination on token transfer amount. -/// -/// The amount is calculated based on the token decimals on the source and destination chains. -/// The calculation is done as following: -/// 1) `destination_amount` = `source_amount` * 10 ^ (`destination_chain_decimals` - `source_chain_decimals`) -/// 3) If new_amount is greater than the destination chain's `max_uint`, the translation -/// fails. -/// 4) If new_amount is zero, the translation fails. -fn destination_amount( - storage: &dyn Storage, - source_chain: &ChainNameRaw, - destination_chain: &ChainNameRaw, - token_id: TokenId, - source_amount: nonempty::Uint256, -) -> Result { - let source_token = try_load_token_instance(storage, source_chain.clone(), token_id)?; - let destination_token = try_load_token_instance(storage, destination_chain.clone(), token_id)?; - let (source_decimals, destination_decimals) = - match (source_token.decimals, destination_token.decimals) { - (Some(source_decimals), Some(destination_decimals)) - if source_decimals == destination_decimals => - { - return Ok(source_amount) - } - (Some(source_decimals), Some(destination_decimals)) => { - (source_decimals, destination_decimals) - } - (None, None) => return Ok(source_amount), - _ => unreachable!( - "decimals should be set in both the source and destination, or set in neither" - ), // This should never happen - }; - let destination_max_uint = state::load_chain_config(storage, destination_chain) - .change_context(Error::State)? - .max_uint; - - // It's intentionally written in this way since the end result may still be fine even if - // 1) amount * (10 ^ (dest_chain_decimals)) overflows - // 2) amount / (10 ^ (src_chain_decimals)) is zero - let scaling_factor = Uint256::from_u128(10) - .checked_pow(source_decimals.abs_diff(destination_decimals).into()) - .change_context_lazy(|| Error::InvalidTransferAmount { - source_chain: source_chain.to_owned(), - destination_chain: destination_chain.to_owned(), - amount: source_amount, - })?; - let destination_amount = if source_decimals > destination_decimals { - source_amount - .checked_div(scaling_factor) - .expect("scaling_factor must be non-zero") - } else { - source_amount - .checked_mul(scaling_factor) - .change_context_lazy(|| Error::InvalidTransferAmount { - source_chain: source_chain.to_owned(), - destination_chain: destination_chain.to_owned(), - amount: source_amount, - })? - }; - - if destination_amount.gt(&destination_max_uint) { - bail!(Error::InvalidTransferAmount { - source_chain: source_chain.to_owned(), - destination_chain: destination_chain.to_owned(), - amount: source_amount, - }) - } - - nonempty::Uint256::try_from(destination_amount).change_context_lazy(|| { - Error::InvalidTransferAmount { - source_chain: source_chain.to_owned(), - destination_chain: destination_chain.to_owned(), - amount: source_amount, - } - }) -} - -/// Calculates the destination token decimals. -/// -/// The destination chain's token decimals are calculated and saved as following: -/// 1) If the source chain's `max_uint` is less than or equal to the destination chain's `max_uint`, -/// the source chain's token decimals are used. -/// 2) Otherwise, the minimum of the source chain's token decimals and the source chain's -/// `max_target_decimals` is used. -fn destination_token_decimals( - storage: &mut dyn Storage, - source_chain: &ChainNameRaw, - destination_chain: &ChainNameRaw, - source_chain_decimals: u8, -) -> Result { - let source_chain_config = - state::load_chain_config(storage, source_chain).change_context(Error::State)?; - let destination_chain_config = - state::load_chain_config(storage, destination_chain).change_context(Error::State)?; - - if source_chain_config - .max_uint - .le(&destination_chain_config.max_uint) - { - source_chain_decimals - } else { - source_chain_config - .max_target_decimals - .min(source_chain_decimals) - } - .then(Result::Ok) -} - -fn apply_transfer( - storage: &mut dyn Storage, - source_chain: ChainNameRaw, - destination_chain: ChainNameRaw, - transfer: &InterchainTransfer, -) -> Result { - let destination_amount = destination_amount( - storage, - &source_chain, - &destination_chain, - transfer.token_id, - transfer.amount, - )?; - - subtract_amount_from_source(storage, source_chain, transfer)?; - - let destination_transfer = InterchainTransfer { - amount: destination_amount, - - ..transfer.clone() - }; - add_amount_to_destination(storage, destination_chain, &destination_transfer)?; - - Ok(destination_transfer) -} - -fn subtract_amount_from_source( - storage: &mut dyn Storage, - source_chain: ChainNameRaw, - transfer: &InterchainTransfer, -) -> Result<(), Error> { - let mut source_instance = - try_load_token_instance(storage, source_chain.clone(), transfer.token_id)?; - - source_instance.supply = source_instance - .supply - .checked_sub(transfer.amount) - .change_context_lazy(|| Error::TokenSupplyInvariantViolated { - token_id: transfer.token_id, - chain: source_chain.clone(), - })?; - - state::save_token_instance(storage, source_chain, transfer.token_id, &source_instance) - .change_context(Error::State) -} - -fn add_amount_to_destination( - storage: &mut dyn Storage, - destination_chain: ChainNameRaw, - transfer: &InterchainTransfer, -) -> Result<(), Error> { - let mut destination_instance = - try_load_token_instance(storage, destination_chain.clone(), transfer.token_id)?; - - destination_instance.supply = destination_instance - .supply - .checked_add(transfer.amount) - .change_context_lazy(|| Error::TokenSupplyInvariantViolated { - token_id: transfer.token_id, - chain: destination_chain.clone(), - })?; - - state::save_token_instance( - storage, - destination_chain, - transfer.token_id, - &destination_instance, - ) - .change_context(Error::State) -} - -fn apply_token_deployment( - storage: &mut dyn Storage, - source_chain: &ChainNameRaw, - destination_chain: &ChainNameRaw, - deploy_token: DeployInterchainToken, -) -> Result { - let destination_token_decimals = destination_token_decimals( - storage, - source_chain, - destination_chain, - deploy_token.decimals, - )?; - - save_token_instances( - storage, - source_chain, - destination_chain, - Some(deploy_token.decimals), - Some(destination_token_decimals), - deploy_token.token_id, - &deploy_token.deployment_type(), - ) - .map(|_| DeployInterchainToken { - decimals: destination_token_decimals, - - ..deploy_token - }) -} - -fn save_token_instances( - storage: &mut dyn Storage, - source_chain: &ChainNameRaw, - destination_chain: &ChainNameRaw, - source_token_decimals: Option, - destination_token_decimals: Option, - token_id: TokenId, - deployment_type: &TokenDeploymentType, -) -> Result<(), Error> { - ensure_token_not_deployed_on_destination(storage, token_id, destination_chain.clone())?; - - let token_config = - state::may_load_token_config(storage, &token_id).change_context(Error::State)?; - - if let Some(TokenConfig { origin_chain, .. }) = token_config { - ensure_matching_original_deployment( - storage, - origin_chain, - source_chain, - token_id, - source_token_decimals, - )?; - } else { - initialize_token_on_origin(storage, source_chain, token_id, source_token_decimals)?; - } - - let destination_instance = TokenInstance::new(deployment_type, destination_token_decimals); - - state::save_token_instance( - storage, - destination_chain.clone(), - token_id, - &destination_instance, - ) - .change_context(Error::State) -} - -fn ensure_matching_original_deployment( - storage: &dyn Storage, - origin_chain: ChainNameRaw, - source_chain: &ChainNameRaw, - token_id: TokenId, - source_token_decimals: Option, -) -> Result<(), Error> { - ensure!( - origin_chain == *source_chain, - Error::TokenDeployedFromNonOriginChain { - token_id, - origin_chain: origin_chain.to_owned(), - chain: source_chain.clone(), - } - ); - - let token_instance = state::may_load_token_instance(storage, origin_chain.clone(), token_id) - .change_context(Error::State)? - .ok_or(report!(Error::TokenNotDeployed { - token_id, - chain: origin_chain.clone() - }))?; - ensure!( - token_instance.decimals == source_token_decimals, - Error::TokenDeployedDecimalsMismatch { - token_id, - chain: source_chain.clone(), - expected: token_instance.decimals, - actual: source_token_decimals - } - ); - - Ok(()) -} - -fn try_load_token_instance( - storage: &dyn Storage, - chain: ChainNameRaw, - token_id: TokenId, -) -> Result { - state::may_load_token_instance(storage, chain.clone(), token_id) - .change_context(Error::State)? - .ok_or(report!(Error::TokenNotDeployed { token_id, chain })) -} - -fn initialize_token_on_origin( - storage: &mut dyn Storage, - source_chain: &ChainNameRaw, - token_id: TokenId, - decimals: Option, -) -> Result<(), Error> { - // Token is being deployed for the first time - let token_config = TokenConfig { - origin_chain: source_chain.clone(), - }; - let instance = TokenInstance::new_on_origin(decimals); - - state::save_token_config(storage, &token_id, &token_config) - .and_then(|_| { - state::save_token_instance(storage, source_chain.clone(), token_id, &instance) - }) - .change_context(Error::State)?; - Ok(()) -} - -/// Ensures that the token is not being redeployed to the same destination chain. -fn ensure_token_not_deployed_on_destination( - storage: &dyn Storage, - token_id: TokenId, - destination_chain: ChainNameRaw, -) -> Result<(), Error> { - let token_instance = - state::may_load_token_instance(storage, destination_chain.clone(), token_id) - .change_context(Error::State)?; - - ensure!( - token_instance.is_none(), - Error::TokenAlreadyDeployed { - token_id, - chain: destination_chain, - } - ); - - Ok(()) -} - -trait DeploymentType { - fn deployment_type(&self) -> TokenDeploymentType; -} - -impl DeploymentType for DeployInterchainToken { - fn deployment_type(&self) -> TokenDeploymentType { - if self.minter.is_some() { - TokenDeploymentType::CustomMinter - } else { - TokenDeploymentType::Trustless - } - } -} - #[cfg(test)] mod tests { use assert_ok::assert_ok; diff --git a/contracts/interchain-token-service/src/state.rs b/contracts/interchain-token-service/src/state.rs index f48e973b4..21c21e067 100644 --- a/contracts/interchain-token-service/src/state.rs +++ b/contracts/interchain-token-service/src/state.rs @@ -266,11 +266,11 @@ pub fn may_load_token_config( pub fn save_token_config( storage: &mut dyn Storage, - token_id: &TokenId, + token_id: TokenId, token_config: &TokenConfig, ) -> Result<(), Error> { TOKEN_CONFIGS - .save(storage, token_id, token_config) + .save(storage, &token_id, token_config) .change_context(Error::Storage) } From 19fa9788c7542ca6fdb325088254c1ce8b6e58b8 Mon Sep 17 00:00:00 2001 From: CJ Cobb <46455409+cjcobb23@users.noreply.github.com> Date: Fri, 8 Nov 2024 15:52:14 -0500 Subject: [PATCH 13/21] feat(minor-interchain-token-service): make decimals non optional (#685) --- .../src/contract/execute/interceptors.rs | 48 ++++++++----------- .../src/contract/execute/mod.rs | 4 +- .../interchain-token-service/src/state.rs | 6 +-- 3 files changed, 25 insertions(+), 33 deletions(-) diff --git a/contracts/interchain-token-service/src/contract/execute/interceptors.rs b/contracts/interchain-token-service/src/contract/execute/interceptors.rs index 247997007..beb69a0fb 100644 --- a/contracts/interchain-token-service/src/contract/execute/interceptors.rs +++ b/contracts/interchain-token-service/src/contract/execute/interceptors.rs @@ -76,7 +76,7 @@ pub fn deploy_token_to_source_chain( origin_chain, chain, deploy_token.token_id, - Some(deploy_token.decimals), + deploy_token.decimals, )?; } None => { @@ -90,7 +90,7 @@ pub fn deploy_token_to_source_chain( storage, chain.clone(), deploy_token.token_id, - &TokenInstance::new_on_origin(Some(deploy_token.decimals)), + &TokenInstance::new_on_origin(deploy_token.decimals), ) }) .change_context(Error::State)?; @@ -119,7 +119,7 @@ pub fn deploy_token_to_destination_chain( storage, chain.clone(), deploy_token.token_id, - &TokenInstance::new(&deploy_token.deployment_type(), Some(deploy_token.decimals)), + &TokenInstance::new(&deploy_token.deployment_type(), deploy_token.decimals), ) .change_context(Error::State) .map(|_| ()) @@ -146,7 +146,7 @@ fn ensure_matching_original_deployment( origin_chain: ChainNameRaw, chain: &ChainNameRaw, token_id: TokenId, - decimals: Option, + decimals: u8, ) -> Result<(), Error> { ensure!( origin_chain == *chain, @@ -236,20 +236,12 @@ fn destination_amount( let destination_token = try_load_token_instance(storage, destination_chain.clone(), token_id)?; let (source_decimals, destination_decimals) = - match (source_token.decimals, destination_token.decimals) { - (Some(source_decimals), Some(destination_decimals)) - if source_decimals == destination_decimals => - { - return Ok(source_amount) - } - (Some(source_decimals), Some(destination_decimals)) => { - (source_decimals, destination_decimals) - } - (None, None) => return Ok(source_amount), - _ => unreachable!( - "decimals should be set in both the source and destination, or set in neither" - ), // This should never happen - }; + (source_token.decimals, destination_token.decimals); + + if source_decimals == destination_decimals { + return Ok(source_amount); + } + let destination_max_uint = state::load_chain_config(storage, destination_chain) .change_context(Error::State)? .max_uint; @@ -339,14 +331,14 @@ mod test { &mut storage, source_chain.clone(), transfer.token_id, - &TokenInstance::new_on_origin(Some(18)), + &TokenInstance::new_on_origin(18), ) .unwrap(); state::save_token_instance( &mut storage, destination_chain.clone(), transfer.token_id, - &TokenInstance::new(&TokenDeploymentType::Trustless, Some(12)), + &TokenInstance::new(&TokenDeploymentType::Trustless, 12), ) .unwrap(); state::save_chain_config( @@ -390,14 +382,14 @@ mod test { &mut storage, source_chain.clone(), transfer.token_id, - &TokenInstance::new_on_origin(Some(12)), + &TokenInstance::new_on_origin(12), ) .unwrap(); state::save_token_instance( &mut storage, destination_chain.clone(), transfer.token_id, - &TokenInstance::new(&TokenDeploymentType::Trustless, Some(18)), + &TokenInstance::new(&TokenDeploymentType::Trustless, 18), ) .unwrap(); state::save_chain_config( @@ -441,14 +433,14 @@ mod test { &mut storage, source_chain.clone(), transfer.token_id, - &TokenInstance::new_on_origin(Some(12)), + &TokenInstance::new_on_origin(12), ) .unwrap(); state::save_token_instance( &mut storage, destination_chain.clone(), transfer.token_id, - &TokenInstance::new(&TokenDeploymentType::Trustless, Some(12)), + &TokenInstance::new(&TokenDeploymentType::Trustless, 12), ) .unwrap(); state::save_chain_config( @@ -492,14 +484,14 @@ mod test { &mut storage, source_chain.clone(), transfer.token_id, - &TokenInstance::new_on_origin(Some(18)), + &TokenInstance::new_on_origin(18), ) .unwrap(); state::save_token_instance( &mut storage, destination_chain.clone(), transfer.token_id, - &TokenInstance::new(&TokenDeploymentType::Trustless, Some(12)), + &TokenInstance::new(&TokenDeploymentType::Trustless, 12), ) .unwrap(); state::save_chain_config( @@ -543,14 +535,14 @@ mod test { &mut storage, source_chain.clone(), transfer.token_id, - &TokenInstance::new_on_origin(Some(18)), + &TokenInstance::new_on_origin(18), ) .unwrap(); state::save_token_instance( &mut storage, destination_chain.clone(), transfer.token_id, - &TokenInstance::new(&TokenDeploymentType::Trustless, Some(12)), + &TokenInstance::new(&TokenDeploymentType::Trustless, 12), ) .unwrap(); state::save_chain_config( diff --git a/contracts/interchain-token-service/src/contract/execute/mod.rs b/contracts/interchain-token-service/src/contract/execute/mod.rs index 502eeac08..ed862e331 100644 --- a/contracts/interchain-token-service/src/contract/execute/mod.rs +++ b/contracts/interchain-token-service/src/contract/execute/mod.rs @@ -64,8 +64,8 @@ pub enum Error { TokenDeployedDecimalsMismatch { token_id: TokenId, chain: ChainNameRaw, - expected: Option, - actual: Option, + expected: u8, + actual: u8, }, #[error("token supply invariant violated for token {token_id} on chain {chain}")] TokenSupplyInvariantViolated { diff --git a/contracts/interchain-token-service/src/state.rs b/contracts/interchain-token-service/src/state.rs index 21c21e067..692319850 100644 --- a/contracts/interchain-token-service/src/state.rs +++ b/contracts/interchain-token-service/src/state.rs @@ -85,18 +85,18 @@ impl TokenSupply { #[cw_serde] pub struct TokenInstance { pub supply: TokenSupply, - pub decimals: Option, + pub decimals: u8, } impl TokenInstance { - pub fn new_on_origin(decimals: Option) -> Self { + pub fn new_on_origin(decimals: u8) -> Self { Self { supply: TokenSupply::Untracked, decimals, } } - pub fn new(deployment_type: &TokenDeploymentType, decimals: Option) -> Self { + pub fn new(deployment_type: &TokenDeploymentType, decimals: u8) -> Self { let supply = match deployment_type { TokenDeploymentType::Trustless => TokenSupply::Tracked(Uint256::zero()), _ => TokenSupply::Untracked, From 421b1eaccdef49878fd2b303f690ca2cbbcd9472 Mon Sep 17 00:00:00 2001 From: CJ Cobb <46455409+cjcobb23@users.noreply.github.com> Date: Mon, 11 Nov 2024 22:19:56 -0500 Subject: [PATCH 14/21] feat(minor-interchain-token-service): fix amount scaling (#686) --- .../src/contract/execute/interceptors.rs | 113 ++++++++++++------ .../src/contract/execute/mod.rs | 31 +++-- contracts/interchain-token-service/src/msg.rs | 7 +- .../interchain-token-service/src/state.rs | 27 +++-- .../interchain-token-service/tests/execute.rs | 14 ++- .../tests/utils/execute.rs | 10 +- 6 files changed, 136 insertions(+), 66 deletions(-) diff --git a/contracts/interchain-token-service/src/contract/execute/interceptors.rs b/contracts/interchain-token-service/src/contract/execute/interceptors.rs index beb69a0fb..c0f70726d 100644 --- a/contracts/interchain-token-service/src/contract/execute/interceptors.rs +++ b/contracts/interchain-token-service/src/contract/execute/interceptors.rs @@ -197,7 +197,7 @@ fn destination_token_decimals( storage: &dyn Storage, source_chain: &ChainNameRaw, destination_chain: &ChainNameRaw, - source_chain_decimals: u8, + source_token_decimals: u8, ) -> Result { let source_chain_config = state::load_chain_config(storage, source_chain).change_context(Error::State)?; @@ -205,14 +205,16 @@ fn destination_token_decimals( state::load_chain_config(storage, destination_chain).change_context(Error::State)?; if source_chain_config + .truncation .max_uint - .le(&destination_chain_config.max_uint) + .le(&destination_chain_config.truncation.max_uint) { - source_chain_decimals + source_token_decimals } else { - source_chain_config - .max_target_decimals - .min(source_chain_decimals) + destination_chain_config + .truncation + .max_decimals_when_truncating + .min(source_token_decimals) } .then(Result::Ok) } @@ -244,6 +246,7 @@ fn destination_amount( let destination_max_uint = state::load_chain_config(storage, destination_chain) .change_context(Error::State)? + .truncation .max_uint; // It's intentionally written in this way since the end result may still be fine even if @@ -311,6 +314,7 @@ mod test { use super::Error; use crate::contract::execute::interceptors; + use crate::msg::TruncationConfig; use crate::state::{self, TokenDeploymentType}; use crate::{msg, DeployInterchainToken, InterchainTransfer, TokenInstance}; @@ -347,8 +351,10 @@ mod test { msg::ChainConfig { chain: destination_chain.clone(), its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), - max_uint: Uint256::from(1_000_000_000u128).try_into().unwrap(), - max_target_decimals: 6, + truncation: TruncationConfig { + max_uint: Uint256::from(1_000_000_000u128).try_into().unwrap(), + max_decimals_when_truncating: 6, + }, }, ) .unwrap(); @@ -398,8 +404,10 @@ mod test { msg::ChainConfig { chain: destination_chain.clone(), its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), - max_uint: Uint256::from(1_000_000_000_000_000u128).try_into().unwrap(), - max_target_decimals: 6, + truncation: TruncationConfig { + max_uint: Uint256::from(1_000_000_000_000_000u128).try_into().unwrap(), + max_decimals_when_truncating: 6, + }, }, ) .unwrap(); @@ -449,8 +457,10 @@ mod test { msg::ChainConfig { chain: destination_chain.clone(), its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), - max_uint: Uint256::from(1_000_000_000_000_000u128).try_into().unwrap(), - max_target_decimals: 6, + truncation: TruncationConfig { + max_uint: Uint256::from(1_000_000_000_000_000u128).try_into().unwrap(), + max_decimals_when_truncating: 6, + }, }, ) .unwrap(); @@ -500,8 +510,10 @@ mod test { msg::ChainConfig { chain: destination_chain.clone(), its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), - max_uint: Uint256::from(100_000u128).try_into().unwrap(), - max_target_decimals: 6, + truncation: TruncationConfig { + max_uint: Uint256::from(100_000u128).try_into().unwrap(), + max_decimals_when_truncating: 6, + }, }, ) .unwrap(); @@ -551,8 +563,10 @@ mod test { msg::ChainConfig { chain: destination_chain.clone(), its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), - max_uint: Uint256::from(100_000u128).try_into().unwrap(), - max_target_decimals: 6, + truncation: TruncationConfig { + max_uint: Uint256::from(100_000u128).try_into().unwrap(), + max_decimals_when_truncating: 6, + }, }, ) .unwrap(); @@ -574,13 +588,6 @@ mod test { let mut storage = MockStorage::new(); let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); - let deploy_token = DeployInterchainToken { - token_id: [1u8; 32].into(), - name: "token".to_string().try_into().unwrap(), - symbol: "TKN".to_string().try_into().unwrap(), - decimals: 9, - minter: None, - }; state::save_chain_config( &mut storage, @@ -588,8 +595,10 @@ mod test { msg::ChainConfig { chain: source_chain.clone(), its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), - max_uint: Uint256::from(1_000_000_000_000_000u128).try_into().unwrap(), - max_target_decimals: 6, + truncation: TruncationConfig { + max_uint: Uint256::from(1_000_000_000_000_000u128).try_into().unwrap(), + max_decimals_when_truncating: 12, + }, }, ) .unwrap(); @@ -599,11 +608,20 @@ mod test { msg::ChainConfig { chain: destination_chain.clone(), its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), - max_uint: Uint256::from(1_000_000_000u128).try_into().unwrap(), - max_target_decimals: 6, + truncation: TruncationConfig { + max_uint: Uint256::from(1_000_000_000u128).try_into().unwrap(), + max_decimals_when_truncating: 6, + }, }, ) .unwrap(); + let deploy_token = DeployInterchainToken { + token_id: [1u8; 32].into(), + name: "token".to_string().try_into().unwrap(), + symbol: "TKN".to_string().try_into().unwrap(), + decimals: 9, + minter: None, + }; let deploy_token = assert_ok!(interceptors::calculate_scaling_factor( &storage, @@ -634,13 +652,6 @@ mod test { let mut storage = MockStorage::new(); let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); - let deploy_token = DeployInterchainToken { - token_id: [1u8; 32].into(), - name: "token".to_string().try_into().unwrap(), - symbol: "TKN".to_string().try_into().unwrap(), - decimals: 9, - minter: None, - }; state::save_chain_config( &mut storage, @@ -648,8 +659,10 @@ mod test { msg::ChainConfig { chain: source_chain.clone(), its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), - max_uint: Uint256::from(1_000_000_000u128).try_into().unwrap(), - max_target_decimals: 6, + truncation: TruncationConfig { + max_uint: Uint256::from(1_000_000_000u128).try_into().unwrap(), + max_decimals_when_truncating: 6, + }, }, ) .unwrap(); @@ -659,12 +672,21 @@ mod test { msg::ChainConfig { chain: destination_chain.clone(), its_edge_contract: "itsedgecontract".to_string().try_into().unwrap(), - max_uint: Uint256::from(1_000_000_000_000_000u128).try_into().unwrap(), - max_target_decimals: 6, + truncation: TruncationConfig { + max_uint: Uint256::from(1_000_000_000_000_000u128).try_into().unwrap(), + max_decimals_when_truncating: 6, + }, }, ) .unwrap(); + let deploy_token = DeployInterchainToken { + token_id: [1u8; 32].into(), + name: "token".to_string().try_into().unwrap(), + symbol: "TKN".to_string().try_into().unwrap(), + decimals: 9, + minter: None, + }; let deploy_token = assert_ok!(interceptors::calculate_scaling_factor( &storage, &source_chain, @@ -672,5 +694,20 @@ mod test { deploy_token, )); assert_eq!(deploy_token.decimals, 9); + + let deploy_token = DeployInterchainToken { + token_id: [1u8; 32].into(), + name: "token".to_string().try_into().unwrap(), + symbol: "TKN".to_string().try_into().unwrap(), + decimals: 3, + minter: None, + }; + let deploy_token = assert_ok!(interceptors::calculate_scaling_factor( + &storage, + &source_chain, + &destination_chain, + deploy_token, + )); + assert_eq!(deploy_token.decimals, 3); } } diff --git a/contracts/interchain-token-service/src/contract/execute/mod.rs b/contracts/interchain-token-service/src/contract/execute/mod.rs index ed862e331..3bbb4b925 100644 --- a/contracts/interchain-token-service/src/contract/execute/mod.rs +++ b/contracts/interchain-token-service/src/contract/execute/mod.rs @@ -300,6 +300,7 @@ mod tests { disable_execution, enable_execution, execute_message, freeze_chain, register_chain, register_chains, unfreeze_chain, update_chain, Error, }; + use crate::msg::TruncationConfig; use crate::state::{self, Config}; use crate::{msg, DeployInterchainToken, HubMessage, InterchainTransfer}; @@ -536,8 +537,10 @@ mod tests { msg::ChainConfig { chain: SOLANA.parse().unwrap(), its_edge_contract: ITS_ADDRESS.to_string().try_into().unwrap(), - max_uint: Uint256::one().try_into().unwrap(), - max_target_decimals: 16u8 + truncation: TruncationConfig { + max_uint: Uint256::one().try_into().unwrap(), + max_decimals_when_truncating: 16u8 + } } )); assert_err_contains!( @@ -546,8 +549,10 @@ mod tests { msg::ChainConfig { chain: SOLANA.parse().unwrap(), its_edge_contract: ITS_ADDRESS.to_string().try_into().unwrap(), - max_uint: Uint256::one().try_into().unwrap(), - max_target_decimals: 16u8 + truncation: TruncationConfig { + max_uint: Uint256::one().try_into().unwrap(), + max_decimals_when_truncating: 16u8 + } } ), Error, @@ -562,14 +567,18 @@ mod tests { msg::ChainConfig { chain: SOLANA.parse().unwrap(), its_edge_contract: ITS_ADDRESS.to_string().try_into().unwrap(), - max_uint: Uint256::MAX.try_into().unwrap(), - max_target_decimals: 16u8, + truncation: TruncationConfig { + max_uint: Uint256::MAX.try_into().unwrap(), + max_decimals_when_truncating: 16u8, + }, }, msg::ChainConfig { chain: XRPL.parse().unwrap(), its_edge_contract: ITS_ADDRESS.to_string().try_into().unwrap(), - max_uint: Uint256::MAX.try_into().unwrap(), - max_target_decimals: 16u8, + truncation: TruncationConfig { + max_uint: Uint256::MAX.try_into().unwrap(), + max_decimals_when_truncating: 16u8, + }, }, ]; assert_ok!(register_chains(deps.as_mut(), chains[0..1].to_vec())); @@ -623,8 +632,10 @@ mod tests { msg::ChainConfig { chain: chain.clone(), its_edge_contract: ITS_ADDRESS.to_string().try_into().unwrap(), - max_uint: Uint256::one().try_into().unwrap(), - max_target_decimals: 16u8 + truncation: TruncationConfig { + max_uint: Uint256::one().try_into().unwrap(), + max_decimals_when_truncating: 16u8 + } } )); } diff --git a/contracts/interchain-token-service/src/msg.rs b/contracts/interchain-token-service/src/msg.rs index a52e55029..e9364019a 100644 --- a/contracts/interchain-token-service/src/msg.rs +++ b/contracts/interchain-token-service/src/msg.rs @@ -57,8 +57,13 @@ pub enum ExecuteMsg { pub struct ChainConfig { pub chain: ChainNameRaw, pub its_edge_contract: Address, + pub truncation: TruncationConfig, +} + +#[cw_serde] +pub struct TruncationConfig { pub max_uint: nonempty::Uint256, // The maximum uint value that is supported by the chain's token standard - pub max_target_decimals: u8, // The maximum number of decimals that is preserved when deploying a token to another chain where smaller uint values are used + pub max_decimals_when_truncating: u8, // The maximum number of decimals that is preserved when deploying from a chain with a larger max_uint } #[cw_serde] diff --git a/contracts/interchain-token-service/src/state.rs b/contracts/interchain-token-service/src/state.rs index 692319850..99e337f49 100644 --- a/contracts/interchain-token-service/src/state.rs +++ b/contracts/interchain-token-service/src/state.rs @@ -34,17 +34,24 @@ pub struct Config { #[cw_serde] pub struct ChainConfig { - pub max_uint: nonempty::Uint256, - pub max_target_decimals: u8, + pub truncation: TruncationConfig, pub its_address: Address, frozen: bool, } +#[cw_serde] +pub struct TruncationConfig { + pub max_uint: nonempty::Uint256, // The maximum uint value that is supported by the chain's token standard + pub max_decimals_when_truncating: u8, // The maximum number of decimals that is preserved when deploying from a chain with a larger max_uint +} + impl From for ChainConfig { fn from(value: msg::ChainConfig) -> Self { Self { - max_uint: value.max_uint, - max_target_decimals: value.max_target_decimals, + truncation: TruncationConfig { + max_uint: value.truncation.max_uint, + max_decimals_when_truncating: value.truncation.max_decimals_when_truncating, + }, its_address: value.its_edge_contract, frozen: false, } @@ -326,8 +333,10 @@ mod tests { msg::ChainConfig { chain: chain1.clone(), its_edge_contract: address1.clone(), - max_uint: Uint256::MAX.try_into().unwrap(), - max_target_decimals: 16u8 + truncation: msg::TruncationConfig { + max_uint: Uint256::MAX.try_into().unwrap(), + max_decimals_when_truncating: 16u8 + } } )); assert_ok!(save_chain_config( @@ -336,8 +345,10 @@ mod tests { msg::ChainConfig { chain: chain2.clone(), its_edge_contract: address2.clone(), - max_uint: Uint256::MAX.try_into().unwrap(), - max_target_decimals: 16u8 + truncation: msg::TruncationConfig { + max_uint: Uint256::MAX.try_into().unwrap(), + max_decimals_when_truncating: 16u8 + } } )); assert_eq!( diff --git a/contracts/interchain-token-service/tests/execute.rs b/contracts/interchain-token-service/tests/execute.rs index f1b9677d1..2ddec0568 100644 --- a/contracts/interchain-token-service/tests/execute.rs +++ b/contracts/interchain-token-service/tests/execute.rs @@ -8,7 +8,7 @@ use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cosmwasm_std::{HexBinary, Uint256}; use interchain_token_service::contract::{self, ExecuteError}; use interchain_token_service::events::Event; -use interchain_token_service::msg::{self, ExecuteMsg}; +use interchain_token_service::msg::{self, ExecuteMsg, TruncationConfig}; use interchain_token_service::{ DeployInterchainToken, HubMessage, InterchainTransfer, TokenId, TokenSupply, }; @@ -111,8 +111,10 @@ fn register_multiple_chains_succeeds() { .map(|i| msg::ChainConfig { chain: i.to_string().parse().unwrap(), its_edge_contract: i.to_string().parse().unwrap(), - max_target_decimals: 18u8, - max_uint: Uint256::MAX.try_into().unwrap(), + truncation: TruncationConfig { + max_decimals_when_truncating: 18u8, + max_uint: Uint256::MAX.try_into().unwrap(), + }, }) .collect(); assert_ok!(register_chains(deps.as_mut(), chains.clone())); @@ -134,8 +136,10 @@ fn register_multiple_chains_fails_if_one_invalid() { .map(|i| msg::ChainConfig { chain: i.to_string().parse().unwrap(), its_edge_contract: i.to_string().parse().unwrap(), - max_target_decimals: 18u8, - max_uint: Uint256::MAX.try_into().unwrap(), + truncation: TruncationConfig { + max_decimals_when_truncating: 18u8, + max_uint: Uint256::MAX.try_into().unwrap(), + }, }) .collect(); assert_ok!(register_chains(deps.as_mut(), chains[0..1].to_vec())); diff --git a/contracts/interchain-token-service/tests/utils/execute.rs b/contracts/interchain-token-service/tests/utils/execute.rs index 0c7c4ac97..7885341c6 100644 --- a/contracts/interchain-token-service/tests/utils/execute.rs +++ b/contracts/interchain-token-service/tests/utils/execute.rs @@ -10,7 +10,7 @@ use cosmwasm_std::{ from_json, to_json_binary, Addr, DepsMut, HexBinary, MemoryStorage, OwnedDeps, Response, Uint256, WasmQuery, }; -use interchain_token_service::msg::{self, ExecuteMsg}; +use interchain_token_service::msg::{self, ExecuteMsg, TruncationConfig}; use interchain_token_service::{contract, HubMessage}; use router_api::{Address, ChainName, ChainNameRaw, CrossChainId}; @@ -88,15 +88,17 @@ pub fn register_chain( chain: ChainNameRaw, its_edge_contract: Address, max_uint: nonempty::Uint256, - max_target_decimals: u8, + max_decimals_when_truncating: u8, ) -> Result { register_chains( deps, vec![msg::ChainConfig { chain, its_edge_contract, - max_uint, - max_target_decimals, + truncation: TruncationConfig { + max_uint, + max_decimals_when_truncating, + }, }], ) } From ada9e92a70e8dd6eda7595ea7cdf2eac1832f8d7 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Tue, 12 Nov 2024 12:26:15 -0500 Subject: [PATCH 15/21] fix(interchain-token-service): remove token manager type (#688) --- contracts/interchain-token-service/src/abi.rs | 10 +--------- .../interchain-token-service/src/primitives.rs | 14 -------------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/contracts/interchain-token-service/src/abi.rs b/contracts/interchain-token-service/src/abi.rs index 45fd54f2c..6e0c9911e 100644 --- a/contracts/interchain-token-service/src/abi.rs +++ b/contracts/interchain-token-service/src/abi.rs @@ -6,7 +6,7 @@ use error_stack::{bail, ensure, report, Report, ResultExt}; use router_api::ChainNameRaw; use crate::primitives::{HubMessage, Message}; -use crate::{primitives, TokenId, TokenManagerType}; +use crate::{primitives, TokenId}; // ITS Message payload types // Reference: https://github.com/axelarnetwork/interchain-token-service/blob/v1.2.4/DESIGN.md#interchain-communication-spec @@ -62,8 +62,6 @@ pub enum Error { InvalidMessageType, #[error("invalid chain name")] InvalidChainName, - #[error("invalid token manager type")] - InvalidTokenManagerType, #[error(transparent)] NonEmpty(#[from] nonempty::Error), #[error(transparent)] @@ -219,12 +217,6 @@ impl From for U256 { } } -impl From for U256 { - fn from(value: TokenManagerType) -> Self { - U256::from(value as u8) - } -} - fn into_vec(value: Option) -> std::vec::Vec { value.map(|v| v.into()).unwrap_or_default() } diff --git a/contracts/interchain-token-service/src/primitives.rs b/contracts/interchain-token-service/src/primitives.rs index 447cbb131..dbd03dcf0 100644 --- a/contracts/interchain-token-service/src/primitives.rs +++ b/contracts/interchain-token-service/src/primitives.rs @@ -4,7 +4,6 @@ use axelar_wasm_std::nonempty; use cosmwasm_schema::cw_serde; use cw_storage_plus::{Key, KeyDeserialize, Prefixer, PrimaryKey}; use router_api::ChainNameRaw; -use strum::FromRepr; /// A unique 32-byte identifier for linked cross-chain tokens across ITS contracts. #[cw_serde] @@ -21,19 +20,6 @@ impl Display for TokenId { } } -/// The supported types of token managers that can be deployed by ITS contracts. -#[cw_serde] -#[derive(Eq, Copy, FromRepr, strum::AsRefStr)] -#[repr(u8)] -pub enum TokenManagerType { - NativeInterchainToken, - MintBurnFrom, - LockUnlock, - LockUnlockFee, - MintBurn, - Gateway, -} - /// A message sent between ITS contracts to facilitate interchain transfers, token deployments, or token manager deployments. /// `Message` routed via the ITS hub get wrapped inside a [`HubMessage`] #[cw_serde] From 9f91b1e66a7340f94fa4eb1af02aedb30b5fd190 Mon Sep 17 00:00:00 2001 From: CJ Cobb <46455409+cjcobb23@users.noreply.github.com> Date: Thu, 14 Nov 2024 12:21:25 -0500 Subject: [PATCH 16/21] fix(minor-rewards): remove old migration handler (#692) --- Cargo.lock | 1 + contracts/rewards/Cargo.toml | 1 + contracts/rewards/src/contract.rs | 22 +- .../rewards/src/contract/migrations/mod.rs | 2 +- .../rewards/src/contract/migrations/v1_0_0.rs | 309 ------------------ 5 files changed, 18 insertions(+), 317 deletions(-) delete mode 100644 contracts/rewards/src/contract/migrations/v1_0_0.rs diff --git a/Cargo.lock b/Cargo.lock index a6fb746e2..cd62719cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6725,6 +6725,7 @@ dependencies = [ name = "rewards" version = "1.1.0" dependencies = [ + "assert_ok", "axelar-wasm-std", "cosmwasm-schema", "cosmwasm-std", diff --git a/contracts/rewards/Cargo.toml b/contracts/rewards/Cargo.toml index 9677eaf5d..1ef4e5591 100644 --- a/contracts/rewards/Cargo.toml +++ b/contracts/rewards/Cargo.toml @@ -46,6 +46,7 @@ serde_json = { workspace = true } thiserror = { workspace = true } [dev-dependencies] +assert_ok = { workspace = true } cw-multi-test = "0.15.1" [lints] diff --git a/contracts/rewards/src/contract.rs b/contracts/rewards/src/contract.rs index 403fee8f5..893dde175 100644 --- a/contracts/rewards/src/contract.rs +++ b/contracts/rewards/src/contract.rs @@ -18,6 +18,7 @@ mod query; const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +const BASE_VERSION: &str = "1.1.0"; #[cfg_attr(not(feature = "library"), entry_point)] pub fn migrate( @@ -25,9 +26,7 @@ pub fn migrate( _env: Env, _msg: Empty, ) -> Result { - migrations::v1_0_0::migrate(deps.storage)?; - - // any version checks should be done before here + cw2::assert_contract_version(deps.storage, CONTRACT_NAME, BASE_VERSION)?; cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; @@ -191,7 +190,8 @@ pub fn query( #[cfg(test)] mod tests { - use cosmwasm_std::testing::{mock_dependencies, mock_env}; + use assert_ok::assert_ok; + use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; use cosmwasm_std::{coins, Addr, BlockInfo, Uint128}; use cw_multi_test::{App, ContractWrapper, Executor}; use router_api::ChainName; @@ -203,9 +203,17 @@ mod tests { #[test] fn migrate_sets_contract_version() { let mut deps = mock_dependencies(); - - #[allow(deprecated)] - migrations::v1_0_0::tests::instantiate_contract(deps.as_mut(), "denom"); + let env = mock_env(); + let info = mock_info("instantiator", &[]); + assert_ok!(instantiate( + deps.as_mut(), + env, + info, + InstantiateMsg { + governance_address: "governance".to_string(), + rewards_denom: "uaxl".to_string() + } + )); migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); diff --git a/contracts/rewards/src/contract/migrations/mod.rs b/contracts/rewards/src/contract/migrations/mod.rs index 1d185e9d7..8b1378917 100644 --- a/contracts/rewards/src/contract/migrations/mod.rs +++ b/contracts/rewards/src/contract/migrations/mod.rs @@ -1 +1 @@ -pub mod v1_0_0; + diff --git a/contracts/rewards/src/contract/migrations/v1_0_0.rs b/contracts/rewards/src/contract/migrations/v1_0_0.rs deleted file mode 100644 index 4075cc492..000000000 --- a/contracts/rewards/src/contract/migrations/v1_0_0.rs +++ /dev/null @@ -1,309 +0,0 @@ -#![allow(deprecated)] - -use std::collections::HashMap; -use std::marker::PhantomData; - -use axelar_wasm_std::error::ContractError; -use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Order, Storage, Uint128}; -use cw_storage_plus::{Item, KeyDeserialize, Map}; -use router_api::ChainName; - -use crate::contract::CONTRACT_NAME; -use crate::state::{self, EpochTally, ParamsSnapshot, PoolId, TallyId}; - -const BASE_VERSION: &str = "1.0.0"; - -pub fn migrate(storage: &mut dyn Storage) -> Result<(), ContractError> { - cw2::assert_contract_version(storage, CONTRACT_NAME, BASE_VERSION)?; - - migrate_params(storage)?; - Ok(()) -} - -fn migrate_params(storage: &mut dyn Storage) -> Result<(), ContractError> { - let params = PARAMS.load(storage)?; - let funded_pools = get_all_pools(storage)?; - - for pool in funded_pools.values() { - state::save_rewards_pool( - storage, - &state::RewardsPool { - params: params.to_owned(), - id: pool.id.to_owned(), - balance: pool.balance, - }, - )?; - } - - let tallied_pools = get_pool_ids_from_tallies(storage)?; - for pool in tallied_pools { - if funded_pools.contains_key(&pool) { - continue; - } - state::save_rewards_pool( - storage, - &state::RewardsPool { - params: params.to_owned(), - id: pool, - balance: Uint128::zero(), - }, - )?; - } - PARAMS.remove(storage); - - Ok(()) -} - -const POOLS: Map = Map::new("pools"); - -const TALLIES: Map = Map::new("tallies"); - -// This will only return pools that were funded -fn get_all_pools(storage: &mut dyn Storage) -> Result, ContractError> { - POOLS - .range(storage, None, None, Order::Ascending) - .map(|res| res.map_err(|err| err.into())) - .collect::, _>>() -} - -// Pools can have active tallies without being funded, or having an object in the POOLS map -fn get_pool_ids_from_tallies(storage: &mut dyn Storage) -> Result, ContractError> { - let mut pool_ids: Vec = vec![]; - let mut lower_bound = None; - loop { - let tallies: Vec<_> = TALLIES - .range_raw(storage, lower_bound, None, Order::Ascending) - .take(1) - .collect::, _>>()?; - match tallies.as_slice() { - [] => { - break; - } - [(tally_id, _)] => { - // cw_storage_plus v1.2.0 has a known bug where certain key types are not decoded correctly - // However, treating the key as a 3 element tuple works to decode the individual fields, and then - // we can recreate the pool id - let (chain_name, contract, _) = - <(ChainName, Addr, u64)>::from_vec(tally_id.to_owned())?; - let pool_id = PoolId { - chain_name, - contract, - }; - pool_ids.push(pool_id.clone()); - lower_bound = Some(cw_storage_plus::Bound::Exclusive(( - TallyId { - pool_id, - epoch_num: u64::MAX, - }, - PhantomData, - ))); - } - _ => panic!("take yielded more than one"), - }; - } - Ok(pool_ids) -} - -#[deprecated(since = "1.0.0", note = "only used during migration")] -const PARAMS: Item = Item::new("params"); - -#[cw_serde] -#[deprecated(since = "1.0.0", note = "only used during migration")] -struct RewardsPool { - pub id: PoolId, - pub balance: Uint128, -} - -#[cfg(test)] -pub mod tests { - use std::collections::HashMap; - - use axelar_wasm_std::permission_control; - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response, Uint128}; - - use super::{RewardsPool, PARAMS, POOLS, TALLIES}; - use crate::contract::migrations::v1_0_0; - use crate::contract::CONTRACT_NAME; - use crate::msg::{InstantiateMsg, Params}; - use crate::state::{self, Config, Epoch, EpochTally, ParamsSnapshot, PoolId, TallyId, CONFIG}; - - #[test] - fn migrate_rewards_pools() { - let mut deps = mock_dependencies(); - instantiate_contract(deps.as_mut(), "denom"); - - let test_pools = vec![ - RewardsPool { - id: PoolId { - chain_name: "mock-chain".parse().unwrap(), - contract: Addr::unchecked("contract-1"), - }, - balance: Uint128::from(250u128), - }, - RewardsPool { - id: PoolId { - chain_name: "mock-chain-2".parse().unwrap(), - contract: Addr::unchecked("contract-2"), - }, - balance: Uint128::from(100u128), - }, - ]; - - for pool in &test_pools { - POOLS - .save(deps.as_mut().storage, pool.id.to_owned(), pool) - .unwrap(); - } - - let current_epoch = Epoch { - epoch_num: 10, - block_height_started: 100, - }; - let test_tally_ids = [ - TallyId { - pool_id: PoolId { - chain_name: "mock-chain".parse().unwrap(), - contract: Addr::unchecked("contract-1"), - }, - epoch_num: current_epoch.epoch_num, - }, - TallyId { - pool_id: PoolId { - chain_name: "mock-chain-3".parse().unwrap(), - contract: Addr::unchecked("contract-3"), - }, - epoch_num: current_epoch.epoch_num, - }, - TallyId { - pool_id: PoolId { - chain_name: "mock-chain-4".parse().unwrap(), - contract: Addr::unchecked("contract-4"), - }, - epoch_num: current_epoch.epoch_num, - }, - ]; - - let params = PARAMS.load(deps.as_mut().storage).unwrap(); - let mut test_tallies: Vec<(&TallyId, EpochTally)> = test_tally_ids - .iter() - .map(|tally_id| { - ( - tally_id, - EpochTally { - pool_id: tally_id.pool_id.clone(), - event_count: 1, - participation: HashMap::new(), - epoch: current_epoch.clone(), - params: params.params.clone(), - }, - ) - }) - .collect(); - let mut test_tallies_2 = test_tallies.clone(); - test_tallies_2 - .iter_mut() - .for_each(|(_, tally)| tally.epoch.epoch_num += 1); - test_tallies.append(&mut test_tallies_2); - - for tally in &test_tallies { - TALLIES - .save(deps.as_mut().storage, tally.0.to_owned(), &tally.1) - .unwrap(); - } - - v1_0_0::migrate(deps.as_mut().storage).unwrap(); - - for pool in &test_pools { - let new_pool = - state::load_rewards_pool(deps.as_mut().storage, pool.id.to_owned()).unwrap(); - assert_eq!( - new_pool, - state::RewardsPool { - id: pool.id.to_owned(), - balance: pool.balance, - params: params.clone() - } - ); - } - - for (tally_id, _) in &test_tallies { - if test_pools.iter().any(|pool| pool.id == tally_id.pool_id) { - continue; - } - let new_pool = - state::load_rewards_pool(deps.as_mut().storage, tally_id.pool_id.to_owned()) - .unwrap(); - assert_eq!( - new_pool, - state::RewardsPool { - id: tally_id.pool_id.to_owned(), - balance: Uint128::zero(), - params: params.clone() - } - ); - } - } - - #[test] - fn migrate_checks_contract_version() { - let mut deps = mock_dependencies(); - instantiate_contract(deps.as_mut(), "denom"); - cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "something wrong").unwrap(); - - assert!(v1_0_0::migrate(deps.as_mut().storage).is_err()); - - cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, v1_0_0::BASE_VERSION) - .unwrap(); - - assert!(v1_0_0::migrate(deps.as_mut().storage).is_ok()); - } - - #[deprecated(since = "0.4.0", note = "only used during migration tests")] - pub fn instantiate_contract(deps: DepsMut, denom: impl Into) { - let msg = InstantiateMsg { - governance_address: "governance".to_string(), - rewards_denom: denom.into(), - }; - instantiate(deps, mock_env(), mock_info("anyone", &[]), msg).unwrap(); - } - - #[deprecated(since = "0.4.0", note = "only used during migration tests")] - fn instantiate( - deps: DepsMut, - env: Env, - _info: MessageInfo, - msg: InstantiateMsg, - ) -> Result { - cw2::set_contract_version(deps.storage, CONTRACT_NAME, v1_0_0::BASE_VERSION)?; - - let governance = deps.api.addr_validate(&msg.governance_address)?; - permission_control::set_governance(deps.storage, &governance)?; - - CONFIG.save( - deps.storage, - &Config { - rewards_denom: msg.rewards_denom, - }, - )?; - - let params = Params { - epoch_duration: 100u64.try_into().unwrap(), - rewards_per_epoch: 1000u128.try_into().unwrap(), - participation_threshold: (1, 2).try_into().unwrap(), - }; - PARAMS.save( - deps.storage, - &ParamsSnapshot { - params, - created_at: Epoch { - epoch_num: 0, - block_height_started: env.block.height, - }, - }, - )?; - - Ok(Response::new()) - } -} From f168b205c9eea79dbcfeafc969e7b78b097edfcc Mon Sep 17 00:00:00 2001 From: CJ Cobb <46455409+cjcobb23@users.noreply.github.com> Date: Thu, 14 Nov 2024 12:28:26 -0500 Subject: [PATCH 17/21] fix(minor-voting-verifier): remove old migration handler (#693) --- contracts/voting-verifier/src/contract.rs | 6 +- .../src/contract/migrations/mod.rs | 2 +- .../src/contract/migrations/v0_5_0.rs | 266 ------------------ 3 files changed, 5 insertions(+), 269 deletions(-) delete mode 100644 contracts/voting-verifier/src/contract/migrations/v0_5_0.rs diff --git a/contracts/voting-verifier/src/contract.rs b/contracts/voting-verifier/src/contract.rs index 099f392a9..f406704ec 100644 --- a/contracts/voting-verifier/src/contract.rs +++ b/contracts/voting-verifier/src/contract.rs @@ -7,7 +7,6 @@ use cosmwasm_std::{ }; use error_stack::ResultExt; -use crate::contract::migrations::v0_5_0; use crate::error::ContractError; use crate::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; use crate::state::{Config, CONFIG}; @@ -18,6 +17,7 @@ mod query; const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +const BASE_VERSION: &str = "1.0.0"; #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( @@ -111,7 +111,9 @@ pub fn migrate( _env: Env, _msg: Empty, ) -> Result { - v0_5_0::migrate(deps.storage)?; + cw2::assert_contract_version(deps.storage, CONTRACT_NAME, BASE_VERSION)?; + + cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; Ok(Response::default()) } diff --git a/contracts/voting-verifier/src/contract/migrations/mod.rs b/contracts/voting-verifier/src/contract/migrations/mod.rs index 504cbdb5c..8b1378917 100644 --- a/contracts/voting-verifier/src/contract/migrations/mod.rs +++ b/contracts/voting-verifier/src/contract/migrations/mod.rs @@ -1 +1 @@ -pub mod v0_5_0; + diff --git a/contracts/voting-verifier/src/contract/migrations/v0_5_0.rs b/contracts/voting-verifier/src/contract/migrations/v0_5_0.rs deleted file mode 100644 index 90d1313ea..000000000 --- a/contracts/voting-verifier/src/contract/migrations/v0_5_0.rs +++ /dev/null @@ -1,266 +0,0 @@ -#![allow(deprecated)] - -use axelar_wasm_std::address::AddressFormat; -use axelar_wasm_std::error::ContractError; -use axelar_wasm_std::msg_id::MessageIdFormat; -use axelar_wasm_std::{nonempty, permission_control, MajorityThreshold}; -use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, Attribute, StdResult, Storage}; -use cw_storage_plus::Item; -use router_api::ChainName; - -use crate::contract::{CONTRACT_NAME, CONTRACT_VERSION}; -use crate::state; - -const BASE_VERSION: &str = "0.5.0"; - -pub fn migrate(storage: &mut dyn Storage) -> Result<(), ContractError> { - cw2::assert_contract_version(storage, CONTRACT_NAME, BASE_VERSION)?; - - let config = CONFIG.load(storage)?; - migrate_permission_control(storage, &config.governance)?; - migrate_config(storage, config)?; - - delete_polls(storage); - - cw2::set_contract_version(storage, CONTRACT_NAME, CONTRACT_VERSION)?; - - Ok(()) -} - -fn migrate_config(storage: &mut dyn Storage, config: Config) -> StdResult<()> { - CONFIG.remove(storage); - - let config = state::Config { - service_registry_contract: config.service_registry_contract, - service_name: config.service_name, - source_chain: config.source_chain, - rewards_contract: config.rewards_contract, - block_expiry: config - .block_expiry - .try_into() - .unwrap_or(1.try_into().expect("1 is not zero")), - confirmation_height: config.confirmation_height, - msg_id_format: config.msg_id_format, - source_gateway_address: config.source_gateway_address, - voting_threshold: config.voting_threshold, - address_format: AddressFormat::Eip55, - }; - - state::CONFIG.save(storage, &config) -} - -fn migrate_permission_control(storage: &mut dyn Storage, governance: &Addr) -> StdResult<()> { - permission_control::set_governance(storage, governance) -} - -fn delete_polls(storage: &mut dyn Storage) { - state::POLLS.clear(storage); - state::VOTES.clear(storage); - state::poll_messages().clear(storage); - state::poll_messages().clear(storage); -} - -#[cw_serde] -#[deprecated(since = "0.5.0", note = "only used during migration")] -pub struct Config { - pub governance: Addr, - pub service_registry_contract: Addr, - pub service_name: nonempty::String, - pub source_gateway_address: nonempty::String, - pub voting_threshold: MajorityThreshold, - pub block_expiry: u64, // number of blocks after which a poll expires - pub confirmation_height: u64, - pub source_chain: ChainName, - pub rewards_contract: Addr, - pub msg_id_format: MessageIdFormat, -} -impl From for Vec { - fn from(other: Config) -> Self { - vec![ - ("service_name", other.service_name.to_string()), - ( - "service_registry_contract", - other.service_registry_contract.to_string(), - ), - ( - "source_gateway_address", - other.source_gateway_address.to_string(), - ), - ( - "voting_threshold", - serde_json::to_string(&other.voting_threshold) - .expect("failed to serialize voting_threshold"), - ), - ("block_expiry", other.block_expiry.to_string()), - ("confirmation_height", other.confirmation_height.to_string()), - ] - .into_iter() - .map(Attribute::from) - .collect() - } -} -#[deprecated(since = "0.5.0", note = "only used during migration")] -pub const CONFIG: Item = Item::new("config"); - -#[cfg(test)] -mod tests { - use axelar_wasm_std::msg_id::MessageIdFormat; - use axelar_wasm_std::permission_control::Permission; - use axelar_wasm_std::{nonempty, permission_control, MajorityThreshold, Threshold}; - use cosmwasm_schema::cw_serde; - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - use cosmwasm_std::{Addr, Attribute, DepsMut, Empty, Env, Event, MessageInfo, Response}; - use router_api::ChainName; - - use crate::contract::migrations::v0_5_0; - use crate::contract::{migrate, CONTRACT_NAME, CONTRACT_VERSION}; - use crate::state; - - const GOVERNANCE: &str = "governance"; - - #[test] - fn migrate_checks_contract_version() { - let mut deps = mock_dependencies(); - instantiate_contract(deps.as_mut()); - cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "something wrong").unwrap(); - - assert!(v0_5_0::migrate(deps.as_mut().storage).is_err()); - - cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, v0_5_0::BASE_VERSION) - .unwrap(); - - assert!(v0_5_0::migrate(deps.as_mut().storage).is_ok()); - } - - #[test] - fn migrate_sets_contract_version() { - let mut deps = mock_dependencies(); - instantiate_contract(deps.as_mut()); - - migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); - - let contract_version = cw2::get_contract_version(deps.as_mut().storage).unwrap(); - assert_eq!(contract_version.contract, CONTRACT_NAME); - assert_eq!(contract_version.version, CONTRACT_VERSION); - } - - #[test] - fn config_gets_migrated() { - let mut deps = mock_dependencies(); - instantiate_contract(deps.as_mut()); - - assert!(v0_5_0::CONFIG.load(deps.as_mut().storage).is_ok()); - assert!(state::CONFIG.load(deps.as_mut().storage).is_err()); - - assert!(v0_5_0::migrate(deps.as_mut().storage).is_ok()); - - assert!(v0_5_0::CONFIG.load(deps.as_mut().storage).is_err()); - assert!(state::CONFIG.load(deps.as_mut().storage).is_ok()); - } - - #[test] - fn permission_control_gets_migrated() { - let mut deps = mock_dependencies(); - instantiate_contract(deps.as_mut()); - - assert!(v0_5_0::migrate(deps.as_mut().storage).is_ok()); - - assert!(permission_control::sender_role( - deps.as_mut().storage, - &Addr::unchecked(GOVERNANCE) - ) - .unwrap() - .contains(Permission::Governance)); - } - - #[test] - fn state_is_cleared_after_migration() { - let mut deps = mock_dependencies(); - instantiate_contract(deps.as_mut()); - - assert!(v0_5_0::migrate(deps.as_mut().storage).is_ok()); - - assert!(state::VOTES.is_empty(deps.as_ref().storage)); - assert!(state::POLLS.is_empty(deps.as_ref().storage)); - assert!(state::poll_messages().is_empty(deps.as_ref().storage)); - assert!(state::poll_verifier_sets().is_empty(deps.as_ref().storage)); - } - - fn instantiate_contract(deps: DepsMut) { - instantiate( - deps, - mock_env(), - mock_info("admin", &[]), - InstantiateMsg { - governance_address: GOVERNANCE.parse().unwrap(), - service_registry_address: "service_registry".parse().unwrap(), - service_name: "service".parse().unwrap(), - source_gateway_address: "source_gateway".parse().unwrap(), - voting_threshold: Threshold::try_from((2u64, 3u64)) - .and_then(MajorityThreshold::try_from) - .unwrap(), - block_expiry: 1, - confirmation_height: 1, - source_chain: "source-chain".parse().unwrap(), - rewards_address: "rewards".to_string(), - msg_id_format: MessageIdFormat::HexTxHashAndEventIndex, - }, - ) - .unwrap(); - } - - #[deprecated(since = "0.5.0", note = "only used to test the migration")] - fn instantiate( - deps: DepsMut, - _env: Env, - _info: MessageInfo, - msg: InstantiateMsg, - ) -> Result { - cw2::set_contract_version(deps.storage, CONTRACT_NAME, v0_5_0::BASE_VERSION)?; - - let config = v0_5_0::Config { - governance: deps.api.addr_validate(&msg.governance_address)?, - service_name: msg.service_name, - service_registry_contract: deps.api.addr_validate(&msg.service_registry_address)?, - source_gateway_address: msg.source_gateway_address, - voting_threshold: msg.voting_threshold, - block_expiry: msg.block_expiry, - confirmation_height: msg.confirmation_height, - source_chain: msg.source_chain, - rewards_contract: deps.api.addr_validate(&msg.rewards_address)?, - msg_id_format: msg.msg_id_format, - }; - v0_5_0::CONFIG.save(deps.storage, &config)?; - - Ok(Response::new() - .add_event(Event::new("instantiated").add_attributes(>::from(config)))) - } - - #[cw_serde] - #[deprecated(since = "0.5.0", note = "only used to test the migration")] - pub struct InstantiateMsg { - /// Address that can call all messages of unrestricted governance permission level, like UpdateVotingThreshold. - /// It can execute messages that bypasses verification checks to rescue the contract if it got into an otherwise unrecoverable state due to external forces. - /// On mainnet it should match the address of the Cosmos governance module. - pub governance_address: nonempty::String, - /// Service registry contract address on axelar. - pub service_registry_address: nonempty::String, - /// Name of service in the service registry for which verifiers are registered. - pub service_name: nonempty::String, - /// Axelar's gateway contract address on the source chain - pub source_gateway_address: nonempty::String, - /// Threshold of weighted votes required for voting to be considered complete for a particular message - pub voting_threshold: MajorityThreshold, - /// The number of blocks after which a poll expires - pub block_expiry: u64, - /// The number of blocks to wait for on the source chain before considering a transaction final - pub confirmation_height: u64, - /// Name of the source chain - pub source_chain: ChainName, - /// Rewards contract address on axelar. - pub rewards_address: String, - /// Format that incoming messages should use for the id field of CrossChainId - pub msg_id_format: MessageIdFormat, - } -} From a5d3d8d1d781ecfbf37bb4123aa940ee33d4afe1 Mon Sep 17 00:00:00 2001 From: CJ Cobb <46455409+cjcobb23@users.noreply.github.com> Date: Thu, 14 Nov 2024 12:32:42 -0500 Subject: [PATCH 18/21] fix(minor-service-registry): remove old migration handler (#695) --- contracts/service-registry/src/contract.rs | 3 +- .../src/contract/migrations/mod.rs | 2 +- .../src/contract/migrations/v0_4_1.rs | 117 ------------------ 3 files changed, 3 insertions(+), 119 deletions(-) delete mode 100644 contracts/service-registry/src/contract/migrations/v0_4_1.rs diff --git a/contracts/service-registry/src/contract.rs b/contracts/service-registry/src/contract.rs index 67cada62a..9731b2e35 100644 --- a/contracts/service-registry/src/contract.rs +++ b/contracts/service-registry/src/contract.rs @@ -18,6 +18,7 @@ mod query; const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +const BASE_VERSION: &str = "1.0.0"; #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( @@ -191,7 +192,7 @@ pub fn migrate( _env: Env, _msg: Empty, ) -> Result { - migrations::v0_4_1::migrate(deps.storage)?; + cw2::assert_contract_version(deps.storage, CONTRACT_NAME, BASE_VERSION)?; cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; diff --git a/contracts/service-registry/src/contract/migrations/mod.rs b/contracts/service-registry/src/contract/migrations/mod.rs index 080e953e7..8b1378917 100644 --- a/contracts/service-registry/src/contract/migrations/mod.rs +++ b/contracts/service-registry/src/contract/migrations/mod.rs @@ -1 +1 @@ -pub mod v0_4_1; + diff --git a/contracts/service-registry/src/contract/migrations/v0_4_1.rs b/contracts/service-registry/src/contract/migrations/v0_4_1.rs deleted file mode 100644 index b06e781c8..000000000 --- a/contracts/service-registry/src/contract/migrations/v0_4_1.rs +++ /dev/null @@ -1,117 +0,0 @@ -#![allow(deprecated)] -use axelar_wasm_std::error::ContractError; -use axelar_wasm_std::permission_control; -use cosmwasm_schema::cw_serde; -use cosmwasm_std::{Addr, StdResult, Storage}; -use cw_storage_plus::Item; - -use crate::contract::CONTRACT_NAME; - -const BASE_VERSION: &str = "0.4.1"; -pub fn migrate(storage: &mut dyn Storage) -> Result<(), ContractError> { - cw2::assert_contract_version(storage, CONTRACT_NAME, BASE_VERSION)?; - - let config = CONFIG.load(storage)?; - delete_config(storage); - migrate_permission_control(storage, config)?; - - Ok(()) -} - -fn migrate_permission_control(storage: &mut dyn Storage, config: Config) -> StdResult<()> { - permission_control::set_governance(storage, &config.governance) -} - -fn delete_config(storage: &mut dyn Storage) { - CONFIG.remove(storage) -} - -#[cw_serde] -#[deprecated(since = "0.4.1", note = "Only used during migrations")] -pub struct Config { - pub governance: Addr, -} - -#[deprecated(since = "0.4.1", note = "Only used during migrations")] -pub const CONFIG: Item = Item::new("config"); - -#[cfg(test)] -mod tests { - use axelar_wasm_std::permission_control; - use axelar_wasm_std::permission_control::Permission; - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response}; - - use crate::contract::migrations::v0_4_1; - use crate::contract::CONTRACT_NAME; - use crate::msg::InstantiateMsg; - - const GOVERNANCE: &str = "governance"; - - #[test] - fn migrate_checks_contract_version() { - let mut deps = mock_dependencies(); - instantiate_contract(deps.as_mut()); - - cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "something wrong").unwrap(); - - assert!(v0_4_1::migrate(deps.as_mut().storage).is_err()); - - cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, v0_4_1::BASE_VERSION) - .unwrap(); - - assert!(v0_4_1::migrate(deps.as_mut().storage).is_ok()); - } - #[test] - fn migrate_to_permission_control() { - let mut deps = mock_dependencies(); - instantiate_contract(deps.as_mut()); - - assert!(v0_4_1::migrate(deps.as_mut().storage).is_ok()); - - assert!( - permission_control::sender_role(&deps.storage, &Addr::unchecked(GOVERNANCE)) - .unwrap() - .contains(Permission::Governance) - ); - } - - #[test] - fn migrate_deletes_config() { - let mut deps = mock_dependencies(); - instantiate_contract(deps.as_mut()); - - assert!(v0_4_1::migrate(deps.as_mut().storage).is_ok()); - - assert!(v0_4_1::CONFIG.load(&deps.storage).is_err()) - } - - fn instantiate_contract(deps: DepsMut) { - instantiate( - deps, - mock_env(), - mock_info("admin", &[]), - InstantiateMsg { - governance_account: GOVERNANCE.to_string(), - }, - ) - .unwrap(); - } - #[deprecated(since = "0.4.1", note = "Only used to test the migration")] - fn instantiate( - deps: DepsMut, - _env: Env, - _info: MessageInfo, - msg: InstantiateMsg, - ) -> Result { - cw2::set_contract_version(deps.storage, CONTRACT_NAME, v0_4_1::BASE_VERSION)?; - - v0_4_1::CONFIG.save( - deps.storage, - &v0_4_1::Config { - governance: deps.api.addr_validate(&msg.governance_account)?, - }, - )?; - Ok(Response::default()) - } -} From 6eb0ed950cc9fb30f11359e8cf6b2853e27c3b1b Mon Sep 17 00:00:00 2001 From: CJ Cobb <46455409+cjcobb23@users.noreply.github.com> Date: Thu, 14 Nov 2024 12:37:15 -0500 Subject: [PATCH 19/21] chore: update changelog with migration notes (#691) --- CHANGELOG.md | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dc58dc07..3c40a1e9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,25 @@ - Change event index in message ids from u32 to u64. Emit message id from voting verifier [#666](https://github.com/axelarnetwork/axelar-amplifier/pull/666) -#### Migration Notes +#### v1.3.0 Migration Notes + +Components that need upgrading: +ampd + +Contracts that need migration: +coordinator +voting-verifier +service-registry +rewards +router +multisig-prover + +Contracts that should be redeployed (deploy fresh instance): +interchain-token-service +axelarnet-gateway (deploy with chain name "axelar") + +Contracts no longer used (no longer part of the active system): +nexus-gateway + +The voting verifier contracts must be migrated before ampd is upgraded. Existing ampd instances will continue to work even after the contract migration, but we recommend upgrading ampd. -The voting verifier contracts must be migrated before ampd is upgraded. Existing ampd instances will continue to work even after the contract migration, but we recommend upgrading ampd. \ No newline at end of file From 2561ddb5bf388cf542c57647de86b8915976dd31 Mon Sep 17 00:00:00 2001 From: Sammy Date: Thu, 14 Nov 2024 13:31:20 -0500 Subject: [PATCH 20/21] fix(minor-multisig-prover): remove old migration (#696) --- contracts/multisig-prover/src/contract.rs | 16 +- .../src/contract/migrations/mod.rs | 2 +- .../src/contract/migrations/v1_0_0.rs | 239 ------------------ 3 files changed, 15 insertions(+), 242 deletions(-) delete mode 100644 contracts/multisig-prover/src/contract/migrations/v1_0_0.rs diff --git a/contracts/multisig-prover/src/contract.rs b/contracts/multisig-prover/src/contract.rs index caa7d25b4..31f2750a7 100644 --- a/contracts/multisig-prover/src/contract.rs +++ b/contracts/multisig-prover/src/contract.rs @@ -19,6 +19,7 @@ pub const START_MULTISIG_REPLY_ID: u64 = 1; const CONTRACT_NAME: &str = env!("CARGO_PKG_NAME"); const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); +const BASE_VERSION: &str = "1.0.0"; #[cfg_attr(not(feature = "library"), entry_point)] pub fn instantiate( @@ -122,9 +123,9 @@ pub fn migrate( _env: Env, _msg: Empty, ) -> Result { - migrations::v1_0_0::migrate(deps.storage)?; - + cw2::assert_contract_version(deps.storage, CONTRACT_NAME, BASE_VERSION)?; cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; + Ok(Response::default()) } @@ -290,6 +291,17 @@ mod tests { query(deps, mock_env(), QueryMsg::CurrentVerifierSet {}).map(|res| from_json(res).unwrap()) } + #[test] + fn migrate_sets_contract_version() { + let mut deps = setup_test_case(); + + migrate(deps.as_mut(), mock_env(), Empty {}).unwrap(); + + let contract_version = cw2::get_contract_version(deps.as_mut().storage).unwrap(); + assert_eq!(contract_version.contract, CONTRACT_NAME); + assert_eq!(contract_version.version, CONTRACT_VERSION); + } + #[test] #[allow(clippy::arithmetic_side_effects)] fn test_instantiation() { diff --git a/contracts/multisig-prover/src/contract/migrations/mod.rs b/contracts/multisig-prover/src/contract/migrations/mod.rs index 1d185e9d7..8b1378917 100644 --- a/contracts/multisig-prover/src/contract/migrations/mod.rs +++ b/contracts/multisig-prover/src/contract/migrations/mod.rs @@ -1 +1 @@ -pub mod v1_0_0; + diff --git a/contracts/multisig-prover/src/contract/migrations/v1_0_0.rs b/contracts/multisig-prover/src/contract/migrations/v1_0_0.rs deleted file mode 100644 index e8dad3daa..000000000 --- a/contracts/multisig-prover/src/contract/migrations/v1_0_0.rs +++ /dev/null @@ -1,239 +0,0 @@ -#![allow(deprecated)] - -use axelar_wasm_std::error::ContractError; -use cosmwasm_std::{wasm_execute, Response, Storage}; - -use crate::contract::execute::all_active_verifiers; -use crate::contract::CONTRACT_NAME; -use crate::state::CONFIG; - -const BASE_VERSION: &str = "1.0.0"; - -pub fn migrate(storage: &mut dyn Storage) -> Result { - cw2::assert_contract_version(storage, CONTRACT_NAME, BASE_VERSION)?; - let config = CONFIG.load(storage)?; - - let verifiers = all_active_verifiers(storage)?; - - Ok(Response::new().add_message( - wasm_execute( - config.coordinator, - &coordinator::msg::ExecuteMsg::SetActiveVerifiers { verifiers }, - vec![], - ) - .map_err(ContractError::from)?, - )) -} - -#[cfg(test)] -mod tests { - use std::collections::HashSet; - - use axelar_wasm_std::{MajorityThreshold, Threshold}; - use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; - use cosmwasm_std::{ - from_json, CosmosMsg, DepsMut, Env, MessageInfo, Response, SubMsg, WasmMsg, - }; - use multisig::key::KeyType; - - use crate::contract::migrations::v1_0_0; - use crate::contract::{CONTRACT_NAME, CONTRACT_VERSION}; - use crate::encoding::Encoder; - use crate::error::ContractError; - use crate::msg::InstantiateMsg; - use crate::state::{Config, CURRENT_VERIFIER_SET, NEXT_VERIFIER_SET}; - use crate::test::test_data; - use crate::test::test_utils::COORDINATOR_ADDRESS; - - #[test] - fn migrate_checks_contract_version() { - let mut deps = mock_dependencies(); - instantiate_contract(deps.as_mut()); - cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, "something wrong").unwrap(); - - assert!(v1_0_0::migrate(deps.as_mut().storage).is_err()); - - cw2::set_contract_version(deps.as_mut().storage, CONTRACT_NAME, v1_0_0::BASE_VERSION) - .unwrap(); - - assert!(v1_0_0::migrate(deps.as_mut().storage).is_ok()); - } - - #[test] - fn migrate_sets_contract_version() { - let mut deps = mock_dependencies(); - instantiate_contract(deps.as_mut()); - - v1_0_0::migrate(deps.as_mut().storage).unwrap(); - - let contract_version = cw2::get_contract_version(deps.as_mut().storage).unwrap(); - assert_eq!(contract_version.contract, CONTRACT_NAME); - assert_eq!(contract_version.version, CONTRACT_VERSION); - } - - // returns None if the msg is not the expected type (coordinator::msg::ExecuteMsg::SetActiveVerifiers) - fn extract_verifiers_from_set_active_verifiers_msg(msg: SubMsg) -> Option> { - match msg.msg { - CosmosMsg::Wasm(WasmMsg::Execute { msg, .. }) => { - let msg: coordinator::msg::ExecuteMsg = from_json(msg).unwrap(); - match msg { - coordinator::msg::ExecuteMsg::SetActiveVerifiers { verifiers } => { - Some(verifiers) - } - _ => None, - } - } - _ => None, - } - } - - // returns None if the msg is not the expected type (WasmMsg::Execute) - fn extract_contract_address_from_wasm_msg(msg: SubMsg) -> Option { - match msg.msg { - CosmosMsg::Wasm(WasmMsg::Execute { contract_addr, .. }) => Some(contract_addr), - _ => None, - } - } - - #[test] - fn migrate_sets_active_verifiers() { - let mut deps = mock_dependencies(); - - instantiate_contract(deps.as_mut()); - CURRENT_VERIFIER_SET - .save(deps.as_mut().storage, &test_data::curr_verifier_set()) - .unwrap(); - - let res = v1_0_0::migrate(deps.as_mut().storage); - assert!(res.is_ok()); - - let msgs = res.unwrap().messages; - assert_eq!(msgs.len(), 1); - let msg = msgs[0].clone(); - - let contract_address = extract_contract_address_from_wasm_msg(msg.clone()); - assert!(contract_address.is_some()); - assert_eq!(COORDINATOR_ADDRESS, contract_address.unwrap()); - - let verifiers = extract_verifiers_from_set_active_verifiers_msg(msg); - assert!(verifiers.is_some()); - - assert_eq!( - verifiers.unwrap(), - test_data::curr_verifier_set() - .signers - .values() - .map(|signer| signer.address.to_string()) - .collect::>() - ); - } - - #[test] - fn migrate_sets_active_verifiers_when_rotation_in_progress() { - let mut deps = mock_dependencies(); - - instantiate_contract(deps.as_mut()); - CURRENT_VERIFIER_SET - .save(deps.as_mut().storage, &test_data::curr_verifier_set()) - .unwrap(); - - NEXT_VERIFIER_SET - .save(deps.as_mut().storage, &test_data::new_verifier_set()) - .unwrap(); - - let res = v1_0_0::migrate(deps.as_mut().storage); - assert!(res.is_ok()); - - let msgs = res.unwrap().messages; - assert_eq!(msgs.len(), 1); - let msg = msgs[0].clone(); - - let contract_address = extract_contract_address_from_wasm_msg(msg.clone()); - assert!(contract_address.is_some()); - assert_eq!(COORDINATOR_ADDRESS, contract_address.unwrap()); - - let verifiers = extract_verifiers_from_set_active_verifiers_msg(msg); - assert!(verifiers.is_some()); - - assert_eq!( - verifiers.unwrap(), - test_data::curr_verifier_set() - .signers - .values() - .chain(test_data::new_verifier_set().signers.values()) - .map(|signer| signer.address.to_string()) - .collect::>() - ); - } - - fn instantiate_contract(deps: DepsMut) { - instantiate( - deps, - mock_env(), - mock_info("admin", &[]), - InstantiateMsg { - admin_address: "admin".to_string(), - governance_address: "governance".to_string(), - gateway_address: "gateway".to_string(), - multisig_address: "multisig".to_string(), - coordinator_address: "coordinator".to_string(), - service_registry_address: "service_registry".to_string(), - voting_verifier_address: "voting_verifier".to_string(), - signing_threshold: Threshold::try_from((2u64, 3u64)) - .and_then(MajorityThreshold::try_from) - .unwrap(), - service_name: "service".to_string(), - chain_name: "chain".to_string(), - verifier_set_diff_threshold: 1, - encoder: Encoder::Abi, - key_type: KeyType::Ecdsa, - domain_separator: [0; 32], - }, - ) - .unwrap(); - } - - #[deprecated(since = "0.6.0", note = "only used to test the migration")] - pub fn instantiate( - deps: DepsMut, - _env: Env, - _info: MessageInfo, - msg: InstantiateMsg, - ) -> Result { - cw2::set_contract_version(deps.storage, CONTRACT_NAME, v1_0_0::BASE_VERSION)?; - - let config = make_config(&deps, msg)?; - v1_0_0::CONFIG.save(deps.storage, &config)?; - - Ok(Response::default()) - } - - fn make_config( - deps: &DepsMut, - msg: InstantiateMsg, - ) -> Result { - let gateway = deps.api.addr_validate(&msg.gateway_address)?; - let multisig = deps.api.addr_validate(&msg.multisig_address)?; - let coordinator = deps.api.addr_validate(&msg.coordinator_address)?; - let service_registry = deps.api.addr_validate(&msg.service_registry_address)?; - let voting_verifier = deps.api.addr_validate(&msg.voting_verifier_address)?; - - Ok(Config { - gateway, - multisig, - coordinator, - service_registry, - voting_verifier, - signing_threshold: msg.signing_threshold, - service_name: msg.service_name, - chain_name: msg - .chain_name - .parse() - .map_err(|_| ContractError::InvalidChainName)?, - verifier_set_diff_threshold: msg.verifier_set_diff_threshold, - encoder: msg.encoder, - key_type: msg.key_type, - domain_separator: msg.domain_separator, - }) - } -} From fc6a657333604e3a90c365e91f507ada9f7502f9 Mon Sep 17 00:00:00 2001 From: haiyizxx Date: Thu, 14 Nov 2024 14:56:27 -0500 Subject: [PATCH 21/21] refactor(ampd): switch from horizon to rpc client for Stellar (#694) --- CHANGELOG.md | 1 + Cargo.lock | 155 +++++++++++++----- ampd/Cargo.toml | 2 +- ampd/src/config.rs | 8 +- ampd/src/handlers/config.rs | 12 +- ampd/src/handlers/stellar_verify_msg.rs | 4 +- .../handlers/stellar_verify_verifier_set.rs | 4 +- ampd/src/lib.rs | 12 +- ampd/src/stellar/http_client.rs | 127 -------------- ampd/src/stellar/mod.rs | 2 +- ampd/src/stellar/rpc_client.rs | 120 ++++++++++++++ ampd/src/stellar/verifier.rs | 12 +- ampd/src/tests/config_template.toml | 4 +- 13 files changed, 259 insertions(+), 204 deletions(-) delete mode 100644 ampd/src/stellar/http_client.rs create mode 100644 ampd/src/stellar/rpc_client.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c40a1e9a..4f496032e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ [Full Changelog](https://github.com/axelarnetwork/axelar-amplifier/compare/ampd-v1.2.0..HEAD) - Change event index in message ids from u32 to u64. Emit message id from voting verifier [#666](https://github.com/axelarnetwork/axelar-amplifier/pull/666) +- Ampd switch from horizon to RPC client for Stellar verifier [#694](https://github.com/axelarnetwork/axelar-amplifier/pull/694) #### v1.3.0 Migration Notes diff --git a/Cargo.lock b/Cargo.lock index cd62719cc..8787f731c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -273,7 +273,7 @@ dependencies = [ "service-registry-api", "sha3", "stellar", - "stellar-rs", + "stellar-rpc-client", "stellar-xdr", "sui-gateway", "sui-json-rpc-types", @@ -2291,17 +2291,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "derive-getters" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6433aac097572ea8ccc60b3f2e756c661c9aeed9225cdd4d0cb119cb7ff6ba" -dependencies = [ - "proc-macro2 1.0.85", - "quote 1.0.36", - "syn 2.0.68", -] - [[package]] name = "derive_more" version = "0.99.18" @@ -3799,7 +3788,9 @@ dependencies = [ "futures-util", "http 0.2.12", "hyper 0.14.29", + "log", "rustls 0.21.12", + "rustls-native-certs 0.6.3", "tokio", "tokio-rustls 0.24.1", ] @@ -4210,11 +4201,11 @@ name = "jsonrpsee" version = "0.16.2" source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcdf8f03081a9bc38bde8#b1b300784795f6a64d0fcdf8f03081a9bc38bde8" dependencies = [ - "jsonrpsee-core", - "jsonrpsee-http-client", + "jsonrpsee-core 0.16.2", + "jsonrpsee-http-client 0.16.2", "jsonrpsee-proc-macros", "jsonrpsee-server", - "jsonrpsee-types", + "jsonrpsee-types 0.16.2", "jsonrpsee-ws-client", "tracing", ] @@ -4226,8 +4217,8 @@ source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcd dependencies = [ "futures-util", "http 0.2.12", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.16.2", + "jsonrpsee-types 0.16.2", "pin-project", "rustls-native-certs 0.6.3", "soketto", @@ -4254,7 +4245,7 @@ dependencies = [ "futures-util", "globset", "hyper 0.14.29", - "jsonrpsee-types", + "jsonrpsee-types 0.16.2", "parking_lot", "rand", "rustc-hash", @@ -4266,6 +4257,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "jsonrpsee-core" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f24ea59b037b6b9b0e2ebe2c30a3e782b56bd7c76dcc5d6d70ba55d442af56e3" +dependencies = [ + "anyhow", + "async-trait", + "beef", + "futures-util", + "hyper 0.14.29", + "jsonrpsee-types 0.20.4", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + [[package]] name = "jsonrpsee-http-client" version = "0.16.2" @@ -4274,8 +4284,8 @@ dependencies = [ "async-trait", "hyper 0.14.29", "hyper-rustls 0.23.2", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.16.2", + "jsonrpsee-types 0.16.2", "rustc-hash", "serde", "serde_json", @@ -4284,6 +4294,26 @@ dependencies = [ "tracing", ] +[[package]] +name = "jsonrpsee-http-client" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c7b9f95208927653e7965a98525e7fc641781cab89f0e27c43fa2974405683" +dependencies = [ + "async-trait", + "hyper 0.14.29", + "hyper-rustls 0.24.2", + "jsonrpsee-core 0.20.4", + "jsonrpsee-types 0.20.4", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tracing", + "url", +] + [[package]] name = "jsonrpsee-proc-macros" version = "0.16.2" @@ -4305,8 +4335,8 @@ dependencies = [ "futures-util", "http 0.2.12", "hyper 0.14.29", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.16.2", + "jsonrpsee-types 0.16.2", "serde", "serde_json", "soketto", @@ -4330,6 +4360,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "jsonrpsee-types" +version = "0.20.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3264e339143fe37ed081953842ee67bfafa99e3b91559bdded6e4abd8fc8535e" +dependencies = [ + "anyhow", + "beef", + "serde", + "serde_json", + "thiserror", + "tracing", +] + [[package]] name = "jsonrpsee-ws-client" version = "0.16.2" @@ -4337,8 +4381,8 @@ source = "git+https://github.com/wlmyng/jsonrpsee.git?rev=b1b300784795f6a64d0fcd dependencies = [ "http 0.2.12", "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.16.2", + "jsonrpsee-types 0.16.2", ] [[package]] @@ -7366,6 +7410,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-aux" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d2e8bfba469d06512e11e3311d4d051a4a387a5b42d010404fecf3200321c95" +dependencies = [ + "chrono", + "serde", + "serde_json", +] + [[package]] name = "serde-json-wasm" version = "0.5.2" @@ -7883,23 +7938,28 @@ dependencies = [ ] [[package]] -name = "stellar-rs" -version = "0.3.2" +name = "stellar-rpc-client" +version = "21.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0099336b1e8824b731cc0138bffbaefb2b9013eb4ced9098c7ee7fef282220" +checksum = "c735c8e456fa7c37f72dd2a6abc022a111b3bf9b0520d50eef091eb1336ad370" dependencies = [ - "base64 0.22.1", - "chrono", - "derive-getters", + "clap", "hex", - "lazy_static", - "reqwest 0.12.5", + "http 1.1.0", + "itertools 0.10.5", + "jsonrpsee-core 0.20.4", + "jsonrpsee-http-client 0.20.4", "serde", + "serde-aux", "serde_json", + "sha2 0.10.8", + "stellar-strkey 0.0.8", "stellar-xdr", - "stellar_rust_sdk_derive", + "termcolor", + "termcolor_output", + "thiserror", "tokio", - "url", + "tracing", ] [[package]] @@ -7940,17 +8000,6 @@ dependencies = [ "stellar-strkey 0.0.8", ] -[[package]] -name = "stellar_rust_sdk_derive" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cc686a17ad5f5a958273870bb1ace955221ef5fbfcf02d5ddb7eb0ec91ea4b" -dependencies = [ - "proc-macro2 1.0.85", - "quote 1.0.36", - "syn 2.0.68", -] - [[package]] name = "strsim" version = "0.10.0" @@ -8573,6 +8622,22 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "termcolor_output" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0363afbf20990ea53a69c03b71800480aaf90e8f49f6fd5385ecc302062895ff" +dependencies = [ + "termcolor", + "termcolor_output_impl", +] + +[[package]] +name = "termcolor_output_impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f34dde0bb841eb3762b42bdff8db11bbdbc0a3bd7b32012955f5ce1d081f86c1" + [[package]] name = "terminal_size" version = "0.3.0" diff --git a/ampd/Cargo.toml b/ampd/Cargo.toml index d8f7659d9..ac777608e 100644 --- a/ampd/Cargo.toml +++ b/ampd/Cargo.toml @@ -53,7 +53,7 @@ serde_with = "3.2.0" service-registry-api = { workspace = true } sha3 = { workspace = true } stellar = { workspace = true } -stellar-rs = "0.3.2" +stellar-rpc-client = "21.4.0" stellar-xdr = { workspace = true, features = ["serde_json"] } sui-gateway = { workspace = true } sui-json-rpc-types = { git = "https://github.com/mystenlabs/sui", tag = "mainnet-v1.26.2" } diff --git a/ampd/src/config.rs b/ampd/src/config.rs index 2ae2b9efc..eaab6ff91 100644 --- a/ampd/src/config.rs +++ b/ampd/src/config.rs @@ -126,12 +126,12 @@ mod tests { [[handlers]] type = 'StellarMsgVerifier' cosmwasm_contract = '{}' - http_url = 'http://localhost:8000' + rpc_url = 'http://localhost:7545' [[handlers]] type = 'StellarVerifierSetVerifier' cosmwasm_contract = '{}' - http_url = 'http://localhost:8000' + rpc_url = 'http://localhost:7545' ", TMAddress::random(PREFIX), TMAddress::random(PREFIX), @@ -342,13 +342,13 @@ mod tests { cosmwasm_contract: TMAddress::from( AccountId::new("axelar", &[0u8; 32]).unwrap(), ), - http_url: Url::from_str("http://127.0.0.1").unwrap(), + rpc_url: Url::from_str("http://127.0.0.1").unwrap(), }, HandlerConfig::StellarVerifierSetVerifier { cosmwasm_contract: TMAddress::from( AccountId::new("axelar", &[0u8; 32]).unwrap(), ), - http_url: Url::from_str("http://127.0.0.1").unwrap(), + rpc_url: Url::from_str("http://127.0.0.1").unwrap(), }, ], ..Config::default() diff --git a/ampd/src/handlers/config.rs b/ampd/src/handlers/config.rs index 2cd8ed6f1..c7bb9da9b 100644 --- a/ampd/src/handlers/config.rs +++ b/ampd/src/handlers/config.rs @@ -57,11 +57,11 @@ pub enum Config { }, StellarMsgVerifier { cosmwasm_contract: TMAddress, - http_url: Url, + rpc_url: Url, }, StellarVerifierSetVerifier { cosmwasm_contract: TMAddress, - http_url: Url, + rpc_url: Url, }, } @@ -275,11 +275,11 @@ mod tests { let configs = vec![ Config::StellarMsgVerifier { cosmwasm_contract: TMAddress::random(PREFIX), - http_url: "http://localhost:8080/".parse().unwrap(), + rpc_url: "http://localhost:7545/".parse().unwrap(), }, Config::StellarMsgVerifier { cosmwasm_contract: TMAddress::random(PREFIX), - http_url: "http://localhost:8080/".parse().unwrap(), + rpc_url: "http://localhost:7545/".parse().unwrap(), }, ]; @@ -292,11 +292,11 @@ mod tests { let configs = vec![ Config::StellarVerifierSetVerifier { cosmwasm_contract: TMAddress::random(PREFIX), - http_url: "http://localhost:8080/".parse().unwrap(), + rpc_url: "http://localhost:7545/".parse().unwrap(), }, Config::StellarVerifierSetVerifier { cosmwasm_contract: TMAddress::random(PREFIX), - http_url: "http://localhost:8080/".parse().unwrap(), + rpc_url: "http://localhost:7545/".parse().unwrap(), }, ]; diff --git a/ampd/src/handlers/stellar_verify_msg.rs b/ampd/src/handlers/stellar_verify_msg.rs index a4ab51ab6..29a3f1017 100644 --- a/ampd/src/handlers/stellar_verify_msg.rs +++ b/ampd/src/handlers/stellar_verify_msg.rs @@ -23,7 +23,7 @@ use voting_verifier::msg::ExecuteMsg; use crate::event_processor::EventHandler; use crate::handlers::errors::Error; use crate::handlers::errors::Error::DeserializeEvent; -use crate::stellar::http_client::Client; +use crate::stellar::rpc_client::Client; use crate::stellar::verifier::verify_message; use crate::types::TMAddress; @@ -185,7 +185,7 @@ mod tests { use super::PollStartedEvent; use crate::event_processor::EventHandler; use crate::handlers::tests::{into_structured_event, participants}; - use crate::stellar::http_client::Client; + use crate::stellar::rpc_client::Client; use crate::types::{EVMAddress, Hash, TMAddress}; use crate::PREFIX; diff --git a/ampd/src/handlers/stellar_verify_verifier_set.rs b/ampd/src/handlers/stellar_verify_verifier_set.rs index fb77e519f..847de8506 100644 --- a/ampd/src/handlers/stellar_verify_verifier_set.rs +++ b/ampd/src/handlers/stellar_verify_verifier_set.rs @@ -22,7 +22,7 @@ use voting_verifier::msg::ExecuteMsg; use crate::event_processor::EventHandler; use crate::handlers::errors::Error; use crate::handlers::errors::Error::DeserializeEvent; -use crate::stellar::http_client::Client; +use crate::stellar::rpc_client::Client; use crate::stellar::verifier::verify_verifier_set; use crate::types::TMAddress; @@ -166,7 +166,7 @@ mod tests { use super::PollStartedEvent; use crate::event_processor::EventHandler; use crate::handlers::tests::{into_structured_event, participants}; - use crate::stellar::http_client::Client; + use crate::stellar::rpc_client::Client; use crate::types::{Hash, TMAddress}; use crate::PREFIX; diff --git a/ampd/src/lib.rs b/ampd/src/lib.rs index c6b774df9..75d258504 100644 --- a/ampd/src/lib.rs +++ b/ampd/src/lib.rs @@ -358,14 +358,14 @@ where ), handlers::config::Config::StellarMsgVerifier { cosmwasm_contract, - http_url, + rpc_url, } => self.create_handler_task( "stellar-msg-verifier", handlers::stellar_verify_msg::Handler::new( verifier.clone(), cosmwasm_contract, - stellar::http_client::Client::new( - http_url.to_string().trim_end_matches('/').into(), + stellar::rpc_client::Client::new( + rpc_url.to_string().trim_end_matches('/').into(), ) .change_context(Error::Connection)?, self.block_height_monitor.latest_block_height(), @@ -374,14 +374,14 @@ where ), handlers::config::Config::StellarVerifierSetVerifier { cosmwasm_contract, - http_url, + rpc_url, } => self.create_handler_task( "stellar-verifier-set-verifier", handlers::stellar_verify_verifier_set::Handler::new( verifier.clone(), cosmwasm_contract, - stellar::http_client::Client::new( - http_url.to_string().trim_end_matches('/').into(), + stellar::rpc_client::Client::new( + rpc_url.to_string().trim_end_matches('/').into(), ) .change_context(Error::Connection)?, self.block_height_monitor.latest_block_height(), diff --git a/ampd/src/stellar/http_client.rs b/ampd/src/stellar/http_client.rs deleted file mode 100644 index 568b0304e..000000000 --- a/ampd/src/stellar/http_client.rs +++ /dev/null @@ -1,127 +0,0 @@ -use std::collections::{HashMap, HashSet}; -use std::str::FromStr; - -use error_stack::{report, Result}; -use futures::future::join_all; -use stellar_rs::horizon_client::HorizonClient; -use stellar_rs::transactions::prelude::{SingleTransactionRequest, TransactionResponse}; -use stellar_xdr::curr::{ContractEvent, Limits, ReadXdr, ScAddress, TransactionMeta, VecM}; -use thiserror::Error; -use tracing::warn; - -#[derive(Error, Debug)] -pub enum Error { - #[error("failed to create client")] - Client, - #[error("invalid tx hash")] - TxHash, -} - -/// TxResponse parses XDR encoded TransactionMeta to ContractEvent type, and only contains necessary fields for verification -#[derive(Debug)] -pub struct TxResponse { - pub transaction_hash: String, - pub source_address: ScAddress, - pub successful: bool, - pub contract_events: Option>, -} - -impl From for TxResponse { - fn from(response: TransactionResponse) -> Self { - let events = - match TransactionMeta::from_xdr_base64(response.result_meta_xdr(), Limits::none()) { - Ok(TransactionMeta::V3(data)) => match data.soroban_meta { - Some(meta) => meta.events, - None => VecM::default(), - }, - _ => VecM::default(), - }; - - Self { - transaction_hash: response.id().to_owned(), - successful: *response.successful(), - source_address: ScAddress::from_str(response.source_account()) - .expect("must convert to Stellar address"), - contract_events: Some(events), - } - } -} - -impl TxResponse { - pub fn has_failed(&self) -> bool { - !self.successful - } - - pub fn event(&self, index: u64) -> Option<&ContractEvent> { - match self.contract_events { - Some(ref events) => { - let log_index = usize::try_from(index).ok()?; - events.get(log_index) - } - None => None, - } - } - - pub fn tx_hash(&self) -> String { - self.transaction_hash.clone() - } -} - -#[cfg_attr(test, faux::create)] -pub struct Client(HorizonClient); - -#[cfg_attr(test, faux::methods)] -impl Client { - pub fn new(url: String) -> Result { - Ok(Self(HorizonClient::new(url).map_err(|err_str| { - report!(Error::Client).attach_printable(err_str) - })?)) - } - - pub async fn transaction_responses( - &self, - tx_hashes: HashSet, - ) -> Result, Error> { - let tx_hashes: Vec<_> = tx_hashes - .into_iter() - .map(|tx_hash| { - SingleTransactionRequest::new() - .set_transaction_hash(tx_hash) - .map_err(|err_str| report!(Error::TxHash).attach_printable(err_str)) - }) - .collect::, _>>()?; - - Ok(join_all( - tx_hashes - .iter() - .map(|tx_hash| self.0.get_single_transaction(tx_hash)), - ) - .await - .into_iter() - .map(|tx_response| tx_response.map(TxResponse::from)) - .filter_map(|tx_response| match tx_response { - Ok(tx_response) => Some((tx_response.tx_hash(), tx_response)), - Err(err) => { - warn!(err, "failed to get transaction response"); - None - } - }) - .collect::>()) - } - - pub async fn transaction_response(&self, tx_hash: String) -> Result, Error> { - let tx_hash = SingleTransactionRequest::new() - .set_transaction_hash(tx_hash) - .map_err(|err_str| report!(Error::TxHash).attach_printable(err_str))?; - - Ok(self - .0 - .get_single_transaction(&tx_hash) - .await - .map(|tx_response| Some(tx_response.into())) - .unwrap_or_else(|err| { - warn!(err, "failed to get transaction response"); - None - })) - } -} diff --git a/ampd/src/stellar/mod.rs b/ampd/src/stellar/mod.rs index be0cc859c..c121275bd 100644 --- a/ampd/src/stellar/mod.rs +++ b/ampd/src/stellar/mod.rs @@ -1,2 +1,2 @@ -pub(crate) mod http_client; +pub(crate) mod rpc_client; pub(crate) mod verifier; diff --git a/ampd/src/stellar/rpc_client.rs b/ampd/src/stellar/rpc_client.rs new file mode 100644 index 000000000..0b770d40f --- /dev/null +++ b/ampd/src/stellar/rpc_client.rs @@ -0,0 +1,120 @@ +use std::collections::{HashMap, HashSet}; +use std::str::FromStr; + +use error_stack::{report, ResultExt}; +use futures::future::join_all; +use stellar_rpc_client::GetTransactionResponse; +use stellar_xdr::curr::{ContractEvent, Hash, TransactionMeta, VecM}; +use thiserror::Error; +use tracing::warn; + +#[derive(Error, Debug)] +pub enum Error { + #[error("failed to create client")] + Client, + #[error("invalid tx hash")] + TxHash, +} + +/// TxResponse parses XDR encoded TransactionMeta to ContractEvent type, and only contains necessary fields for verification +#[derive(Debug)] +pub struct TxResponse { + pub transaction_hash: String, + pub successful: bool, + pub contract_events: VecM, +} + +const STATUS_SUCCESS: &str = "SUCCESS"; + +impl From<(Hash, GetTransactionResponse)> for TxResponse { + fn from((transaction_hash, response): (Hash, GetTransactionResponse)) -> Self { + let contract_events = response + .result_meta + .and_then(|meta| match meta { + TransactionMeta::V3(data) => data.soroban_meta.map(|meta| meta.events), + _ => None, + }) + .unwrap_or_default(); + + Self { + transaction_hash: transaction_hash.to_string(), + successful: response.status == STATUS_SUCCESS, + contract_events, + } + } +} + +impl TxResponse { + pub fn has_failed(&self) -> bool { + !self.successful + } + + pub fn event(&self, index: u64) -> Option<&ContractEvent> { + let log_index = usize::try_from(index).ok()?; + self.contract_events.get(log_index) + } + + pub fn tx_hash(&self) -> String { + self.transaction_hash.clone() + } +} + +#[cfg_attr(test, faux::create)] +pub struct Client(stellar_rpc_client::Client); + +#[cfg_attr(test, faux::methods)] +impl Client { + pub fn new(url: String) -> error_stack::Result { + Ok(Self( + stellar_rpc_client::Client::new(url.as_str()) + .map_err(|err_str| report!(Error::Client).attach_printable(err_str))?, + )) + } + + pub async fn transaction_responses( + &self, + tx_hashes: HashSet, + ) -> error_stack::Result, Error> { + let tx_hashes: Vec<_> = tx_hashes + .into_iter() + .map(|tx_hash| Hash::from_str(tx_hash.as_str()).change_context(Error::TxHash)) + .collect::, _>>()?; + + let responses = join_all( + tx_hashes + .iter() + .map(|tx_hash| self.0.get_transaction(tx_hash)), + ) + .await; + + Ok(responses + .into_iter() + .zip(tx_hashes) + .filter_map(|(response, hash)| match response { + Ok(resp) => { + let tx_response = TxResponse::from((hash, resp)); + Some((tx_response.tx_hash(), tx_response)) + } + Err(err) => { + warn!(error = ?err, tx_hash = ?hash, "failed to get transaction response"); + None + } + }) + .collect::>()) + } + + pub async fn transaction_response( + &self, + tx_hash: String, + ) -> error_stack::Result, Error> { + let tx_hash = Hash::from_str(tx_hash.as_str()).change_context(Error::TxHash)?; + + match self.0.get_transaction(&tx_hash).await { + Ok(response) => Ok(Some(TxResponse::from((tx_hash, response)))), + Err(err) => { + warn!(error = ?err, "failed to get transaction response"); + Ok(None) + } + } + } +} diff --git a/ampd/src/stellar/verifier.rs b/ampd/src/stellar/verifier.rs index 5efa0e926..4e5c1f4b1 100644 --- a/ampd/src/stellar/verifier.rs +++ b/ampd/src/stellar/verifier.rs @@ -6,7 +6,7 @@ use stellar_xdr::curr::{BytesM, ContractEventBody, ScAddress, ScBytes, ScSymbol, use crate::handlers::stellar_verify_msg::Message; use crate::handlers::stellar_verify_verifier_set::VerifierSetConfirmation; -use crate::stellar::http_client::TxResponse; +use crate::stellar::rpc_client::TxResponse; const TOPIC_CONTRACT_CALLED: &str = "contract_called"; const TOPIC_SIGNERS_ROTATED: &str = "signers_rotated"; @@ -146,7 +146,7 @@ mod test { use crate::handlers::stellar_verify_msg::Message; use crate::handlers::stellar_verify_verifier_set::VerifierSetConfirmation; - use crate::stellar::http_client::TxResponse; + use crate::stellar::rpc_client::TxResponse; use crate::stellar::verifier::{ verify_message, verify_verifier_set, TOPIC_CONTRACT_CALLED, TOPIC_SIGNERS_ROTATED, }; @@ -330,9 +330,8 @@ mod test { let tx_response = TxResponse { transaction_hash: msg.message_id.tx_hash_as_hex_no_prefix().to_string(), - source_address: msg.source_address.clone(), successful: true, - contract_events: Some(vec![event].try_into().unwrap()), + contract_events: vec![event].try_into().unwrap(), }; (gateway_address, tx_response, msg) @@ -389,11 +388,8 @@ mod test { .message_id .tx_hash_as_hex_no_prefix() .to_string(), - source_address: ScAddress::Account(AccountId(PublicKey::PublicKeyTypeEd25519( - Uint256::from(SigningKey::generate(&mut OsRng).verifying_key().to_bytes()), - ))), successful: true, - contract_events: Some(vec![event].try_into().unwrap()), + contract_events: vec![event].try_into().unwrap(), }; (gateway_address, tx_response, verifier_set_confirmation) diff --git a/ampd/src/tests/config_template.toml b/ampd/src/tests/config_template.toml index e6b9c818a..ec99f3de7 100644 --- a/ampd/src/tests/config_template.toml +++ b/ampd/src/tests/config_template.toml @@ -75,12 +75,12 @@ proxy_url = 'http://127.0.0.1/' [[handlers]] type = 'StellarMsgVerifier' cosmwasm_contract = 'axelar1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqecnww6' -http_url = 'http://127.0.0.1/' +rpc_url = 'http://127.0.0.1/' [[handlers]] type = 'StellarVerifierSetVerifier' cosmwasm_contract = 'axelar1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqecnww6' -http_url = 'http://127.0.0.1/' +rpc_url = 'http://127.0.0.1/' [tofnd_config] url = 'http://localhost:50051/'