diff --git a/contracts/interchain-token-service/src/contract.rs b/contracts/interchain-token-service/src/contract.rs index e3607a6e..b58202de 100644 --- a/contracts/interchain-token-service/src/contract.rs +++ b/contracts/interchain-token-service/src/contract.rs @@ -1,30 +1,38 @@ use axelar_gas_service::AxelarGasServiceClient; use axelar_gateway::{executable::AxelarExecutableInterface, AxelarGatewayMessagingClient}; -use axelar_soroban_std::events::Event; -use axelar_soroban_std::token::validate_token_metadata; -use axelar_soroban_std::ttl::{extend_instance_ttl, extend_persistent_ttl}; use axelar_soroban_std::{ - address::AddressExt, ensure, interfaces, types::Token, Ownable, Upgradable, + address::AddressExt, + ensure, + events::Event, + interfaces, + token::validate_token_metadata, + ttl::{extend_instance_ttl, extend_persistent_ttl}, + types::Token, + Operatable, Ownable, Upgradable, }; use interchain_token::InterchainTokenClient; -use soroban_sdk::token::{self, StellarAssetClient}; -use soroban_sdk::xdr::{FromXdr, ToXdr}; -use soroban_sdk::{contract, contractimpl, panic_with_error, Address, Bytes, BytesN, Env, String}; +use soroban_sdk::{ + contract, contractimpl, panic_with_error, + token::{self, StellarAssetClient}, + xdr::{FromXdr, ToXdr}, + Address, Bytes, BytesN, Env, String, +}; use soroban_token_sdk::metadata::TokenMetadata; -use crate::abi::{get_message_type, MessageType as EncodedMessageType}; -use crate::error::ContractError; -use crate::event::{ - InterchainTokenDeployedEvent, InterchainTokenDeploymentStartedEvent, - InterchainTokenIdClaimedEvent, InterchainTransferReceivedEvent, InterchainTransferSentEvent, - TrustedChainRemovedEvent, TrustedChainSetEvent, -}; -use crate::executable::InterchainTokenExecutableClient; -use crate::interface::InterchainTokenServiceInterface; -use crate::storage_types::{DataKey, TokenIdConfigValue}; -use crate::token_handler; -use crate::types::{ - DeployInterchainToken, HubMessage, InterchainTransfer, Message, TokenManagerType, +use crate::{ + abi::{get_message_type, MessageType as EncodedMessageType}, + error::ContractError, + event::{ + InterchainTokenDeployedEvent, InterchainTokenDeploymentStartedEvent, + InterchainTokenIdClaimedEvent, InterchainTransferReceivedEvent, + InterchainTransferSentEvent, TrustedChainRemovedEvent, TrustedChainSetEvent, + }, + executable::InterchainTokenExecutableClient, + flow_limit::{self, FlowDirection}, + interface::InterchainTokenServiceInterface, + storage_types::{DataKey, TokenIdConfigValue}, + token_handler, + types::{DeployInterchainToken, HubMessage, InterchainTransfer, Message, TokenManagerType}, }; const ITS_HUB_CHAIN_NAME: &str = "axelar"; @@ -33,7 +41,7 @@ const PREFIX_INTERCHAIN_TOKEN_SALT: &str = "interchain-token-salt"; const PREFIX_CANONICAL_TOKEN_SALT: &str = "canonical-token-salt"; #[contract] -#[derive(Ownable, Upgradable)] +#[derive(Operatable, Ownable, Upgradable)] pub struct InterchainTokenService; #[contractimpl] @@ -41,6 +49,7 @@ impl InterchainTokenService { pub fn __constructor( env: Env, owner: Address, + operator: Address, gateway: Address, gas_service: Address, its_hub_address: String, @@ -48,6 +57,7 @@ impl InterchainTokenService { interchain_token_wasm_hash: BytesN<32>, ) { interfaces::set_owner(&env, &owner); + interfaces::set_operator(&env, &operator); env.storage().instance().set(&DataKey::Gateway, &gateway); env.storage() .instance() @@ -160,6 +170,28 @@ impl InterchainTokenServiceInterface for InterchainTokenService { .into() } + fn flow_limit(env: &Env, token_id: BytesN<32>) -> Option { + flow_limit::flow_limit(env, token_id) + } + + fn flow_out_amount(env: &Env, token_id: BytesN<32>) -> i128 { + flow_limit::flow_out_amount(env, token_id) + } + + fn flow_in_amount(env: &Env, token_id: BytesN<32>) -> i128 { + flow_limit::flow_in_amount(env, token_id) + } + + fn set_flow_limit( + env: &Env, + token_id: BytesN<32>, + flow_limit: Option, + ) -> Result<(), ContractError> { + Self::operator(env).require_auth(); + + flow_limit::set_flow_limit(env, token_id, flow_limit) + } + /// Computes a 32-byte deployment salt for a canonical token using the provided token address. /// /// The salt is derived by hashing a combination of a prefix, the chain name hash, @@ -337,6 +369,8 @@ impl InterchainTokenServiceInterface for InterchainTokenService { amount, )?; + FlowDirection::Out.add_flow(env, token_id.clone(), amount)?; + InterchainTransferSentEvent { token_id: token_id.clone(), source_address: caller.clone(), @@ -504,6 +538,8 @@ impl InterchainTokenService { let token_config_value = Self::token_id_config_with_extended_ttl(env, token_id.clone())?; + FlowDirection::In.add_flow(env, token_id.clone(), amount)?; + token_handler::give_token( env, &destination_address, diff --git a/contracts/interchain-token-service/src/error.rs b/contracts/interchain-token-service/src/error.rs index 7c977378..2d18f11a 100644 --- a/contracts/interchain-token-service/src/error.rs +++ b/contracts/interchain-token-service/src/error.rs @@ -22,4 +22,7 @@ pub enum ContractError { InvalidTokenMetaData = 16, InvalidTokenId = 17, TokenAlreadyDeployed = 18, + InvalidFlowLimit = 19, + FlowLimitExceeded = 20, + FlowAmountOverflow = 21, } diff --git a/contracts/interchain-token-service/src/event.rs b/contracts/interchain-token-service/src/event.rs index 88252c60..2841d767 100644 --- a/contracts/interchain-token-service/src/event.rs +++ b/contracts/interchain-token-service/src/event.rs @@ -1,7 +1,7 @@ use core::fmt::Debug; use axelar_soroban_std::events::Event; -use soroban_sdk::{Address, Bytes, BytesN, Env, IntoVal, String, Symbol, Topics, Val, Vec}; +use soroban_sdk::{Address, Bytes, BytesN, Env, IntoVal, String, Symbol, Topics, Val}; #[derive(Debug, PartialEq, Eq)] pub struct TrustedChainSetEvent { @@ -13,6 +13,13 @@ pub struct TrustedChainRemovedEvent { pub chain: String, } +#[derive(Debug, PartialEq, Eq)] +pub struct FlowLimitSetEvent { + pub token_id: BytesN<32>, + /// A `None` value implies that flow limit checks have been disabled for this `token_id` + pub flow_limit: Option, +} + #[derive(Debug, PartialEq, Eq)] pub struct InterchainTokenDeployedEvent { pub token_id: BytesN<32>, @@ -65,10 +72,6 @@ impl Event for TrustedChainSetEvent { fn topics(&self, env: &Env) -> impl Topics + Debug { (Symbol::new(env, "trusted_chain_set"), self.chain.to_val()) } - - fn data(&self, env: &Env) -> impl IntoVal + Debug { - Vec::::new(env) - } } impl Event for TrustedChainRemovedEvent { @@ -78,9 +81,15 @@ impl Event for TrustedChainRemovedEvent { self.chain.to_val(), ) } +} - fn data(&self, env: &Env) -> impl IntoVal + Debug { - Vec::::new(env) +impl Event for FlowLimitSetEvent { + fn topics(&self, env: &Env) -> impl Topics + Debug { + ( + Symbol::new(env, "flow_limit_set"), + self.token_id.to_val(), + self.flow_limit, + ) } } @@ -96,10 +105,6 @@ impl Event for InterchainTokenDeployedEvent { self.minter.clone(), ) } - - fn data(&self, env: &Env) -> impl IntoVal + Debug { - Vec::::new(env) - } } impl Event for InterchainTokenDeploymentStartedEvent { @@ -115,10 +120,6 @@ impl Event for InterchainTokenDeploymentStartedEvent { self.minter.clone(), ) } - - fn data(&self, env: &Env) -> impl IntoVal + Debug { - Vec::::new(env) - } } impl Event for InterchainTokenIdClaimedEvent { @@ -130,10 +131,6 @@ impl Event for InterchainTokenIdClaimedEvent { self.salt.to_val(), ) } - - fn data(&self, env: &Env) -> impl IntoVal + Debug { - Vec::::new(env) - } } impl Event for InterchainTransferSentEvent { @@ -179,6 +176,9 @@ impl_event_testutils!(TrustedChainSetEvent, (Symbol, String), ()); #[cfg(any(test, feature = "testutils"))] impl_event_testutils!(TrustedChainRemovedEvent, (Symbol, String), ()); +#[cfg(any(test, feature = "testutils"))] +impl_event_testutils!(FlowLimitSetEvent, (Symbol, BytesN<32>, Option), ()); + #[cfg(any(test, feature = "testutils"))] impl_event_testutils!( InterchainTokenDeployedEvent, diff --git a/contracts/interchain-token-service/src/flow_limit.rs b/contracts/interchain-token-service/src/flow_limit.rs new file mode 100644 index 00000000..84d8e33b --- /dev/null +++ b/contracts/interchain-token-service/src/flow_limit.rs @@ -0,0 +1,138 @@ +use axelar_soroban_std::{ensure, events::Event, ttl::extend_persistent_ttl}; +use soroban_sdk::{BytesN, Env}; + +use crate::{ + error::ContractError, + event::FlowLimitSetEvent, + storage_types::{DataKey, FlowKey}, +}; + +const EPOCH_TIME: u64 = 6 * 60 * 60; // 6 hours in seconds = 21600 + +pub enum FlowDirection { + /// An interchain transfer coming in to this chain from another chain + In, + /// An interchain transfer going out from this chain to another chain + Out, +} + +impl FlowDirection { + fn flow(&self, env: &Env, token_id: BytesN<32>) -> i128 { + match self { + Self::In => flow_in_amount(env, token_id), + Self::Out => flow_out_amount(env, token_id), + } + } + + fn reverse_flow(&self, env: &Env, token_id: BytesN<32>) -> i128 { + match self { + Self::In => flow_out_amount(env, token_id), + Self::Out => flow_in_amount(env, token_id), + } + } + + fn update_flow(&self, env: &Env, token_id: BytesN<32>, new_flow: i128) { + let flow_key = FlowKey { + token_id, + epoch: current_epoch(env), + }; + + let key = match self { + Self::In => DataKey::FlowIn(flow_key), + Self::Out => DataKey::FlowOut(flow_key), + }; + + env.storage().temporary().set(&key, &new_flow); + } + + /// Adds flow amount in the specified direction (in/out) for a token. + /// Flow amounts are stored in temporary storage since they only need to persist for + /// the 6-hour epoch duration. + /// + /// Checks that: + /// - Flow amount doesn't exceed the flow limit + /// - Adding flows won't cause overflow + /// - Net flow (outgoing minus incoming flow) doesn't exceed the limit + pub fn add_flow( + &self, + env: &Env, + token_id: BytesN<32>, + flow_amount: i128, + ) -> Result<(), ContractError> { + let Some(flow_limit) = flow_limit(env, token_id.clone()) else { + return Ok(()); + }; + + ensure!(flow_amount <= flow_limit, ContractError::FlowLimitExceeded); + + let new_flow = self + .flow(env, token_id.clone()) + .checked_add(flow_amount) + .ok_or(ContractError::FlowAmountOverflow)?; + let max_allowed = self + .reverse_flow(env, token_id.clone()) + .checked_add(flow_limit) + .ok_or(ContractError::FlowAmountOverflow)?; + + // Equivalent to flow_amount + flow - reverse_flow <= flow_limit + ensure!(new_flow <= max_allowed, ContractError::FlowLimitExceeded); + + self.update_flow(env, token_id.clone(), new_flow); + + extend_persistent_ttl(env, &DataKey::FlowLimit(token_id)); + + Ok(()) + } +} + +fn current_epoch(env: &Env) -> u64 { + env.ledger().timestamp() / EPOCH_TIME +} + +pub fn flow_limit(env: &Env, token_id: BytesN<32>) -> Option { + env.storage() + .persistent() + .get(&DataKey::FlowLimit(token_id)) +} + +pub fn set_flow_limit( + env: &Env, + token_id: BytesN<32>, + flow_limit: Option, +) -> Result<(), ContractError> { + if let Some(limit) = flow_limit { + ensure!(limit >= 0, ContractError::InvalidFlowLimit); + } + + env.storage() + .persistent() + .set(&DataKey::FlowLimit(token_id.clone()), &flow_limit); + + FlowLimitSetEvent { + token_id, + flow_limit, + } + .emit(env); + + Ok(()) +} + +pub fn flow_out_amount(env: &Env, token_id: BytesN<32>) -> i128 { + env.storage() + .temporary() + .get(&DataKey::FlowOut(FlowKey { + token_id, + epoch: current_epoch(env), + })) + .unwrap_or(0) +} + +pub fn flow_in_amount(env: &Env, token_id: BytesN<32>) -> i128 { + env.storage() + .temporary() + .get(&DataKey::FlowIn(FlowKey { + token_id, + epoch: current_epoch(env), + })) + .unwrap_or(0) +} diff --git a/contracts/interchain-token-service/src/interface.rs b/contracts/interchain-token-service/src/interface.rs index 918a6167..01e6c711 100644 --- a/contracts/interchain-token-service/src/interface.rs +++ b/contracts/interchain-token-service/src/interface.rs @@ -34,6 +34,42 @@ pub trait InterchainTokenServiceInterface: AxelarExecutableInterface { fn token_manager_type(env: &Env, token_id: BytesN<32>) -> TokenManagerType; + /// Retrieves the flow limit for the token associated with the specified token ID. + /// Returns `None` if no limit is set. + fn flow_limit(env: &Env, token_id: BytesN<32>) -> Option; + + /// Retrieves the amount that has flowed out of the chain to other chains during the current epoch + /// for the token associated with the specified token ID. + fn flow_out_amount(env: &Env, token_id: BytesN<32>) -> i128; + + /// Retrieves the amount that has flowed into the chain from other chains during the current epoch + /// for the token associated with the specified token ID. + fn flow_in_amount(env: &Env, token_id: BytesN<32>) -> i128; + + /// Sets or updates the flow limit for a token. + /// + /// Flow limit controls how many tokens can flow in/out during a single epoch. + /// Setting the limit to `None` disables flow limit checks for the token. + /// Setting the limit to 0 effectively freezes the token by preventing any flow. + /// + /// # Arguments + /// - `token_id`: Unique identifier of the token. + /// - `flow_limit`: The new flow limit value. Must be positive if Some. + /// + /// # Returns + /// - `Result<(), ContractError>`: Ok(()) on success. + /// + /// # Errors + /// - `ContractError::InvalidFlowLimit`: If the provided flow limit is not positive. + /// + /// # Authorization + /// - Must be called by the [`Self::operator`]. + fn set_flow_limit( + env: &Env, + token_id: BytesN<32>, + flow_limit: Option, + ) -> Result<(), ContractError>; + fn deploy_interchain_token( env: &Env, deployer: Address, diff --git a/contracts/interchain-token-service/src/lib.rs b/contracts/interchain-token-service/src/lib.rs index 73c8b4fb..5d3f241b 100644 --- a/contracts/interchain-token-service/src/lib.rs +++ b/contracts/interchain-token-service/src/lib.rs @@ -17,6 +17,7 @@ cfg_if::cfg_if! { mod storage_types; mod token_handler; mod contract; + mod flow_limit; pub use contract::{InterchainTokenService, InterchainTokenServiceClient}; } diff --git a/contracts/interchain-token-service/src/storage_types.rs b/contracts/interchain-token-service/src/storage_types.rs index 39beacfd..75915246 100644 --- a/contracts/interchain-token-service/src/storage_types.rs +++ b/contracts/interchain-token-service/src/storage_types.rs @@ -12,6 +12,9 @@ pub enum DataKey { ChainName, InterchainTokenWasmHash, TokenIdConfigKey(BytesN<32>), + FlowLimit(BytesN<32>), + FlowOut(FlowKey), + FlowIn(FlowKey), } #[contracttype] @@ -20,3 +23,10 @@ pub struct TokenIdConfigValue { pub token_address: Address, pub token_manager_type: TokenManagerType, } + +#[contracttype] +#[derive(Clone, Debug)] +pub struct FlowKey { + pub token_id: BytesN<32>, + pub epoch: u64, +} diff --git a/contracts/interchain-token-service/tests/execute.rs b/contracts/interchain-token-service/tests/execute.rs index e5d77bfa..de4f9cdf 100644 --- a/contracts/interchain-token-service/tests/execute.rs +++ b/contracts/interchain-token-service/tests/execute.rs @@ -51,7 +51,7 @@ fn execute_fails_with_invalid_message() { }, ]; - approve_gateway_messages(&env, gateway_client, signers, messages); + approve_gateway_messages(&env, &gateway_client, signers, messages); client.execute( &source_chain, @@ -101,7 +101,7 @@ fn interchain_transfer_message_execute_succeeds() { }, ]; - approve_gateway_messages(&env, gateway_client, signers, messages); + approve_gateway_messages(&env, &gateway_client, signers, messages); client.execute(&source_chain, &message_id, &source_address, &payload); @@ -153,7 +153,7 @@ fn deploy_interchain_token_message_execute_succeeds() { }, ]; - approve_gateway_messages(&env, gateway_client, signers, messages); + approve_gateway_messages(&env, &gateway_client, signers, messages); client.execute(&source_chain, &message_id, &source_address, &payload); @@ -207,7 +207,7 @@ fn deploy_interchain_token_message_execute_fails_empty_token_name() { }, ]; - approve_gateway_messages(&env, gateway_client, signers, messages); + approve_gateway_messages(&env, &gateway_client, signers, messages); client.execute( &source_chain, @@ -254,7 +254,7 @@ fn deploy_interchain_token_message_execute_fails_empty_token_symbol() { }, ]; - approve_gateway_messages(&env, gateway_client, signers, messages); + approve_gateway_messages(&env, &gateway_client, signers, messages); client.execute( &source_chain, @@ -303,7 +303,7 @@ fn deploy_interchain_token_message_execute_fails_invalid_minter_address() { }, ]; - approve_gateway_messages(&env, gateway_client, signers, messages); + approve_gateway_messages(&env, &gateway_client, signers, messages); client.execute( &source_chain, @@ -364,7 +364,7 @@ fn deploy_interchain_token_message_execute_fails_token_already_deployed() { }, ]; - approve_gateway_messages(&env, gateway_client, signers, messages); + approve_gateway_messages(&env, &gateway_client, signers, messages); client.execute(&source_chain, &first_message_id, &source_address, &payload); diff --git a/contracts/interchain-token-service/tests/flow_limit.rs b/contracts/interchain-token-service/tests/flow_limit.rs new file mode 100644 index 00000000..1882f761 --- /dev/null +++ b/contracts/interchain-token-service/tests/flow_limit.rs @@ -0,0 +1,290 @@ +mod utils; + +use axelar_gateway::types::Message as GatewayMessage; +use axelar_soroban_std::{assert_contract_err, assert_invoke_auth_ok, events, traits::BytesExt}; +use interchain_token_service::{ + error::ContractError, + event::FlowLimitSetEvent, + types::{HubMessage, InterchainTransfer, Message}, + InterchainTokenServiceClient, +}; +use soroban_sdk::testutils::{Address as _, Ledger as _}; +use soroban_sdk::{vec, xdr::ToXdr, Address, Bytes, BytesN, Env, String, Vec}; +use utils::{ + approve_gateway_messages, register_chains, setup_env, setup_gas_token, setup_its_token, + HUB_CHAIN, +}; + +const TEST_FLOW_LIMIT: Option = Some(1000); +const EPOCH_TIME: u64 = 6 * 60 * 60; + +fn setup_flow_limit(env: &Env, client: &InterchainTokenServiceClient) -> (BytesN<32>, Address) { + let supply = i128::MAX; + let deployer = Address::generate(env); + let token_id = setup_its_token(env, client, &deployer, supply); + + client + .mock_all_auths() + .set_flow_limit(&token_id, &TEST_FLOW_LIMIT); + + (token_id, deployer) +} + +fn create_interchain_transfer_message( + env: &Env, + client: &InterchainTokenServiceClient, + token_id: &BytesN<32>, + amount: i128, +) -> (String, String, String, Bytes, Vec) { + let sender = Address::generate(env).to_xdr(env); + let recipient = Address::generate(env).to_xdr(env); + let source_chain = client.its_hub_chain_name(); + let source_address = Address::generate(env).to_string(); + + let msg = HubMessage::ReceiveFromHub { + source_chain: String::from_str(env, HUB_CHAIN), + message: Message::InterchainTransfer(InterchainTransfer { + token_id: token_id.clone(), + source_address: sender, + destination_address: recipient, + amount, + data: None, + }), + }; + let payload = msg.abi_encode(env).unwrap(); + let payload_hash: BytesN<32> = env.crypto().keccak256(&payload).into(); + + let message_id = Address::generate(env).to_string(); + + let messages = vec![ + &env, + GatewayMessage { + source_chain: source_chain.clone(), + message_id: message_id.clone(), + source_address: source_address.clone(), + contract_address: client.address.clone(), + payload_hash, + }, + ]; + + (source_chain, message_id, source_address, payload, messages) +} + +#[test] +fn set_flow_limit_succeeds() { + let (env, client, _, _, _) = setup_env(); + let token_id = BytesN::from_array(&env, &[1; 32]); + + assert_eq!(client.flow_limit(&token_id), None); + + assert_invoke_auth_ok!( + client.operator(), + client.try_set_flow_limit(&token_id, &TEST_FLOW_LIMIT) + ); + + assert_eq!(client.flow_limit(&token_id), TEST_FLOW_LIMIT); + + goldie::assert!(events::fmt_last_emitted_event::(&env)); +} + +#[test] +#[should_panic(expected = "Error(Contract, #20)")] // FlowLimitExceeded +fn zero_flow_limit_freezes_token() { + let (env, client, gateway_client, _, signers) = setup_env(); + register_chains(&env, &client); + let (token_id, _) = setup_flow_limit(&env, &client); + + client.mock_all_auths().set_flow_limit(&token_id, &Some(0)); + + let amount = 1; + let (source_chain, message_id, source_address, payload, messages) = + create_interchain_transfer_message(&env, &client, &token_id, amount); + approve_gateway_messages(&env, &gateway_client, signers, messages); + + client.execute(&source_chain, &message_id, &source_address, &payload); +} + +#[test] +fn set_flow_limit_fails_invalid_amount() { + let (env, client, _, _, _) = setup_env(); + let token_id = BytesN::from_array(&env, &[1; 32]); + + let invalid_limit = Some(-1); + + assert_contract_err!( + client + .mock_all_auths() + .try_set_flow_limit(&token_id, &invalid_limit), + ContractError::InvalidFlowLimit + ); +} + +#[test] +fn flow_limit_resets_after_epoch() { + let (env, client, gateway_client, _, signers) = setup_env(); + register_chains(&env, &client); + let (token_id, _) = setup_flow_limit(&env, &client); + + let amount = TEST_FLOW_LIMIT.unwrap(); + + let (source_chain, message_id, source_address, payload, messages) = + create_interchain_transfer_message(&env, &client, &token_id, amount); + approve_gateway_messages(&env, &gateway_client, signers.clone(), messages); + client.execute(&source_chain, &message_id, &source_address, &payload); + assert_eq!(client.flow_in_amount(&token_id), amount); + + let current_timestamp = env.ledger().timestamp(); + env.ledger().set_timestamp(current_timestamp + EPOCH_TIME); + + let (source_chain, message_id, source_address, payload, messages) = + create_interchain_transfer_message(&env, &client, &token_id, amount); + approve_gateway_messages(&env, &gateway_client, signers, messages); + client.execute(&source_chain, &message_id, &source_address, &payload); + assert_eq!(client.flow_in_amount(&token_id), amount); +} + +#[test] +fn add_flow_in_succeeds() { + let (env, client, gateway_client, _, signers) = setup_env(); + register_chains(&env, &client); + let (token_id, _) = setup_flow_limit(&env, &client); + + let amount = TEST_FLOW_LIMIT.unwrap(); + let (source_chain, message_id, source_address, payload, messages) = + create_interchain_transfer_message(&env, &client, &token_id, amount); + approve_gateway_messages(&env, &gateway_client, signers, messages); + + assert_eq!(client.flow_in_amount(&token_id), 0); + + client.execute(&source_chain, &message_id, &source_address, &payload); + + assert_eq!(client.flow_in_amount(&token_id), amount); +} + +#[test] +#[should_panic(expected = "Error(Contract, #20)")] // ContractError::FlowLimitExceeded +fn add_flow_in_fails_exceeds_flow_limit() { + let (env, client, gateway_client, _, signers) = setup_env(); + register_chains(&env, &client); + let (token_id, _) = setup_flow_limit(&env, &client); + + let amount = TEST_FLOW_LIMIT.unwrap(); + let (source_chain, message_id, source_address, payload, messages) = + create_interchain_transfer_message(&env, &client, &token_id, amount); + approve_gateway_messages(&env, &gateway_client, signers.clone(), messages); + + client.execute(&source_chain, &message_id, &source_address, &payload); + + let second_amount = 1; + let (source_chain, message_id, source_address, payload, messages) = + create_interchain_transfer_message(&env, &client, &token_id, second_amount); + approve_gateway_messages(&env, &gateway_client, signers, messages); + + client.execute(&source_chain, &message_id, &source_address, &payload); +} + +#[test] +fn add_flow_out_succeeds() { + let (env, client, _, _, _) = setup_env(); + register_chains(&env, &client); + let (token_id, sender) = setup_flow_limit(&env, &client); + let gas_token = setup_gas_token(&env, &sender); + + let amount = 1000; + let destination_chain = String::from_str(&env, "ethereum"); + let destination_address = Bytes::from_hex(&env, "4F4495243837681061C4743b74B3eEdf548D56A5"); + let data = None; + + client + .mock_all_auths() + .set_trusted_chain(&destination_chain); + + client.mock_all_auths().interchain_transfer( + &sender, + &token_id, + &destination_chain, + &destination_address, + &amount, + &data, + &gas_token, + ); + + assert_eq!(client.flow_out_amount(&token_id), amount); +} + +#[test] +#[should_panic(expected = "Error(Contract, #20)")] // ContractError::FlowLimitExceeded +fn add_flow_out_fails_exceeds_flow_limit() { + let (env, client, _, _, _) = setup_env(); + register_chains(&env, &client); + let (token_id, sender) = setup_flow_limit(&env, &client); + let gas_token = setup_gas_token(&env, &sender); + + let amount = TEST_FLOW_LIMIT.unwrap(); + let destination_chain = String::from_str(&env, "ethereum"); + let destination_address = Bytes::from_hex(&env, "4F4495243837681061C4743b74B3eEdf548D56A5"); + let data = None; + + client + .mock_all_auths() + .set_trusted_chain(&destination_chain); + + client.mock_all_auths().interchain_transfer( + &sender, + &token_id, + &destination_chain, + &destination_address, + &amount, + &data, + &gas_token, + ); + + let second_amount = 1; + + client.mock_all_auths().interchain_transfer( + &sender, + &token_id, + &destination_chain, + &destination_address, + &second_amount, + &data, + &gas_token, + ); +} + +#[test] +#[should_panic(expected = "Error(Contract, #21)")] // ContractError::FlowAmountOverflow +fn add_flow_fails_on_flow_comparison_overflow() { + let (env, client, gateway_client, _, signers) = setup_env(); + register_chains(&env, &client); + let (token_id, sender) = setup_flow_limit(&env, &client); + let gas_token = setup_gas_token(&env, &sender); + + client + .mock_all_auths() + .set_flow_limit(&token_id, &Some(i128::MAX - 50)); + + let high_amount = i128::MAX - 100; + let (source_chain, message_id, source_address, payload, messages) = + create_interchain_transfer_message(&env, &client, &token_id, high_amount); + approve_gateway_messages(&env, &gateway_client, signers, messages); + client.execute(&source_chain, &message_id, &source_address, &payload); + + let small_amount = 100; + let destination_chain = String::from_str(&env, "ethereum"); + let destination_address = Bytes::from_hex(&env, "4F4495243837681061C4743b74B3eEdf548D56A5"); + + client + .mock_all_auths() + .set_trusted_chain(&destination_chain); + + client.mock_all_auths().interchain_transfer( + &sender, + &token_id, + &destination_chain, + &destination_address, + &small_amount, + &None, + &gas_token, + ); +} diff --git a/contracts/interchain-token-service/tests/testdata/canonical_token_id_derivation.golden b/contracts/interchain-token-service/tests/testdata/canonical_token_id_derivation.golden index 74b242c2..64ba4111 100644 --- a/contracts/interchain-token-service/tests/testdata/canonical_token_id_derivation.golden +++ b/contracts/interchain-token-service/tests/testdata/canonical_token_id_derivation.golden @@ -1,5 +1,5 @@ [ "bdfb629dd56a9581bfba5ac25009cef0ee7646acbbd1880e99d7d26ea68f0885", - "007a506f6c21cb0e84f844a86f9a8a86be868f99f35015c78ba868a7b66afc6d", - "25c162306962b7a6d3ca7e65974ca39afa329e1cea9ed3d3cdaa70e38475a73a" + "b81a06139cc7eda856e5f063ea31759be22ace7d8b4532abe85c2dd28063776b", + "001664fa3a1ee48676821e0ecd0ae89705ac1983ba91cf432358e9f6e296946f" ] \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/deploy_interchain_token_message_execute_succeeds.golden b/contracts/interchain-token-service/tests/testdata/deploy_interchain_token_message_execute_succeeds.golden index 39b8f699..fbb7c4e0 100644 --- a/contracts/interchain-token-service/tests/testdata/deploy_interchain_token_message_execute_succeeds.golden +++ b/contracts/interchain-token-service/tests/testdata/deploy_interchain_token_message_execute_succeeds.golden @@ -1,3 +1,3 @@ -contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5) -topics: (Symbol(interchain_token_deployed), BytesN<32>(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), Contract(CC2X6B2EMWFZBSQLILCJNRFQWPAD4MS4XYWW7XU5GL3LHCWD7OU6A3ZS), String(Test), String(TEST), 18, Some(Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON))) +contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON) +topics: (Symbol(interchain_token_deployed), BytesN<32>(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), Contract(CD2M6PPSH3SRJENCB2343AJLQD4HMFUE262YUBKPU4KMHKCJR24CYLKD), String(Test), String(TEST), 18, Some(Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5))) data: () \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/deploy_interchain_token_succeeds.golden b/contracts/interchain-token-service/tests/testdata/deploy_interchain_token_succeeds.golden index 1f090518..d0fe05bd 100644 --- a/contracts/interchain-token-service/tests/testdata/deploy_interchain_token_succeeds.golden +++ b/contracts/interchain-token-service/tests/testdata/deploy_interchain_token_succeeds.golden @@ -1,3 +1,3 @@ -contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5) -topics: (Symbol(interchain_token_deployed), BytesN<32>(127, 197, 48, 155, 232, 144, 233, 5, 104, 135, 130, 118, 76, 97, 230, 164, 117, 144, 45, 242, 69, 90, 124, 72, 93, 71, 115, 62, 188, 31, 2, 167), Contract(CBXOPW23I3THDRTZG2QKYC3WFGQ2Y5BEU2OAPTNLCLFFXQE3BEUJKBOQ), String(name), String(symbol), 6, Some(Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5))) +contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON) +topics: (Symbol(interchain_token_deployed), BytesN<32>(99, 53, 232, 73, 94, 142, 208, 34, 221, 37, 56, 71, 241, 2, 248, 182, 137, 194, 30, 8, 231, 132, 62, 118, 139, 56, 235, 69, 172, 181, 4, 10), Contract(CAQT3W6SGEY6V6DZTKTX5JNNABDPW75R7BWIJFUY2IN4IKCUSIMZPCNG), String(name), String(symbol), 6, Some(Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON))) data: () \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/deploy_remote_canonical_token_succeeds.golden b/contracts/interchain-token-service/tests/testdata/deploy_remote_canonical_token_succeeds.golden index 6cfdc395..e2c61045 100644 --- a/contracts/interchain-token-service/tests/testdata/deploy_remote_canonical_token_succeeds.golden +++ b/contracts/interchain-token-service/tests/testdata/deploy_remote_canonical_token_succeeds.golden @@ -1,3 +1,3 @@ -contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5) -topics: (Symbol(token_deployment_started), BytesN<32>(242, 242, 113, 171, 85, 152, 84, 8, 108, 160, 123, 152, 207, 224, 199, 212, 81, 160, 129, 59, 136, 60, 3, 77, 101, 229, 115, 108, 194, 43, 206, 19), Contract(CCF7HWZ6PBT2MQ6AUBKXM4CPWAR2PO6ND34WXLB32NDAYD7KIKBAAS7I), String(ethereum), String(aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA35JU), String(aaa), 7, None) +contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON) +topics: (Symbol(token_deployment_started), BytesN<32>(176, 82, 11, 134, 46, 148, 119, 103, 208, 219, 201, 52, 236, 230, 170, 39, 215, 103, 118, 78, 122, 72, 213, 57, 75, 143, 90, 231, 223, 12, 26, 102), Contract(CD6J4NTZEADXNETD2UY7ALD2N26RY35GGC73JSXXZB2GKB3ZTLR3Y4FH), String(ethereum), String(aaa:GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5FQE), String(aaa), 7, None) data: () \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/deploy_remote_interchain_token_succeeds.golden b/contracts/interchain-token-service/tests/testdata/deploy_remote_interchain_token_succeeds.golden index 23dcca6d..fd5d82ad 100644 --- a/contracts/interchain-token-service/tests/testdata/deploy_remote_interchain_token_succeeds.golden +++ b/contracts/interchain-token-service/tests/testdata/deploy_remote_interchain_token_succeeds.golden @@ -1,3 +1,3 @@ -contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5) -topics: (Symbol(token_deployment_started), BytesN<32>(127, 197, 48, 155, 232, 144, 233, 5, 104, 135, 130, 118, 76, 97, 230, 164, 117, 144, 45, 242, 69, 90, 124, 72, 93, 71, 115, 62, 188, 31, 2, 167), Contract(CBXOPW23I3THDRTZG2QKYC3WFGQ2Y5BEU2OAPTNLCLFFXQE3BEUJKBOQ), String(ethereum), String(name), String(symbol), 6, None) +contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON) +topics: (Symbol(token_deployment_started), BytesN<32>(99, 53, 232, 73, 94, 142, 208, 34, 221, 37, 56, 71, 241, 2, 248, 182, 137, 194, 30, 8, 231, 132, 62, 118, 139, 56, 235, 69, 172, 181, 4, 10), Contract(CAQT3W6SGEY6V6DZTKTX5JNNABDPW75R7BWIJFUY2IN4IKCUSIMZPCNG), String(ethereum), String(name), String(symbol), 6, None) data: () \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/interchain_transfer_execute_succeeds.golden b/contracts/interchain-token-service/tests/testdata/interchain_transfer_execute_succeeds.golden index 4aa7effc..e480984b 100644 --- a/contracts/interchain-token-service/tests/testdata/interchain_transfer_execute_succeeds.golden +++ b/contracts/interchain-token-service/tests/testdata/interchain_transfer_execute_succeeds.golden @@ -1,3 +1,3 @@ -contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON) -topics: (Symbol(executed), String(axelar), String(test), Bytes(0, 0, 0, 18, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10), BytesN<32>(127, 60, 199, 172, 30, 8, 21, 148, 10, 241, 246, 188, 96, 212, 234, 84, 53, 106, 109, 125, 167, 249, 31, 45, 164, 255, 214, 11, 85, 184, 125, 106), Contract(CDVVTYOBZQXIA6CA7UDEZMPS45IWRXW3NBEWON44FXYMBLSRSAS3BAU6), 1000) +contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5) +topics: (Symbol(executed), String(axelar), String(test), Bytes(0, 0, 0, 18, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11), BytesN<32>(19, 164, 112, 128, 76, 247, 2, 236, 196, 20, 87, 206, 58, 2, 208, 102, 225, 238, 175, 151, 45, 250, 47, 204, 76, 253, 158, 193, 68, 73, 208, 149), Contract(CCG4OE5Y32VT7WLMQCATZYK6KJDCUFY5XBSIHW7TEGXVR4ZD27YFN2N2), 1000) data: (Bytes(222, 173)) \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/interchain_transfer_message_execute_succeeds.golden b/contracts/interchain-token-service/tests/testdata/interchain_transfer_message_execute_succeeds.golden index 7d199c0d..64c914fa 100644 --- a/contracts/interchain-token-service/tests/testdata/interchain_transfer_message_execute_succeeds.golden +++ b/contracts/interchain-token-service/tests/testdata/interchain_transfer_message_execute_succeeds.golden @@ -1,3 +1,3 @@ -contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5) -topics: (Symbol(interchain_transfer_received), String(axelar), BytesN<32>(127, 60, 199, 172, 30, 8, 21, 148, 10, 241, 246, 188, 96, 212, 234, 84, 53, 106, 109, 125, 167, 249, 31, 45, 164, 255, 214, 11, 85, 184, 125, 106), Bytes(0, 0, 0, 18, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9), Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5), 1000) +contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON) +topics: (Symbol(interchain_transfer_received), String(axelar), BytesN<32>(19, 164, 112, 128, 76, 247, 2, 236, 196, 20, 87, 206, 58, 2, 208, 102, 225, 238, 175, 151, 45, 250, 47, 204, 76, 253, 158, 193, 68, 73, 208, 149), Bytes(0, 0, 0, 18, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10), Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXI7N), 1000) data: (None) \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/interchain_transfer_send_succeeds.golden b/contracts/interchain-token-service/tests/testdata/interchain_transfer_send_succeeds.golden index d578ac03..9be88994 100644 --- a/contracts/interchain-token-service/tests/testdata/interchain_transfer_send_succeeds.golden +++ b/contracts/interchain-token-service/tests/testdata/interchain_transfer_send_succeeds.golden @@ -1,3 +1,3 @@ -contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5) -topics: (Symbol(interchain_transfer_sent), BytesN<32>(127, 197, 48, 155, 232, 144, 233, 5, 104, 135, 130, 118, 76, 97, 230, 164, 117, 144, 45, 242, 69, 90, 124, 72, 93, 71, 115, 62, 188, 31, 2, 167), Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON), String(ethereum), Bytes(79, 68, 149, 36, 56, 55, 104, 16, 97, 196, 116, 59, 116, 179, 238, 223, 84, 141, 86, 165), 1000) +contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON) +topics: (Symbol(interchain_transfer_sent), BytesN<32>(99, 53, 232, 73, 94, 142, 208, 34, 221, 37, 56, 71, 241, 2, 248, 182, 137, 194, 30, 8, 231, 132, 62, 118, 139, 56, 235, 69, 172, 181, 4, 10), Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5), String(ethereum), Bytes(79, 68, 149, 36, 56, 55, 104, 16, 97, 196, 116, 59, 116, 179, 238, 223, 84, 141, 86, 165), 1000) data: (Some(Bytes(171, 205))) \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/register_canonical_token_succeeds.golden b/contracts/interchain-token-service/tests/testdata/register_canonical_token_succeeds.golden index 40453992..b2387d3b 100644 --- a/contracts/interchain-token-service/tests/testdata/register_canonical_token_succeeds.golden +++ b/contracts/interchain-token-service/tests/testdata/register_canonical_token_succeeds.golden @@ -1,3 +1,3 @@ -contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5) -topics: (Symbol(interchain_token_id_claimed), BytesN<32>(37, 193, 98, 48, 105, 98, 183, 166, 211, 202, 126, 101, 151, 76, 163, 154, 250, 50, 158, 28, 234, 158, 211, 211, 205, 170, 112, 227, 132, 117, 167, 58), AccountId(GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF), BytesN<32>(0, 122, 80, 111, 108, 33, 203, 14, 132, 248, 68, 168, 111, 154, 138, 134, 190, 134, 143, 153, 243, 80, 21, 199, 139, 168, 104, 167, 182, 106, 252, 109)) +contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON) +topics: (Symbol(interchain_token_id_claimed), BytesN<32>(0, 22, 100, 250, 58, 30, 228, 134, 118, 130, 30, 14, 205, 10, 232, 151, 5, 172, 25, 131, 186, 145, 207, 67, 35, 88, 233, 246, 226, 150, 148, 111), AccountId(GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF), BytesN<32>(184, 26, 6, 19, 156, 199, 237, 168, 86, 229, 240, 99, 234, 49, 117, 155, 226, 42, 206, 125, 139, 69, 50, 171, 232, 92, 45, 210, 128, 99, 119, 107)) data: () \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/remove_trusted_chain.golden b/contracts/interchain-token-service/tests/testdata/remove_trusted_chain.golden index 1d00b35d..4f4410d4 100644 --- a/contracts/interchain-token-service/tests/testdata/remove_trusted_chain.golden +++ b/contracts/interchain-token-service/tests/testdata/remove_trusted_chain.golden @@ -1,3 +1,3 @@ -contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5) +contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON) topics: (Symbol(trusted_chain_removed), String(chain)) data: () \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/set_flow_limit_succeeds.golden b/contracts/interchain-token-service/tests/testdata/set_flow_limit_succeeds.golden new file mode 100644 index 00000000..faaf4a6b --- /dev/null +++ b/contracts/interchain-token-service/tests/testdata/set_flow_limit_succeeds.golden @@ -0,0 +1,3 @@ +contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON) +topics: (Symbol(flow_limit_set), BytesN<32>(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), Some(1000)) +data: () \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/testdata/set_trusted_address.golden b/contracts/interchain-token-service/tests/testdata/set_trusted_address.golden index e9ee4dbf..555d3462 100644 --- a/contracts/interchain-token-service/tests/testdata/set_trusted_address.golden +++ b/contracts/interchain-token-service/tests/testdata/set_trusted_address.golden @@ -1,3 +1,3 @@ -contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARQG5) +contract: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATYON) topics: (Symbol(trusted_chain_set), String(chain)) data: () \ No newline at end of file diff --git a/contracts/interchain-token-service/tests/utils/mod.rs b/contracts/interchain-token-service/tests/utils/mod.rs index 468798f9..e0c80239 100644 --- a/contracts/interchain-token-service/tests/utils/mod.rs +++ b/contracts/interchain-token-service/tests/utils/mod.rs @@ -26,6 +26,7 @@ fn setup_its<'a>( gas_service: &AxelarGasServiceClient, ) -> InterchainTokenServiceClient<'a> { let owner = Address::generate(env); + let operator = Address::generate(env); let its_hub_address = String::from_str(env, "its_hub_address"); let chain_name = String::from_str(env, "chain_name"); @@ -39,6 +40,7 @@ fn setup_its<'a>( InterchainTokenService, ( &owner, + &operator, &gateway.address, &gas_service.address, its_hub_address, @@ -117,7 +119,7 @@ pub fn register_chains(env: &Env, client: &InterchainTokenServiceClient) { #[allow(dead_code)] pub fn approve_gateway_messages( env: &Env, - gateway_client: AxelarGatewayClient, + gateway_client: &AxelarGatewayClient, signers: TestSignerSet, messages: Vec, ) { diff --git a/packages/axelar-soroban-std/src/events.rs b/packages/axelar-soroban-std/src/events.rs index 00bcb190..175a4215 100644 --- a/packages/axelar-soroban-std/src/events.rs +++ b/packages/axelar-soroban-std/src/events.rs @@ -1,5 +1,5 @@ use core::fmt::Debug; -use soroban_sdk::{Env, IntoVal, Topics, Val}; +use soroban_sdk::{Env, IntoVal, Topics, Val, Vec}; #[cfg(any(test, feature = "testutils"))] pub use testutils::*; @@ -7,7 +7,10 @@ pub use testutils::*; pub trait Event: Debug + PartialEq { fn topics(&self, env: &Env) -> impl Topics + Debug; - fn data(&self, env: &Env) -> impl IntoVal + Debug; + /// A default empty tuple/vector is used for event data, since majority of events only use topics. + fn data(&self, env: &Env) -> impl IntoVal + Debug { + Vec::::new(env) + } fn emit(&self, env: &Env) { env.events().publish(self.topics(env), self.data(env));