diff --git a/contracts/interchain-token-service/src/abi.rs b/contracts/interchain-token-service/src/abi.rs index dabec3048..45fd54f2c 100644 --- a/contracts/interchain-token-service/src/abi.rs +++ b/contracts/interchain-token-service/src/abi.rs @@ -16,7 +16,7 @@ sol! { enum MessageType { InterchainTransfer, DeployInterchainToken, - DeployTokenManager, + DeployTokenManager, // note, this case is not supported by the ITS hub SendToHub, ReceiveFromHub, } @@ -39,13 +39,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 +103,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 +146,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 +250,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 +370,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 +472,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 +500,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 +518,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 +538,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.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 a5dcd6a46..1ab5ee746 100644 --- a/contracts/interchain-token-service/src/contract/execute.rs +++ b/contracts/interchain-token-service/src/contract/execute.rs @@ -1,20 +1,20 @@ 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 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, DeployTokenManager, 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")] @@ -31,10 +31,18 @@ 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")] - ChainConfigAlreadySet(ChainNameRaw), + #[error("chain {0} already registered")] + ChainAlreadyRegistered(ChainNameRaw), #[error("token {token_id} not deployed on chain {chain}")] TokenNotDeployed { token_id: TokenId, @@ -51,6 +59,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, @@ -91,13 +108,13 @@ 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()))?; - 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 +144,22 @@ 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)? } 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)? } } - - Ok(()) + .then(Result::Ok) } fn ensure_chain_not_frozen(storage: &dyn Storage, chain: &ChainNameRaw) -> Result<(), Error> { @@ -175,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, @@ -203,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)?; @@ -241,18 +226,138 @@ pub fn enable_execution(deps: DepsMut) -> Result { killswitch::disengage(deps.storage, Event::ExecutionEnabled).change_context(Error::State) } -pub fn set_chain_config( +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, - max_uint: nonempty::Uint256, - max_target_decimals: u8, + its_address: Address, ) -> 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) - .change_context(Error::State)? - .then(|_| Ok(Response::new())), + 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. +/// 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( @@ -260,9 +365,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( @@ -271,23 +392,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 +411,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 +433,131 @@ 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 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( + storage, + origin_chain, + source_chain, + token_id, + 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( + storage: &dyn Storage, origin_chain: ChainNameRaw, + source_chain: &ChainNameRaw, token_id: TokenId, + 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: &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 +569,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!( @@ -424,12 +597,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; @@ -440,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"; @@ -671,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, @@ -695,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/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/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/primitives.rs b/contracts/interchain-token-service/src/primitives.rs index 4e5dcdeb3..447cbb131 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")] @@ -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.clone(), + | Message::DeployInterchainToken(DeployInterchainToken { token_id, .. }) => *token_id, } } } diff --git a/contracts/interchain-token-service/src/state.rs b/contracts/interchain-token-service/src/state.rs index 96a3eb1e6..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 { @@ -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,11 +34,23 @@ 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, + 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. @@ -71,22 +85,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 } } } @@ -104,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"); @@ -128,31 +143,48 @@ 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::ChainNotFound(chain.to_owned()))) +} + 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) } @@ -162,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::, _>>() } @@ -311,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/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 cbd202c81..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, DeployTokenManager, HubMessage, InterchainTransfer, TokenId, - TokenManagerType, TokenSupply, + DeployInterchainToken, HubMessage, InterchainTransfer, TokenId, TokenSupply, }; use router_api::{Address, ChainName, ChainNameRaw, CrossChainId}; -use utils::{params, TestMessage}; +use serde_json::json; +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 ); } @@ -112,20 +177,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 @@ -173,6 +224,199 @@ fn execute_hub_message_succeeds() { goldie::assert_json!(responses); } +#[test] +fn execute_message_interchain_transfer_should_scale_the_amount_when_source_decimals_are_different() +{ + 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(), + router_message.cc_id.clone(), + source_its_contract.clone(), + 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(), + 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(), + 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, + u64::MAX.try_into().unwrap(), + 6, + ); + + 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, + )); + 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, + TestMessage { + router_message, + source_its_chain, + source_its_contract, + destination_its_chain, + .. + }, + ) = utils::setup_with_chain_configs( + Uint256::MAX.try_into().unwrap(), + 6, + Uint256::MAX.try_into().unwrap(), + 6, + ); + + 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, + )); + 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(); @@ -208,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( @@ -236,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( @@ -269,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(), @@ -280,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] @@ -296,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(); @@ -368,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 )); @@ -406,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 )); @@ -517,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 )); @@ -537,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(_) ) } @@ -580,7 +865,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 +884,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 +894,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 +905,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 +928,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 +938,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 +966,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 +985,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 +996,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 +1015,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 +1039,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 +1075,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 +1101,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 +1119,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(), @@ -906,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(), @@ -955,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/execute_hub_message_succeeds.golden b/contracts/interchain-token-service/tests/testdata/execute_hub_message_succeeds.golden index 69e88351f..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 @@ -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 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 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/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 5bcca0cd8..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,90 @@ pub fn make_deps() -> OwnedDeps>, + deps: DepsMut, + chain: ChainNameRaw, + 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_contract: Address, + its_edge_contract: Address, +) -> Result { + contract::execute( + deps, + mock_env(), + mock_info(params::GOVERNANCE, &[]), + ExecuteMsg::UpdateChain { + chain, + its_edge_contract, + }, + ) +} + +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, ) { - register_its_contract(deps.as_mut(), chain.clone(), its_contract).unwrap(); - set_chain_config( + 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( deps.as_mut(), - chain, - Uint256::MAX.try_into().unwrap(), - u8::MAX, + source_its_chain, + source_its_contract, + source_max_uint, + source_max_target_decimals, + ) + .unwrap(); + + register_chain( + deps.as_mut(), + destination_its_chain, + destination_its_contract, + destination_max_uint, + destination_max_target_decimals, ) .unwrap(); + + (deps, TestMessage::dummy()) } pub fn setup() -> ( @@ -157,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(), }, ) } 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;