From 0c3ffa504b22f26196960b555f61ef742bb93ffc Mon Sep 17 00:00:00 2001 From: Sammy Date: Wed, 6 Nov 2024 00:19:33 -0500 Subject: [PATCH 1/4] refactor(interchain-token-service): create message execution interceptors --- .../src/contract/execute/interceptors.rs | 649 ++++++++++++++++++ .../contract/{execute.rs => execute/mod.rs} | 403 ++--------- .../interchain-token-service/src/state.rs | 4 +- 3 files changed, 702 insertions(+), 354 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} (61%) 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..d02ae07e8 --- /dev/null +++ b/contracts/interchain-token-service/src/contract/execute/interceptors.rs @@ -0,0 +1,649 @@ +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, + transfer: &mut InterchainTransfer, +) -> Result<(), Error> { + transfer.amount = destination_amount( + storage, + source_chain, + destination_chain, + transfer.token_id, + transfer.amount, + )?; + + Ok(()) +} + +pub fn save_token_instance_for_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 save_token_instance_for_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) + .and_then(|_| Ok(())) +} + +pub fn calculate_scaling_factor( + storage: &dyn Storage, + source_chain: &ChainNameRaw, + destination_chain: &ChainNameRaw, + deploy_token: &mut DeployInterchainToken, +) -> Result<(), Error> { + deploy_token.decimals = destination_token_decimals( + storage, + source_chain, + destination_chain, + deploy_token.decimals, + )?; + + Ok(()) +} + +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, Uint256}; + use router_api::ChainNameRaw; + + use super::Error; + use crate::{ + contract::execute::interceptors, + state::{self, TokenDeploymentType}, + 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 mut transfer = InterchainTransfer { + token_id: [1u8; 32].try_into().unwrap(), + 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, + Uint256::from(1_000_000_000u128).try_into().unwrap(), + 6, + ) + .unwrap(); + + assert_ok!(interceptors::apply_scaling_factor_to_amount( + &storage, + &source_chain, + &destination_chain, + &mut 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 mut transfer = InterchainTransfer { + token_id: [1u8; 32].try_into().unwrap(), + 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, + Uint256::from(1_000_000_000_000_000u128).try_into().unwrap(), + 6, + ) + .unwrap(); + + assert_ok!(interceptors::apply_scaling_factor_to_amount( + &storage, + &source_chain, + &destination_chain, + &mut 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 mut transfer = InterchainTransfer { + token_id: [1u8; 32].try_into().unwrap(), + 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, + Uint256::from(1_000_000_000_000_000u128).try_into().unwrap(), + 6, + ) + .unwrap(); + + assert_ok!(interceptors::apply_scaling_factor_to_amount( + &storage, + &source_chain, + &destination_chain, + &mut 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 mut transfer = InterchainTransfer { + token_id: [1u8; 32].try_into().unwrap(), + 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, + Uint256::from(1_000_00u128).try_into().unwrap(), + 6, + ) + .unwrap(); + + assert_err_contains!( + interceptors::apply_scaling_factor_to_amount( + &storage, + &source_chain, + &destination_chain, + &mut 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 mut transfer = InterchainTransfer { + token_id: [1u8; 32].try_into().unwrap(), + 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_00u128).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, + Uint256::from(1_000_00u128).try_into().unwrap(), + 6, + ) + .unwrap(); + + assert_err_contains!( + interceptors::apply_scaling_factor_to_amount( + &storage, + &source_chain, + &destination_chain, + &mut 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 mut deploy_token = DeployInterchainToken { + token_id: [1u8; 32].try_into().unwrap(), + 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, + Uint256::from(1_000_000_000_000_000u128).try_into().unwrap(), + 6, + ) + .unwrap(); + state::save_chain_config( + &mut storage, + &destination_chain, + Uint256::from(1_000_000_000u128).try_into().unwrap(), + 6, + ) + .unwrap(); + + assert_ok!(interceptors::calculate_scaling_factor( + &storage, + &source_chain, + &destination_chain, + &mut deploy_token, + )); + assert_eq!(deploy_token.decimals, 6); + + let mut deploy_token = DeployInterchainToken { + token_id: [1u8; 32].try_into().unwrap(), + name: "token".to_string().try_into().unwrap(), + symbol: "TKN".to_string().try_into().unwrap(), + decimals: 3, + minter: None, + }; + assert_ok!(interceptors::calculate_scaling_factor( + &storage, + &source_chain, + &destination_chain, + &mut 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 mut deploy_token = DeployInterchainToken { + token_id: [1u8; 32].try_into().unwrap(), + 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, + Uint256::from(1_000_000_000u128).try_into().unwrap(), + 6, + ) + .unwrap(); + state::save_chain_config( + &mut storage, + &destination_chain, + Uint256::from(1_000_000_000_000_000u128).try_into().unwrap(), + 6, + ) + .unwrap(); + + assert_ok!(interceptors::calculate_scaling_factor( + &storage, + &source_chain, + &destination_chain, + &mut 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 61% rename from contracts/interchain-token-service/src/contract/execute.rs rename to contracts/interchain-token-service/src/contract/execute/mod.rs index 4346f4298..196ab71bc 100644 --- a/contracts/interchain-token-service/src/contract/execute.rs +++ b/contracts/interchain-token-service/src/contract/execute/mod.rs @@ -1,14 +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 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, -}; +use crate::state::{self, is_chain_frozen, load_config, load_its_contract}; +use crate::{DeployInterchainToken, InterchainTransfer, Message, TokenId}; + +mod interceptors; #[derive(thiserror::Error, Debug, IntoContractError)] pub enum Error { @@ -150,17 +150,61 @@ 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, + mut transfer: InterchainTransfer, +) -> Result { + interceptors::subtract_supply_amount(storage, &source_chain, &transfer) + .and_then(|_| { + interceptors::apply_scaling_factor_to_amount( + storage, + &source_chain, + &destination_chain, + &mut transfer, + ) + }) + .and_then(|_| interceptors::add_supply_amount(storage, &destination_chain, &transfer)) + .and_then(|_| Ok(transfer)) +} + +fn apply_to_token_deployment( + storage: &mut dyn Storage, + source_chain: &ChainNameRaw, + destination_chain: &ChainNameRaw, + mut deploy_token: DeployInterchainToken, +) -> Result { + interceptors::save_token_instance_for_source_chain(storage, source_chain, &deploy_token) + .and_then(|_| { + interceptors::calculate_scaling_factor( + storage, + source_chain, + destination_chain, + &mut deploy_token, + ) + }) + .and_then(|_| { + interceptors::save_token_instance_for_destination_chain( + storage, + destination_chain, + &deploy_token, + ) + }) + .and_then(|_| 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)?, @@ -257,351 +301,6 @@ 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: -/// 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 0ded455cf..3506e763b 100644 --- a/contracts/interchain-token-service/src/state.rs +++ b/contracts/interchain-token-service/src/state.rs @@ -270,11 +270,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 a0677fc8e20f1b24554c5d9c29481fc06b216a61 Mon Sep 17 00:00:00 2001 From: Sammy Date: Wed, 6 Nov 2024 13:11:28 -0500 Subject: [PATCH 2/4] fix clippy --- .../src/contract/execute/interceptors.rs | 24 +++++++++---------- .../src/contract/execute/mod.rs | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/contracts/interchain-token-service/src/contract/execute/interceptors.rs b/contracts/interchain-token-service/src/contract/execute/interceptors.rs index d02ae07e8..5b7a2ddf9 100644 --- a/contracts/interchain-token-service/src/contract/execute/interceptors.rs +++ b/contracts/interchain-token-service/src/contract/execute/interceptors.rs @@ -122,7 +122,7 @@ pub fn save_token_instance_for_destination_chain( &TokenInstance::new(&deploy_token.deployment_type(), Some(deploy_token.decimals)), ) .change_context(Error::State) - .and_then(|_| Ok(())) + .map(|_| ()) } pub fn calculate_scaling_factor( @@ -329,7 +329,7 @@ mod test { let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); let mut transfer = InterchainTransfer { - token_id: [1u8; 32].try_into().unwrap(), + 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(), @@ -376,7 +376,7 @@ mod test { let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); let mut transfer = InterchainTransfer { - token_id: [1u8; 32].try_into().unwrap(), + 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(), @@ -423,7 +423,7 @@ mod test { let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); let mut transfer = InterchainTransfer { - token_id: [1u8; 32].try_into().unwrap(), + 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(), @@ -470,7 +470,7 @@ mod test { let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); let mut transfer = InterchainTransfer { - token_id: [1u8; 32].try_into().unwrap(), + 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(), @@ -494,7 +494,7 @@ mod test { state::save_chain_config( &mut storage, &destination_chain, - Uint256::from(1_000_00u128).try_into().unwrap(), + Uint256::from(100_000u128).try_into().unwrap(), 6, ) .unwrap(); @@ -517,10 +517,10 @@ mod test { let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); let mut transfer = InterchainTransfer { - token_id: [1u8; 32].try_into().unwrap(), + 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_00u128).try_into().unwrap(), + amount: Uint256::from(100_000u128).try_into().unwrap(), data: None, }; @@ -541,7 +541,7 @@ mod test { state::save_chain_config( &mut storage, &destination_chain, - Uint256::from(1_000_00u128).try_into().unwrap(), + Uint256::from(100_000u128).try_into().unwrap(), 6, ) .unwrap(); @@ -564,7 +564,7 @@ mod test { let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); let mut deploy_token = DeployInterchainToken { - token_id: [1u8; 32].try_into().unwrap(), + token_id: [1u8; 32].into(), name: "token".to_string().try_into().unwrap(), symbol: "TKN".to_string().try_into().unwrap(), decimals: 9, @@ -595,7 +595,7 @@ mod test { assert_eq!(deploy_token.decimals, 6); let mut deploy_token = DeployInterchainToken { - token_id: [1u8; 32].try_into().unwrap(), + token_id: [1u8; 32].into(), name: "token".to_string().try_into().unwrap(), symbol: "TKN".to_string().try_into().unwrap(), decimals: 3, @@ -616,7 +616,7 @@ mod test { let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); let mut deploy_token = DeployInterchainToken { - token_id: [1u8; 32].try_into().unwrap(), + token_id: [1u8; 32].into(), name: "token".to_string().try_into().unwrap(), symbol: "TKN".to_string().try_into().unwrap(), decimals: 9, diff --git a/contracts/interchain-token-service/src/contract/execute/mod.rs b/contracts/interchain-token-service/src/contract/execute/mod.rs index 196ab71bc..f1c825b56 100644 --- a/contracts/interchain-token-service/src/contract/execute/mod.rs +++ b/contracts/interchain-token-service/src/contract/execute/mod.rs @@ -177,7 +177,7 @@ fn apply_to_transfer( ) }) .and_then(|_| interceptors::add_supply_amount(storage, &destination_chain, &transfer)) - .and_then(|_| Ok(transfer)) + .map(|_| transfer) } fn apply_to_token_deployment( @@ -202,7 +202,7 @@ fn apply_to_token_deployment( &deploy_token, ) }) - .and_then(|_| Ok(deploy_token)) + .map(|_| deploy_token) } fn ensure_chain_not_frozen(storage: &dyn Storage, chain: &ChainNameRaw) -> Result<(), Error> { From 851ffdfa5f0545f221eafea65ba78d1cbffd6464 Mon Sep 17 00:00:00 2001 From: Sammy Date: Wed, 6 Nov 2024 14:01:50 -0500 Subject: [PATCH 3/4] fix fmt --- .../src/contract/execute/interceptors.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/contracts/interchain-token-service/src/contract/execute/interceptors.rs b/contracts/interchain-token-service/src/contract/execute/interceptors.rs index 5b7a2ddf9..9c6341a10 100644 --- a/contracts/interchain-token-service/src/contract/execute/interceptors.rs +++ b/contracts/interchain-token-service/src/contract/execute/interceptors.rs @@ -313,15 +313,14 @@ impl DeploymentType for DeployInterchainToken { mod test { use assert_ok::assert_ok; use axelar_wasm_std::assert_err_contains; - use cosmwasm_std::{testing::MockStorage, Uint256}; + use cosmwasm_std::testing::MockStorage; + use cosmwasm_std::Uint256; use router_api::ChainNameRaw; use super::Error; - use crate::{ - contract::execute::interceptors, - state::{self, TokenDeploymentType}, - DeployInterchainToken, InterchainTransfer, TokenInstance, - }; + use crate::contract::execute::interceptors; + use crate::state::{self, TokenDeploymentType}; + use crate::{DeployInterchainToken, InterchainTransfer, TokenInstance}; #[test] fn apply_scaling_factor_to_amount_when_source_decimals_are_bigger() { From f9c3e2a0a41b3aaf3fa8c426835d1dd1b3e228c3 Mon Sep 17 00:00:00 2001 From: Sammy Date: Fri, 8 Nov 2024 12:05:58 -0500 Subject: [PATCH 4/4] address comments --- .../src/contract/execute/interceptors.rs | 60 +++++++++---------- .../src/contract/execute/mod.rs | 52 +++++++--------- 2 files changed, 52 insertions(+), 60 deletions(-) diff --git a/contracts/interchain-token-service/src/contract/execute/interceptors.rs b/contracts/interchain-token-service/src/contract/execute/interceptors.rs index 16f13b2c7..247997007 100644 --- a/contracts/interchain-token-service/src/contract/execute/interceptors.rs +++ b/contracts/interchain-token-service/src/contract/execute/interceptors.rs @@ -49,8 +49,8 @@ pub fn apply_scaling_factor_to_amount( storage: &dyn Storage, source_chain: &ChainNameRaw, destination_chain: &ChainNameRaw, - transfer: &mut InterchainTransfer, -) -> Result<(), Error> { + mut transfer: InterchainTransfer, +) -> Result { transfer.amount = destination_amount( storage, source_chain, @@ -59,10 +59,10 @@ pub fn apply_scaling_factor_to_amount( transfer.amount, )?; - Ok(()) + Ok(transfer) } -pub fn save_token_instance_for_source_chain( +pub fn deploy_token_to_source_chain( storage: &mut dyn Storage, chain: &ChainNameRaw, deploy_token: &DeployInterchainToken, @@ -100,7 +100,7 @@ pub fn save_token_instance_for_source_chain( Ok(()) } -pub fn save_token_instance_for_destination_chain( +pub fn deploy_token_to_destination_chain( storage: &mut dyn Storage, chain: &ChainNameRaw, deploy_token: &DeployInterchainToken, @@ -129,8 +129,8 @@ pub fn calculate_scaling_factor( storage: &dyn Storage, source_chain: &ChainNameRaw, destination_chain: &ChainNameRaw, - deploy_token: &mut DeployInterchainToken, -) -> Result<(), Error> { + mut deploy_token: DeployInterchainToken, +) -> Result { deploy_token.decimals = destination_token_decimals( storage, source_chain, @@ -138,7 +138,7 @@ pub fn calculate_scaling_factor( deploy_token.decimals, )?; - Ok(()) + Ok(deploy_token) } fn ensure_matching_original_deployment( @@ -327,7 +327,7 @@ mod test { let mut storage = MockStorage::new(); let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); - let mut transfer = InterchainTransfer { + 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(), @@ -361,11 +361,11 @@ mod test { ) .unwrap(); - assert_ok!(interceptors::apply_scaling_factor_to_amount( + let transfer = assert_ok!(interceptors::apply_scaling_factor_to_amount( &storage, &source_chain, &destination_chain, - &mut transfer, + transfer, )); assert_eq!( transfer.amount, @@ -378,7 +378,7 @@ mod test { let mut storage = MockStorage::new(); let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); - let mut transfer = InterchainTransfer { + 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(), @@ -412,11 +412,11 @@ mod test { ) .unwrap(); - assert_ok!(interceptors::apply_scaling_factor_to_amount( + let transfer = assert_ok!(interceptors::apply_scaling_factor_to_amount( &storage, &source_chain, &destination_chain, - &mut transfer, + transfer, )); assert_eq!( transfer.amount, @@ -429,7 +429,7 @@ mod test { let mut storage = MockStorage::new(); let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); - let mut transfer = InterchainTransfer { + 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(), @@ -463,11 +463,11 @@ mod test { ) .unwrap(); - assert_ok!(interceptors::apply_scaling_factor_to_amount( + let transfer = assert_ok!(interceptors::apply_scaling_factor_to_amount( &storage, &source_chain, &destination_chain, - &mut transfer, + transfer, )); assert_eq!( transfer.amount, @@ -480,7 +480,7 @@ mod test { let mut storage = MockStorage::new(); let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); - let mut transfer = InterchainTransfer { + 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(), @@ -519,7 +519,7 @@ mod test { &storage, &source_chain, &destination_chain, - &mut transfer, + transfer, ), Error, Error::InvalidTransferAmount { .. } @@ -531,7 +531,7 @@ mod test { let mut storage = MockStorage::new(); let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); - let mut transfer = InterchainTransfer { + 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(), @@ -570,7 +570,7 @@ mod test { &storage, &source_chain, &destination_chain, - &mut transfer, + transfer, ), Error, Error::InvalidTransferAmount { .. } @@ -582,7 +582,7 @@ mod test { let mut storage = MockStorage::new(); let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); - let mut deploy_token = DeployInterchainToken { + let deploy_token = DeployInterchainToken { token_id: [1u8; 32].into(), name: "token".to_string().try_into().unwrap(), symbol: "TKN".to_string().try_into().unwrap(), @@ -613,26 +613,26 @@ mod test { ) .unwrap(); - assert_ok!(interceptors::calculate_scaling_factor( + let deploy_token = assert_ok!(interceptors::calculate_scaling_factor( &storage, &source_chain, &destination_chain, - &mut deploy_token, + deploy_token, )); assert_eq!(deploy_token.decimals, 6); - let mut deploy_token = DeployInterchainToken { + 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, }; - assert_ok!(interceptors::calculate_scaling_factor( + let deploy_token = assert_ok!(interceptors::calculate_scaling_factor( &storage, &source_chain, &destination_chain, - &mut deploy_token, + deploy_token, )); assert_eq!(deploy_token.decimals, 3); } @@ -642,7 +642,7 @@ mod test { let mut storage = MockStorage::new(); let source_chain: ChainNameRaw = "sourcechain".try_into().unwrap(); let destination_chain: ChainNameRaw = "destinationchain".try_into().unwrap(); - let mut deploy_token = DeployInterchainToken { + let deploy_token = DeployInterchainToken { token_id: [1u8; 32].into(), name: "token".to_string().try_into().unwrap(), symbol: "TKN".to_string().try_into().unwrap(), @@ -673,11 +673,11 @@ mod test { ) .unwrap(); - assert_ok!(interceptors::calculate_scaling_factor( + let deploy_token = assert_ok!(interceptors::calculate_scaling_factor( &storage, &source_chain, &destination_chain, - &mut deploy_token, + deploy_token, )); assert_eq!(deploy_token.decimals, 9); } diff --git a/contracts/interchain-token-service/src/contract/execute/mod.rs b/contracts/interchain-token-service/src/contract/execute/mod.rs index 277082df5..502eeac08 100644 --- a/contracts/interchain-token-service/src/contract/execute/mod.rs +++ b/contracts/interchain-token-service/src/contract/execute/mod.rs @@ -165,44 +165,36 @@ fn apply_to_transfer( storage: &mut dyn Storage, source_chain: ChainNameRaw, destination_chain: ChainNameRaw, - mut transfer: InterchainTransfer, + transfer: InterchainTransfer, ) -> Result { - interceptors::subtract_supply_amount(storage, &source_chain, &transfer) - .and_then(|_| { - interceptors::apply_scaling_factor_to_amount( - storage, - &source_chain, - &destination_chain, - &mut transfer, - ) - }) - .and_then(|_| interceptors::add_supply_amount(storage, &destination_chain, &transfer)) - .map(|_| transfer) + 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, - mut deploy_token: DeployInterchainToken, + deploy_token: DeployInterchainToken, ) -> Result { - interceptors::save_token_instance_for_source_chain(storage, source_chain, &deploy_token) - .and_then(|_| { - interceptors::calculate_scaling_factor( - storage, - source_chain, - destination_chain, - &mut deploy_token, - ) - }) - .and_then(|_| { - interceptors::save_token_instance_for_destination_chain( - storage, - destination_chain, - &deploy_token, - ) - }) - .map(|_| deploy_token) + 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> {