diff --git a/contracts/stellar-example/src/tests/testdata/its_example.golden b/contracts/stellar-example/src/tests/testdata/its_example.golden index 12bae6e1..a805d1db 100644 --- a/contracts/stellar-example/src/tests/testdata/its_example.golden +++ b/contracts/stellar-example/src/tests/testdata/its_example.golden @@ -29,7 +29,7 @@ TokenReceivedEvent { message_id: String(message-id), source_address: Bytes(67, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 68, 50, 75, 77), token_id: 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), - token_address: Contract(CBBOW4DL4KLP3B46GBQKYO7R4EE3XIT4GX5D2ETGQ43BOPF2CPVYI5KR), + token_address: Contract(CC3SQJDCS3OA77SQI6ODO2FMMQHYS57O352TXNQ7EXE4AXVRPXX3WJ5U), amount: 1000, payload: Bytes(67, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 52, 66, 86, 53), } diff --git a/contracts/stellar-interchain-token-service/src/contract.rs b/contracts/stellar-interchain-token-service/src/contract.rs index e1847605..067642f1 100644 --- a/contracts/stellar-interchain-token-service/src/contract.rs +++ b/contracts/stellar-interchain-token-service/src/contract.rs @@ -18,9 +18,8 @@ use stellar_interchain_token::InterchainTokenClient; use crate::error::ContractError; use crate::event::{ - InterchainTokenDeployedEvent, InterchainTokenDeploymentStartedEvent, - InterchainTokenIdClaimedEvent, InterchainTransferReceivedEvent, InterchainTransferSentEvent, - TokenManagerDeployedEvent, TrustedChainRemovedEvent, TrustedChainSetEvent, + InterchainTokenDeploymentStartedEvent, InterchainTransferReceivedEvent, + InterchainTransferSentEvent, TrustedChainRemovedEvent, TrustedChainSetEvent, }; use crate::flow_limit::FlowDirection; use crate::interface::InterchainTokenServiceInterface; @@ -29,13 +28,9 @@ use crate::token_metadata::TokenMetadataExt; use crate::types::{ DeployInterchainToken, HubMessage, InterchainTransfer, Message, TokenManagerType, }; -use crate::{flow_limit, token_handler, token_metadata}; +use crate::{deployer, flow_limit, token_handler, token_id, token_metadata}; const ITS_HUB_CHAIN_NAME: &str = "axelar"; -const PREFIX_INTERCHAIN_TOKEN_ID: &str = "its-interchain-token-id"; -const PREFIX_INTERCHAIN_TOKEN_SALT: &str = "interchain-token-salt"; -const PREFIX_CANONICAL_TOKEN_SALT: &str = "canonical-token-salt"; -const PREFIX_TOKEN_MANAGER: &str = "token-manager-id"; const EXECUTE_WITH_INTERCHAIN_TOKEN: &str = "execute_with_interchain_token"; #[contract] @@ -169,32 +164,20 @@ impl InterchainTokenServiceInterface for InterchainTokenService { Ok(()) } - fn interchain_token_deploy_salt(env: &Env, deployer: Address, salt: BytesN<32>) -> BytesN<32> { - let chain_name_hash = Self::chain_name_hash(env); - env.crypto() - .keccak256( - &( - PREFIX_INTERCHAIN_TOKEN_SALT, - chain_name_hash, - deployer, - salt, - ) - .to_xdr(env), - ) - .into() + fn interchain_token_id(env: &Env, deployer: Address, salt: BytesN<32>) -> BytesN<32> { + token_id::interchain_token_id(env, Self::chain_name_hash(env), deployer, salt) } - fn interchain_token_id(env: &Env, sender: Address, salt: BytesN<32>) -> BytesN<32> { - env.crypto() - .keccak256(&(PREFIX_INTERCHAIN_TOKEN_ID, sender, salt).to_xdr(env)) - .into() + fn canonical_interchain_token_id(env: &Env, token_address: Address) -> BytesN<32> { + token_id::canonical_interchain_token_id(env, Self::chain_name_hash(env), token_address) } - fn canonical_token_deploy_salt(env: &Env, token_address: Address) -> BytesN<32> { - let chain_name_hash = Self::chain_name_hash(env); - env.crypto() - .keccak256(&(PREFIX_CANONICAL_TOKEN_SALT, chain_name_hash, token_address).to_xdr(env)) - .into() + fn interchain_token_address(env: &Env, token_id: BytesN<32>) -> Address { + deployer::interchain_token_address(env, token_id) + } + + fn token_manager_address(env: &Env, token_id: BytesN<32>) -> Address { + deployer::token_manager_address(env, token_id) } fn token_address(env: &Env, token_id: BytesN<32>) -> Address { @@ -250,39 +233,16 @@ impl InterchainTokenServiceInterface for InterchainTokenService { ensure!(initial_supply >= 0, ContractError::InvalidSupply); - let deploy_salt = Self::interchain_token_deploy_salt(env, caller.clone(), salt); - let token_id = Self::interchain_token_id(env, Address::zero(env), deploy_salt); + let token_id = Self::interchain_token_id(env, caller.clone(), salt); token_metadata.validate()?; - let token_address = - Self::deploy_interchain_token_contract(env, minter, token_id.clone(), token_metadata); - - let token_manager_type = TokenManagerType::NativeInterchainToken; - let token_manager_address = Self::deploy_token_manager_contract( - env, - token_id.clone(), - token_address.clone(), - token_manager_type, - ); - let interchain_token_client = InterchainTokenClient::new(env, &token_address); - - if initial_supply > 0 { - StellarAssetClient::new(env, &token_address).mint(&caller, &initial_supply); - } - - // Transfer minter role to token manager - interchain_token_client.add_minter(&token_manager_address); - interchain_token_client.remove_minter(&env.current_contract_address()); - - Self::set_token_id_config( + Self::deploy_token( env, token_id.clone(), - TokenIdConfigValue { - token_address, - token_manager: token_manager_address, - token_manager_type, - }, + token_metadata, + minter, + Some((initial_supply, caller)), ); Ok(token_id) @@ -298,9 +258,11 @@ impl InterchainTokenServiceInterface for InterchainTokenService { ) -> Result, ContractError> { caller.require_auth(); - let deploy_salt = Self::interchain_token_deploy_salt(env, caller.clone(), salt); + let token_id = Self::interchain_token_id(env, caller.clone(), salt); + + Self::deploy_remote_token(env, caller, token_id.clone(), destination_chain, gas_token)?; - Self::deploy_remote_token(env, caller, deploy_salt, destination_chain, gas_token) + Ok(token_id) } #[when_not_paused] @@ -308,8 +270,7 @@ impl InterchainTokenServiceInterface for InterchainTokenService { env: &Env, token_address: Address, ) -> Result, ContractError> { - let deploy_salt = Self::canonical_token_deploy_salt(env, token_address.clone()); - let token_id = Self::interchain_token_id(env, Address::zero(env), deploy_salt.clone()); + let token_id = Self::canonical_interchain_token_id(env, token_address.clone()); ensure!( !env.storage() @@ -318,29 +279,11 @@ impl InterchainTokenServiceInterface for InterchainTokenService { ContractError::TokenAlreadyRegistered ); - let token_manager_type = TokenManagerType::LockUnlock; - let token_manager_address = Self::deploy_token_manager_contract( - env, - token_id.clone(), - token_address.clone(), - token_manager_type, - ); - - InterchainTokenIdClaimedEvent { - token_id: token_id.clone(), - deployer: Address::zero(env), - salt: deploy_salt, - } - .emit(env); - - Self::set_token_id_config( + let _ = Self::deploy_token_manager( env, token_id.clone(), - TokenIdConfigValue { - token_address, - token_manager: token_manager_address, - token_manager_type, - }, + token_address, + TokenManagerType::LockUnlock, ); Ok(token_id) @@ -356,10 +299,9 @@ impl InterchainTokenServiceInterface for InterchainTokenService { ) -> Result, ContractError> { spender.require_auth(); - let deploy_salt = Self::canonical_token_deploy_salt(env, token_address); + let token_id = Self::canonical_interchain_token_id(env, token_address); - let token_id = - Self::deploy_remote_token(env, spender, deploy_salt, destination_chain, gas_token)?; + Self::deploy_remote_token(env, spender, token_id.clone(), destination_chain, gas_token)?; Ok(token_id) } @@ -456,17 +398,17 @@ impl InterchainTokenService { message: Message, gas_token: Token, ) -> Result<(), ContractError> { - // Note: ITS Hub chain as the actual destination chain for the messsage isn't supported ensure!( Self::is_trusted_chain(env, destination_chain.clone()), ContractError::UntrustedChain ); + extend_persistent_ttl(env, &DataKey::TrustedChain(destination_chain.clone())); let gateway = AxelarGatewayMessagingClient::new(env, &Self::gateway(env)); let gas_service = AxelarGasServiceClient::new(env, &Self::gas_service(env)); let payload = HubMessage::SendToHub { - destination_chain: destination_chain.clone(), + destination_chain, message, } .abi_encode(env)?; @@ -491,7 +433,6 @@ impl InterchainTokenService { &payload, ); - extend_persistent_ttl(env, &DataKey::TrustedChain(destination_chain)); extend_instance_ttl(env); Ok(()) @@ -548,6 +489,7 @@ impl InterchainTokenService { Self::is_trusted_chain(env, original_source_chain.clone()), ContractError::UntrustedChain ); + extend_persistent_ttl(env, &DataKey::TrustedChain(original_source_chain.clone())); Ok((original_source_chain, message)) } @@ -602,12 +544,6 @@ impl InterchainTokenService { env.crypto().keccak256(&chain_name.to_xdr(env)).into() } - fn token_manager_salt(env: &Env, token_id: BytesN<32>) -> BytesN<32> { - env.crypto() - .keccak256(&(PREFIX_TOKEN_MANAGER, token_id).to_xdr(env)) - .into() - } - /// Deploys a remote token on a specified destination chain. /// /// This function retrieves and validates the token's metadata @@ -616,13 +552,10 @@ impl InterchainTokenService { /// /// # Arguments /// * `caller` - Address of the caller initiating the deployment. - /// * `deploy_salt` - Unique salt used for token deployment. + /// * `token_id` - The token ID for the remote token being deployed. /// * `destination_chain` - The name of the destination chain where the token will be deployed. /// * `gas_token` - The token used to pay for gas during the deployment. /// - /// # Returns - /// - `Ok(BytesN<32>)`: Returns the token ID. - /// /// # Errors /// - `ContractError::InvalidDestinationChain`: If the `destination_chain` is the current chain. /// - `ContractError::InvalidTokenId`: If the token ID is invalid. @@ -634,16 +567,15 @@ impl InterchainTokenService { fn deploy_remote_token( env: &Env, caller: Address, - deploy_salt: BytesN<32>, + token_id: BytesN<32>, destination_chain: String, gas_token: Token, - ) -> Result, ContractError> { + ) -> Result<(), ContractError> { ensure!( destination_chain != Self::chain_name(env), ContractError::InvalidDestinationChain ); - let token_id = Self::interchain_token_id(env, Address::zero(env), deploy_salt); let token_address = Self::token_id_config(env, token_id.clone())?.token_address; let TokenMetadata { name, @@ -660,7 +592,7 @@ impl InterchainTokenService { }); InterchainTokenDeploymentStartedEvent { - token_id: token_id.clone(), + token_id, token_address, destination_chain: destination_chain.clone(), name, @@ -672,67 +604,7 @@ impl InterchainTokenService { Self::pay_gas_and_call_contract(env, caller, destination_chain, message, gas_token)?; - Ok(token_id) - } - - fn deploy_interchain_token_contract( - env: &Env, - minter: Option
, - token_id: BytesN<32>, - token_metadata: TokenMetadata, - ) -> Address { - let deployed_address = env - .deployer() - .with_address(env.current_contract_address(), token_id.clone()) - .deploy_v2( - Self::interchain_token_wasm_hash(env), - ( - env.current_contract_address(), - minter.clone(), - token_id.clone(), - token_metadata.clone(), - ), - ); - - InterchainTokenDeployedEvent { - token_id, - token_address: deployed_address.clone(), - name: token_metadata.name, - symbol: token_metadata.symbol, - decimals: token_metadata.decimal, - minter, - } - .emit(env); - - deployed_address - } - - fn deploy_token_manager_contract( - env: &Env, - token_id: BytesN<32>, - token_address: Address, - token_manager_type: TokenManagerType, - ) -> Address { - let deployed_address = env - .deployer() - .with_address( - env.current_contract_address(), - Self::token_manager_salt(env, token_id.clone()), - ) - .deploy_v2( - Self::token_manager_wasm_hash(env), - (env.current_contract_address(),), - ); - - TokenManagerDeployedEvent { - token_id, - token_address, - token_manager: deployed_address.clone(), - token_manager_type, - } - .emit(env); - - deployed_address + Ok(()) } fn execute_transfer_message( @@ -834,31 +706,78 @@ impl InterchainTokenService { // Note: attempt to convert a byte string which doesn't represent a valid Soroban address fails at the Host level let minter = minter.map(|m| Address::from_string_bytes(&m)); - let token_address = - Self::deploy_interchain_token_contract(env, minter, token_id.clone(), token_metadata); + Self::deploy_token(env, token_id, token_metadata, minter, None); - let token_manager_address = Self::deploy_token_manager_contract( + Ok(()) + } + + fn deploy_token_manager( + env: &Env, + token_id: BytesN<32>, + token_address: Address, + token_manager_type: TokenManagerType, + ) -> Address { + let token_manager = deployer::deploy_token_manager( env, + Self::token_manager_wasm_hash(env), token_id.clone(), token_address.clone(), - TokenManagerType::NativeInterchainToken, + token_manager_type, ); - // Transfer minter role to token manager - let interchain_token_client = InterchainTokenClient::new(env, &token_address); - interchain_token_client.add_minter(&token_manager_address); - interchain_token_client.remove_minter(&env.current_contract_address()); - Self::set_token_id_config( env, token_id, TokenIdConfigValue { token_address, - token_manager: token_manager_address, - token_manager_type: TokenManagerType::NativeInterchainToken, + token_manager: token_manager.clone(), + token_manager_type, }, ); - Ok(()) + token_manager + } + + /// Deploy an interchain token on the current chain and its corresponding token manager. + /// + /// # Arguments + /// * `token_id` - The token ID for the interchain token being deployed. + /// * `token_metadata` - The metadata for the interchain token being deployed. + /// * `minter` - An optional address of an additional minter for the interchain token being deployed. + /// * `initial_mint` - The initial mint amount and recipient for the interchain token being deployed. + fn deploy_token( + env: &Env, + token_id: BytesN<32>, + token_metadata: TokenMetadata, + minter: Option
, + initial_mint: Option<(i128, Address)>, + ) { + let token_address = deployer::deploy_interchain_token( + env, + Self::interchain_token_wasm_hash(env), + minter, + token_id.clone(), + token_metadata, + ); + let interchain_token_client = InterchainTokenClient::new(env, &token_address); + + let token_manager = Self::deploy_token_manager( + env, + token_id, + token_address, + TokenManagerType::NativeInterchainToken, + ); + + match initial_mint { + Some((initial_supply, recipient)) if initial_supply > 0 => { + StellarAssetClient::new(env, &interchain_token_client.address) + .mint(&recipient, &initial_supply); + } + _ => {} + } + + // Transfer minter role to the token manager + interchain_token_client.add_minter(&token_manager); + interchain_token_client.remove_minter(&env.current_contract_address()); } } diff --git a/contracts/stellar-interchain-token-service/src/deployer.rs b/contracts/stellar-interchain-token-service/src/deployer.rs new file mode 100644 index 00000000..62c0daab --- /dev/null +++ b/contracts/stellar-interchain-token-service/src/deployer.rs @@ -0,0 +1,92 @@ +use soroban_sdk::xdr::ToXdr; +use soroban_sdk::{Address, BytesN, Env}; +use soroban_token_sdk::metadata::TokenMetadata; +use stellar_axelar_std::events::Event; + +use crate::event::{InterchainTokenDeployedEvent, TokenManagerDeployedEvent}; +use crate::types::TokenManagerType; + +/// This prefix along with the tokenId is used to generate the salt for the deterministic interchain token deployment +const PREFIX_INTERCHAIN_TOKEN_DEPLOYMENT_SALT: &str = "its-interchain-token-salt"; +/// This prefix, along with the tokenId, is used to generate the salt for the deterministic token manager deployment +const PREFIX_TOKEN_MANAGER_DEPLOYMENT_SALT: &str = "its-token-manager-salt"; + +fn interchain_token_deployment_salt(env: &Env, token_id: BytesN<32>) -> BytesN<32> { + env.crypto() + .keccak256(&(PREFIX_INTERCHAIN_TOKEN_DEPLOYMENT_SALT, token_id).to_xdr(env)) + .into() +} + +pub fn interchain_token_address(env: &Env, token_id: BytesN<32>) -> Address { + env.deployer() + .with_current_contract(interchain_token_deployment_salt(env, token_id)) + .deployed_address() +} + +fn token_manager_deployment_salt(env: &Env, token_id: BytesN<32>) -> BytesN<32> { + env.crypto() + .keccak256(&(PREFIX_TOKEN_MANAGER_DEPLOYMENT_SALT, token_id).to_xdr(env)) + .into() +} + +pub fn token_manager_address(env: &Env, token_id: BytesN<32>) -> Address { + env.deployer() + .with_current_contract(token_manager_deployment_salt(env, token_id)) + .deployed_address() +} + +pub fn deploy_interchain_token( + env: &Env, + interchain_token_wasm_hash: BytesN<32>, + minter: Option
, + token_id: BytesN<32>, + token_metadata: TokenMetadata, +) -> Address { + let deployed_address = env + .deployer() + .with_current_contract(interchain_token_deployment_salt(env, token_id.clone())) + .deploy_v2( + interchain_token_wasm_hash, + ( + env.current_contract_address(), + minter.clone(), + token_id.clone(), + token_metadata.clone(), + ), + ); + + InterchainTokenDeployedEvent { + token_id, + token_address: deployed_address.clone(), + name: token_metadata.name, + symbol: token_metadata.symbol, + decimals: token_metadata.decimal, + minter, + } + .emit(env); + + deployed_address +} + +pub fn deploy_token_manager( + env: &Env, + token_manager_wasm_hash: BytesN<32>, + token_id: BytesN<32>, + token_address: Address, + token_manager_type: TokenManagerType, +) -> Address { + let deployed_address = env + .deployer() + .with_current_contract(token_manager_deployment_salt(env, token_id.clone())) + .deploy_v2(token_manager_wasm_hash, (env.current_contract_address(),)); + + TokenManagerDeployedEvent { + token_id, + token_address, + token_manager: deployed_address.clone(), + token_manager_type, + } + .emit(env); + + deployed_address +} diff --git a/contracts/stellar-interchain-token-service/src/event.rs b/contracts/stellar-interchain-token-service/src/event.rs index c5d8e64f..3ffe8394 100644 --- a/contracts/stellar-interchain-token-service/src/event.rs +++ b/contracts/stellar-interchain-token-service/src/event.rs @@ -52,13 +52,6 @@ pub struct InterchainTokenDeploymentStartedEvent { pub minter: Option
, } -#[derive(Debug, PartialEq, Eq, IntoEvent)] -pub struct InterchainTokenIdClaimedEvent { - pub token_id: BytesN<32>, - pub deployer: Address, - pub salt: BytesN<32>, -} - #[derive(Debug, PartialEq, Eq, IntoEvent)] pub struct InterchainTransferSentEvent { pub token_id: BytesN<32>, diff --git a/contracts/stellar-interchain-token-service/src/interface.rs b/contracts/stellar-interchain-token-service/src/interface.rs index fbf5c679..775c1b7c 100644 --- a/contracts/stellar-interchain-token-service/src/interface.rs +++ b/contracts/stellar-interchain-token-service/src/interface.rs @@ -52,29 +52,17 @@ pub trait InterchainTokenServiceInterface: /// - Must be called by the [`Self::owner`]. fn remove_trusted_chain(env: &Env, chain: String) -> Result<(), ContractError>; - /// Computes a 32-byte deployment salt for a new interchain token. - /// - /// The deployment salt is derived uniquely for the given chain, deployer, and salt combination. - /// - /// # Parameters - /// - `deployer`: The address of the token deployer. - /// - `salt`: A unique value provided by the deployer. - /// - /// # Returns - /// - A `BytesN<32>` value representing the computed deployment salt. - fn interchain_token_deploy_salt(env: &Env, deployer: Address, salt: BytesN<32>) -> BytesN<32>; - /// Computes the unique identifier for an interchain token. /// - /// The token ID is derived uniquely from the sender's address and the provided salt. + /// The token ID is derived uniquely from the deployer's address and the provided salt. /// /// # Parameters - /// - `sender`: The address of the token deployer. In the case of tokens deployed by this contract, it will be Stellar's "dead" address. + /// - `deployer`: The address of the token deployer. In the case of tokens deployed by this contract, it will be Stellar's "dead" address. /// - `salt`: A unique value used to generate the token ID. /// /// # Returns /// - A `BytesN<32>` value representing the token's unique ID. - fn interchain_token_id(env: &Env, sender: Address, salt: BytesN<32>) -> BytesN<32>; + fn interchain_token_id(env: &Env, deployer: Address, salt: BytesN<32>) -> BytesN<32>; /// Computes a 32-byte deployment salt for a canonical token. /// @@ -85,7 +73,25 @@ pub trait InterchainTokenServiceInterface: /// /// # Returns /// - A `BytesN<32>` value representing the computed deployment salt. - fn canonical_token_deploy_salt(env: &Env, token_address: Address) -> BytesN<32>; + fn canonical_interchain_token_id(env: &Env, token_address: Address) -> BytesN<32>; + + /// Returns the predicted address of the native interchain token associated with the specified token ID. + /// + /// # Arguments + /// - `token_id`: The token ID for the interchain token. + /// + /// # Returns + /// - `Address`: The address of the interchain token. + fn interchain_token_address(env: &Env, token_id: BytesN<32>) -> Address; + + /// Returns the predicted address of the token manager that will be deployed for the specified token ID. + /// + /// # Arguments + /// - `token_id`: The token ID for the interchain token. + /// + /// # Returns + /// - `Address`: The address of the token manager. + fn token_manager_address(env: &Env, token_id: BytesN<32>) -> Address; /// Returns the address of the token associated with the specified token ID. fn token_address(env: &Env, token_id: BytesN<32>) -> Address; diff --git a/contracts/stellar-interchain-token-service/src/lib.rs b/contracts/stellar-interchain-token-service/src/lib.rs index 0f773286..c133771b 100644 --- a/contracts/stellar-interchain-token-service/src/lib.rs +++ b/contracts/stellar-interchain-token-service/src/lib.rs @@ -19,8 +19,10 @@ cfg_if::cfg_if! { pub use interface::{InterchainTokenServiceClient, InterchainTokenServiceInterface}; } else { mod abi; + mod deployer; pub mod event; mod storage_types; + mod token_id; mod token_manager; mod token_metadata; mod token_handler; diff --git a/contracts/stellar-interchain-token-service/src/tests/deploy_interchain_token.rs b/contracts/stellar-interchain-token-service/src/tests/deploy_interchain_token.rs index 414a11a6..7bffb7ee 100644 --- a/contracts/stellar-interchain-token-service/src/tests/deploy_interchain_token.rs +++ b/contracts/stellar-interchain-token-service/src/tests/deploy_interchain_token.rs @@ -1,7 +1,6 @@ use soroban_sdk::testutils::Address as _; use soroban_sdk::{Address, BytesN, Env}; use soroban_token_sdk::metadata::TokenMetadata; -use stellar_axelar_std::address::AddressExt; use stellar_axelar_std::{assert_auth, assert_auth_err, assert_contract_err, events}; use stellar_interchain_token::InterchainTokenClient; @@ -130,8 +129,7 @@ fn deploy_interchain_token_check_token_id_and_token_manager_type() { let minter = Some(Address::generate(&env)); let initial_supply = 100; - let deploy_salt = client.interchain_token_deploy_salt(&sender, &salt); - let expected_token_id = client.interchain_token_id(&Address::zero(&env), &deploy_salt); + let expected_token_id = client.interchain_token_id(&sender, &salt); let token_id = assert_auth!( &sender, diff --git a/contracts/stellar-interchain-token-service/src/tests/deploy_remote_canonical_token.rs b/contracts/stellar-interchain-token-service/src/tests/deploy_remote_canonical_token.rs index 61650b76..017ec067 100644 --- a/contracts/stellar-interchain-token-service/src/tests/deploy_remote_canonical_token.rs +++ b/contracts/stellar-interchain-token-service/src/tests/deploy_remote_canonical_token.rs @@ -3,7 +3,6 @@ use soroban_sdk::token::{self, StellarAssetClient}; use soroban_sdk::{Address, Bytes, BytesN, IntoVal, String, Symbol}; use soroban_token_sdk::metadata::TokenMetadata; use stellar_axelar_gas_service::testutils::setup_gas_token; -use stellar_axelar_std::address::AddressExt; use stellar_axelar_std::{auth_invocation, events}; use super::utils::{setup_env, TokenMetadataExt}; @@ -23,8 +22,7 @@ fn deploy_remote_canonical_token_succeeds() { .mint(&spender, &initial_amount); let token_address = asset.address(); - let expected_deploy_salt = client.canonical_token_deploy_salt(&token_address); - let expected_id = client.interchain_token_id(&Address::zero(&env), &expected_deploy_salt); + let expected_id = client.canonical_interchain_token_id(&token_address); assert_eq!(client.register_canonical_token(&token_address), expected_id); assert_eq!(client.token_address(&expected_id), token_address); assert_eq!( @@ -171,8 +169,7 @@ fn deploy_remote_canonical_token_fail_no_actual_token() { let spender = Address::generate(&env); let gas_token = setup_gas_token(&env, &spender); let token_address = Address::generate(&env); - let expected_deploy_salt = client.canonical_token_deploy_salt(&token_address); - let expected_id = client.interchain_token_id(&Address::zero(&env), &expected_deploy_salt); + let expected_id = client.canonical_interchain_token_id(&token_address); assert_eq!(client.register_canonical_token(&token_address), expected_id); assert_eq!(client.token_address(&expected_id), token_address); diff --git a/contracts/stellar-interchain-token-service/src/tests/deployer.rs b/contracts/stellar-interchain-token-service/src/tests/deployer.rs new file mode 100644 index 00000000..611edace --- /dev/null +++ b/contracts/stellar-interchain-token-service/src/tests/deployer.rs @@ -0,0 +1,26 @@ +use soroban_sdk::BytesN; +use stellar_axelar_std::address::AddressExt; + +use super::utils::setup_env; + +// NOTE: This MUST NOT change after the initial deployment to avoid breaking existing logic +#[test] +fn interchain_token_address_is_unchanged() { + let (env, client, _, _, _) = setup_env(); + let token_id = BytesN::<32>::from_array(&env, &[1; 32]); + + goldie::assert!(hex::encode( + client.interchain_token_address(&token_id).to_raw_bytes() + )); +} + +// NOTE: This MUST NOT change after the initial deployment to avoid breaking existing logic +#[test] +fn token_manager_derivation_is_unchanged() { + let (env, client, _, _, _) = setup_env(); + let token_id = BytesN::<32>::from_array(&env, &[1; 32]); + + goldie::assert!(hex::encode( + client.token_manager_address(&token_id).to_raw_bytes() + )); +} diff --git a/contracts/stellar-interchain-token-service/src/tests/execute.rs b/contracts/stellar-interchain-token-service/src/tests/execute.rs index 2c7d7857..1c7c1db3 100644 --- a/contracts/stellar-interchain-token-service/src/tests/execute.rs +++ b/contracts/stellar-interchain-token-service/src/tests/execute.rs @@ -1,4 +1,5 @@ use soroban_sdk::testutils::Address as _; +use soroban_sdk::token::{StellarAssetClient, TokenClient}; use soroban_sdk::{vec, Address, Bytes, BytesN, Env, String}; use soroban_token_sdk::metadata::TokenMetadata; use stellar_axelar_gateway::testutils::approve_gateway_messages; @@ -68,6 +69,76 @@ fn interchain_transfer_message_execute_succeeds() { >(&env)); } +#[test] +fn interchain_transfer_message_canonical_token_execute_succeeds() { + let (env, client, gateway_client, _, signers) = setup_env(); + + let sender = Address::generate(&env).to_string_bytes(); + let recipient = Address::generate(&env).to_string_bytes(); + let source_chain = client.its_hub_chain_name(); + let source_address = client.its_hub_address(); + let original_source_chain = String::from_str(&env, "ethereum"); + + let amount = 1000; + let deployer = Address::generate(&env); + + let token_address = env.register_stellar_asset_contract_v2(deployer).address(); + let token_id = client + .mock_all_auths() + .register_canonical_token(&token_address); + let token_manager = client.token_manager(&token_id); + + StellarAssetClient::new(&env, &token_address) + .mock_all_auths() + .mint(&token_manager, &amount); + + client + .mock_all_auths() + .set_trusted_chain(&original_source_chain); + + let msg = HubMessage::ReceiveFromHub { + source_chain: original_source_chain, + message: Message::InterchainTransfer(InterchainTransfer { + token_id, + source_address: sender, + destination_address: recipient.clone(), + amount, + data: None, + }), + }; + let message_id = String::from_str(&env, "test"); + let payload = msg.abi_encode(&env).unwrap(); + let payload_hash: BytesN<32> = env.crypto().keccak256(&payload).into(); + + 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, + }, + ]; + + approve_gateway_messages(&env, &gateway_client, signers, messages); + + client.execute(&source_chain, &message_id, &source_address, &payload); + + goldie::assert!(events::fmt_last_emitted_event::< + InterchainTransferReceivedEvent, + >(&env)); + + assert_eq!( + TokenClient::new(&env, &token_address).balance(&token_manager), + 0 + ); + assert_eq!( + TokenClient::new(&env, &token_address).balance(&Address::from_string_bytes(&recipient)), + amount + ); +} + #[test] fn deploy_interchain_token_message_execute_succeeds() { let (env, client, gateway_client, _, signers) = setup_env(); diff --git a/contracts/stellar-interchain-token-service/src/tests/interchain_transfer.rs b/contracts/stellar-interchain-token-service/src/tests/interchain_transfer.rs index 1c9b5745..f974905e 100644 --- a/contracts/stellar-interchain-token-service/src/tests/interchain_transfer.rs +++ b/contracts/stellar-interchain-token-service/src/tests/interchain_transfer.rs @@ -1,4 +1,5 @@ use soroban_sdk::testutils::Address as _; +use soroban_sdk::token::{StellarAssetClient, TokenClient}; use soroban_sdk::{Address, Bytes, BytesN, Env, String}; use stellar_axelar_gas_service::testutils::setup_gas_token; use stellar_axelar_std::traits::BytesExt; @@ -58,6 +59,58 @@ fn interchain_transfer_send_succeeds() { >(&env, -4)); } +#[test] +fn interchain_transfer_canonical_token_send_succeeds() { + let (env, client, _, _, _) = setup_env(); + + let amount = 1000; + let sender: Address = Address::generate(&env); + let gas_token = setup_gas_token(&env, &sender); + let (destination_chain, destination_address, data) = dummy_transfer_params(&env); + + let token_address = env + .register_stellar_asset_contract_v2(sender.clone()) + .address(); + + let token_id = client + .mock_all_auths() + .register_canonical_token(&token_address); + let token_manager = client.token_manager(&token_id); + + assert_eq!( + TokenClient::new(&env, &token_address).balance(&token_manager), + 0 + ); + + StellarAssetClient::new(&env, &token_address) + .mock_all_auths() + .mint(&sender, &amount); + + 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, + ); + + goldie::assert!(events::fmt_emitted_event_at_idx::< + InterchainTransferSentEvent, + >(&env, -4)); + + // Check that the tokens were escrowed in the token manager + assert_eq!( + TokenClient::new(&env, &token_address).balance(&token_manager), + amount + ); +} + #[test] fn interchain_transfer_send_fails_when_paused() { let (env, client, _, _, _) = setup_env(); diff --git a/contracts/stellar-interchain-token-service/src/tests/mod.rs b/contracts/stellar-interchain-token-service/src/tests/mod.rs index f2231fc4..a9facb6d 100644 --- a/contracts/stellar-interchain-token-service/src/tests/mod.rs +++ b/contracts/stellar-interchain-token-service/src/tests/mod.rs @@ -1,6 +1,7 @@ mod deploy_interchain_token; mod deploy_remote_canonical_token; mod deploy_remote_interchain_token; +mod deployer; mod executable; mod execute; mod flow_limit; @@ -8,5 +9,6 @@ mod interchain_transfer; mod message_routing; mod pause; mod register_canonical_token; +mod token_id; mod trusted_chain; mod utils; diff --git a/contracts/stellar-interchain-token-service/src/tests/register_canonical_token.rs b/contracts/stellar-interchain-token-service/src/tests/register_canonical_token.rs index b4a4f668..99a55e93 100644 --- a/contracts/stellar-interchain-token-service/src/tests/register_canonical_token.rs +++ b/contracts/stellar-interchain-token-service/src/tests/register_canonical_token.rs @@ -3,20 +3,18 @@ use std::vec; use soroban_sdk::testutils::Address as _; use soroban_sdk::xdr::ToXdr; use soroban_sdk::{Address, BytesN}; -use stellar_axelar_std::address::AddressExt; use stellar_axelar_std::{assert_contract_err, events}; use super::utils::setup_env; use crate::error::ContractError; -use crate::event::{InterchainTokenIdClaimedEvent, TokenManagerDeployedEvent}; +use crate::event::TokenManagerDeployedEvent; use crate::types::TokenManagerType; #[test] fn register_canonical_token_succeeds() { let (env, client, _, _, _) = setup_env(); let token_address = Address::generate(&env); - let expected_deploy_salt = client.canonical_token_deploy_salt(&token_address); - let expected_id = client.interchain_token_id(&Address::zero(&env), &expected_deploy_salt); + let expected_id = client.canonical_interchain_token_id(&token_address); assert_eq!( client @@ -24,21 +22,15 @@ fn register_canonical_token_succeeds() { .register_canonical_token(&token_address), expected_id ); - let interchain_token_id_claimed_event = - events::fmt_last_emitted_event::(&env); let token_manager_deployed_event = - events::fmt_emitted_event_at_idx::(&env, -2); + events::fmt_emitted_event_at_idx::(&env, -1); assert_eq!(client.token_address(&expected_id), token_address); assert_eq!( client.token_manager_type(&expected_id), TokenManagerType::LockUnlock ); - goldie::assert!([ - interchain_token_id_claimed_event, - token_manager_deployed_event - ] - .join("\n\n")); + goldie::assert!([token_manager_deployed_event].join("\n\n")); } #[test] @@ -73,13 +65,10 @@ fn canonical_token_id_derivation() { let chain_name = client.chain_name(); let chain_name_hash: BytesN<32> = env.crypto().keccak256(&(chain_name).to_xdr(&env)).into(); - let deploy_salt = client.canonical_token_deploy_salt(&token_address); - - let token_id = client.interchain_token_id(&Address::zero(&env), &deploy_salt); + let token_id = client.canonical_interchain_token_id(&token_address); goldie::assert_json!(vec![ hex::encode(chain_name_hash.to_array()), - hex::encode(deploy_salt.to_array()), hex::encode(token_id.to_array()) ]); } diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/canonical_interchain_token_id_is_unchanged.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/canonical_interchain_token_id_is_unchanged.golden new file mode 100644 index 00000000..ec49e066 --- /dev/null +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/canonical_interchain_token_id_is_unchanged.golden @@ -0,0 +1 @@ +5e9847dc27fbfe16165532c125788aa891ac7489eb896fb1e4e8b2756c0063a8 \ No newline at end of file diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/canonical_token_id_derivation.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/canonical_token_id_derivation.golden index e80d81bf..290e3c2d 100644 --- a/contracts/stellar-interchain-token-service/src/tests/testdata/canonical_token_id_derivation.golden +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/canonical_token_id_derivation.golden @@ -1,5 +1,4 @@ [ "bdfb629dd56a9581bfba5ac25009cef0ee7646acbbd1880e99d7d26ea68f0885", - "6b0638349aebaeb032da7b9ef6890abe42eca7a835b52bb232c04c8f81a26a82", "6da6bda0ac5a04b5d83ddb7175cd63341673f2e8f2eddc2dd7d39aeb21aaa244" ] \ No newline at end of file diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_check_token_id_and_token_manager_type.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_check_token_id_and_token_manager_type.golden index fe7c95b5..5eafc94f 100644 --- a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_check_token_id_and_token_manager_type.golden +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_check_token_id_and_token_manager_type.golden @@ -1,6 +1,6 @@ InterchainTokenDeployedEvent { token_id: BytesN<32>(125, 96, 86, 2, 12, 87, 185, 38, 24, 69, 45, 187, 113, 21, 59, 197, 31, 115, 71, 89, 174, 148, 246, 246, 88, 83, 233, 86, 107, 151, 39, 140), - token_address: Contract(CCSKIPMIS4O5TMGE6IZWALEL5LEVNGLZ6KH5662WUXIVIVIBTOXZIZ6A), + token_address: Contract(CCLJONNNVXPGI3EUIEDRFFEUGRNZL2F2MWRYG3MIF7THONTE3T4INXXL), name: String(Test), symbol: String(TEST), decimals: 6, diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_fails_with_zero_initial_supply_and_no_minter.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_fails_with_zero_initial_supply_and_no_minter.golden index cc68255f..e0b32cdf 100644 --- a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_fails_with_zero_initial_supply_and_no_minter.golden +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_fails_with_zero_initial_supply_and_no_minter.golden @@ -1,6 +1,6 @@ InterchainTokenDeployedEvent { token_id: BytesN<32>(125, 96, 86, 2, 12, 87, 185, 38, 24, 69, 45, 187, 113, 21, 59, 197, 31, 115, 71, 89, 174, 148, 246, 246, 88, 83, 233, 86, 107, 151, 39, 140), - token_address: Contract(CCSKIPMIS4O5TMGE6IZWALEL5LEVNGLZ6KH5662WUXIVIVIBTOXZIZ6A), + token_address: Contract(CCLJONNNVXPGI3EUIEDRFFEUGRNZL2F2MWRYG3MIF7THONTE3T4INXXL), name: String(Test), symbol: String(TEST), decimals: 6, diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_message_execute_succeeds.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_message_execute_succeeds.golden index c798bd5d..96427aae 100644 --- a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_message_execute_succeeds.golden +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_message_execute_succeeds.golden @@ -1,6 +1,6 @@ InterchainTokenDeployedEvent { token_id: 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), - token_address: Contract(CBAWP2LID52I5JPA77XDY5EX3ZIL3S4SVHZCMIYLG47ZT6MQQ44V5VVR), + token_address: Contract(CA36KTELKNXBETWXHHYV4C6GMPBTGSWCEQNREQR46T7P3D4WKMICRDU6), name: String(Test), symbol: String(TEST), decimals: 18, @@ -22,8 +22,8 @@ interchain_token_deployed { TokenManagerDeployedEvent { token_id: 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), - token_address: Contract(CBAWP2LID52I5JPA77XDY5EX3ZIL3S4SVHZCMIYLG47ZT6MQQ44V5VVR), - token_manager: Contract(CCFYXHU3UMDYC7SFCELM3OSIS6XU6RT44ERJTVE5MYA6Z5GXRLMDJSIM), + token_address: Contract(CA36KTELKNXBETWXHHYV4C6GMPBTGSWCEQNREQR46T7P3D4WKMICRDU6), + token_manager: Contract(CCSVDGI4C4HI4NXYPFHVGDBMW336KYFMJ7WE75VKRA5LFL5F7EBZTHLZ), token_manager_type: NativeInterchainToken, } diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_succeeds.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_succeeds.golden index 3996236a..4a0c931f 100644 --- a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_succeeds.golden +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_succeeds.golden @@ -1,6 +1,6 @@ InterchainTokenDeployedEvent { token_id: BytesN<32>(125, 96, 86, 2, 12, 87, 185, 38, 24, 69, 45, 187, 113, 21, 59, 197, 31, 115, 71, 89, 174, 148, 246, 246, 88, 83, 233, 86, 107, 151, 39, 140), - token_address: Contract(CCSKIPMIS4O5TMGE6IZWALEL5LEVNGLZ6KH5662WUXIVIVIBTOXZIZ6A), + token_address: Contract(CCLJONNNVXPGI3EUIEDRFFEUGRNZL2F2MWRYG3MIF7THONTE3T4INXXL), name: String(Test), symbol: String(TEST), decimals: 6, @@ -20,8 +20,8 @@ interchain_token_deployed { TokenManagerDeployedEvent { token_id: BytesN<32>(125, 96, 86, 2, 12, 87, 185, 38, 24, 69, 45, 187, 113, 21, 59, 197, 31, 115, 71, 89, 174, 148, 246, 246, 88, 83, 233, 86, 107, 151, 39, 140), - token_address: Contract(CCSKIPMIS4O5TMGE6IZWALEL5LEVNGLZ6KH5662WUXIVIVIBTOXZIZ6A), - token_manager: Contract(CCY3LEB6P3DS7R67365LHDSVF43GPB4KF6PJBN2OS2HHYSFQOI7SJYXM), + token_address: Contract(CCLJONNNVXPGI3EUIEDRFFEUGRNZL2F2MWRYG3MIF7THONTE3T4INXXL), + token_manager: Contract(CCDVPZH52RLSQBF7XPNI7HJT4B63UZ44SGYTGSMPE6XGBATEWZDPA6LF), token_manager_type: NativeInterchainToken, } diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_with_initial_supply_no_minter.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_with_initial_supply_no_minter.golden index cc68255f..e0b32cdf 100644 --- a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_with_initial_supply_no_minter.golden +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_with_initial_supply_no_minter.golden @@ -1,6 +1,6 @@ InterchainTokenDeployedEvent { token_id: BytesN<32>(125, 96, 86, 2, 12, 87, 185, 38, 24, 69, 45, 187, 113, 21, 59, 197, 31, 115, 71, 89, 174, 148, 246, 246, 88, 83, 233, 86, 107, 151, 39, 140), - token_address: Contract(CCSKIPMIS4O5TMGE6IZWALEL5LEVNGLZ6KH5662WUXIVIVIBTOXZIZ6A), + token_address: Contract(CCLJONNNVXPGI3EUIEDRFFEUGRNZL2F2MWRYG3MIF7THONTE3T4INXXL), name: String(Test), symbol: String(TEST), decimals: 6, diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_with_initial_supply_valid_minter.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_with_initial_supply_valid_minter.golden index fe7c95b5..5eafc94f 100644 --- a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_with_initial_supply_valid_minter.golden +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_with_initial_supply_valid_minter.golden @@ -1,6 +1,6 @@ InterchainTokenDeployedEvent { token_id: BytesN<32>(125, 96, 86, 2, 12, 87, 185, 38, 24, 69, 45, 187, 113, 21, 59, 197, 31, 115, 71, 89, 174, 148, 246, 246, 88, 83, 233, 86, 107, 151, 39, 140), - token_address: Contract(CCSKIPMIS4O5TMGE6IZWALEL5LEVNGLZ6KH5662WUXIVIVIBTOXZIZ6A), + token_address: Contract(CCLJONNNVXPGI3EUIEDRFFEUGRNZL2F2MWRYG3MIF7THONTE3T4INXXL), name: String(Test), symbol: String(TEST), decimals: 6, diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_zero_initial_supply_and_valid_minter.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_zero_initial_supply_and_valid_minter.golden index fe7c95b5..5eafc94f 100644 --- a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_zero_initial_supply_and_valid_minter.golden +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_interchain_token_zero_initial_supply_and_valid_minter.golden @@ -1,6 +1,6 @@ InterchainTokenDeployedEvent { token_id: BytesN<32>(125, 96, 86, 2, 12, 87, 185, 38, 24, 69, 45, 187, 113, 21, 59, 197, 31, 115, 71, 89, 174, 148, 246, 246, 88, 83, 233, 86, 107, 151, 39, 140), - token_address: Contract(CCSKIPMIS4O5TMGE6IZWALEL5LEVNGLZ6KH5662WUXIVIVIBTOXZIZ6A), + token_address: Contract(CCLJONNNVXPGI3EUIEDRFFEUGRNZL2F2MWRYG3MIF7THONTE3T4INXXL), name: String(Test), symbol: String(TEST), decimals: 6, diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_remote_canonical_token_succeeds_without_name_truncation.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_remote_canonical_token_succeeds_without_name_truncation.golden index 453a7f11..e53fe51e 100644 --- a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_remote_canonical_token_succeeds_without_name_truncation.golden +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_remote_canonical_token_succeeds_without_name_truncation.golden @@ -1,6 +1,6 @@ InterchainTokenDeploymentStartedEvent { - token_id: BytesN<32>(247, 214, 168, 250, 162, 27, 103, 121, 206, 126, 141, 252, 22, 96, 17, 104, 179, 98, 104, 231, 160, 3, 162, 197, 137, 24, 18, 183, 248, 208, 163, 109), - token_address: Contract(CCGZAZST4ZRJ6VF32SF3JQYKSDKDHJ3BITGC7SQNXCQW3K7MH7DJA42S), + token_id: BytesN<32>(60, 2, 220, 188, 8, 205, 164, 191, 58, 197, 58, 16, 100, 159, 151, 83, 114, 109, 109, 165, 242, 106, 241, 4, 32, 114, 77, 111, 202, 198, 203, 227), + token_address: Contract(CCWZVT7W4B7OUHS6F3MMVHPGHE3ZOJR6QX6HMJHZGGU6M5KBHQFHWCSH), destination_chain: String(ethereum), name: String(name), symbol: String(symbol), diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_remote_interchain_token_succeeds.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_remote_interchain_token_succeeds.golden index d8a17225..0d82536e 100644 --- a/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_remote_interchain_token_succeeds.golden +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/deploy_remote_interchain_token_succeeds.golden @@ -1,6 +1,6 @@ InterchainTokenDeploymentStartedEvent { token_id: BytesN<32>(125, 96, 86, 2, 12, 87, 185, 38, 24, 69, 45, 187, 113, 21, 59, 197, 31, 115, 71, 89, 174, 148, 246, 246, 88, 83, 233, 86, 107, 151, 39, 140), - token_address: Contract(CCSKIPMIS4O5TMGE6IZWALEL5LEVNGLZ6KH5662WUXIVIVIBTOXZIZ6A), + token_address: Contract(CCLJONNNVXPGI3EUIEDRFFEUGRNZL2F2MWRYG3MIF7THONTE3T4INXXL), destination_chain: String(ethereum), name: String(name), symbol: String(symbol), diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/interchain_token_address_is_unchanged.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/interchain_token_address_is_unchanged.golden new file mode 100644 index 00000000..50acf845 --- /dev/null +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/interchain_token_address_is_unchanged.golden @@ -0,0 +1 @@ +434133364b54454c4b4e58424554575848485956344336474d5042544753574345514e524551523436543750334434574b4d494352445536 \ No newline at end of file diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/interchain_token_id_is_unchanged.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/interchain_token_id_is_unchanged.golden new file mode 100644 index 00000000..0ac10555 --- /dev/null +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/interchain_token_id_is_unchanged.golden @@ -0,0 +1 @@ +acb98b33503ea2e098542a3a7de598d37a329bc4ac24b9b304edcee7e7887fd8 \ No newline at end of file diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/interchain_transfer_canonical_token_send_succeeds.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/interchain_transfer_canonical_token_send_succeeds.golden new file mode 100644 index 00000000..b01c7a23 --- /dev/null +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/interchain_transfer_canonical_token_send_succeeds.golden @@ -0,0 +1,21 @@ +InterchainTransferSentEvent { + token_id: 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), + source_address: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXI7N), + destination_chain: String(ethereum), + destination_address: Bytes(79, 68, 149, 36, 56, 55, 104, 16, 97, 196, 116, 59, 116, 179, 238, 223, 84, 141, 86, 165), + amount: 1000, + data: Some( + Bytes(171, 205), + ), +} + +Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5) + +interchain_transfer_sent { + #[topic] token_id: BytesN < 32 >, + #[topic] source_address: Address, + #[topic] destination_chain: String, + #[topic] destination_address: Bytes, + #[topic] amount: i128, + #[data] data: Option < Bytes >, +} \ No newline at end of file diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/interchain_transfer_execute_succeeds.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/interchain_transfer_execute_succeeds.golden index 0beb3780..370f1ee5 100644 --- a/contracts/stellar-interchain-token-service/src/tests/testdata/interchain_transfer_execute_succeeds.golden +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/interchain_transfer_execute_succeeds.golden @@ -3,7 +3,7 @@ ExecutedEvent { message_id: String(test), source_address: Bytes(67, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 89, 82, 69, 53), token_id: 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), - token_address: Contract(CBQXZFUKGANX4DCFGMAEZNEIZ6KEN5RRIUPP25SRMZ6WGLVGFAFT5XSD), + token_address: Contract(CCSE5RCPZ5456Z6MJFMSTB6NYQOBDD76JVB2ZOFMYEYIXJLORPRHNA6T), amount: 1000, payload: Bytes(222, 173), } diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/interchain_transfer_message_canonical_token_execute_succeeds.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/interchain_transfer_message_canonical_token_execute_succeeds.golden new file mode 100644 index 00000000..264c43e1 --- /dev/null +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/interchain_transfer_message_canonical_token_execute_succeeds.golden @@ -0,0 +1,19 @@ +InterchainTransferReceivedEvent { + source_chain: String(ethereum), + token_id: 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), + source_address: Bytes(67, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 88, 73, 55, 78), + destination_address: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYRE5), + amount: 1000, + data: None, +} + +Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5) + +interchain_transfer_received { + #[topic] source_chain: String, + #[topic] token_id: BytesN < 32 >, + #[topic] source_address: Bytes, + #[topic] destination_address: Address, + #[topic] amount: i128, + #[data] data: Option < Bytes >, +} \ No newline at end of file diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/register_canonical_token_succeeds.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/register_canonical_token_succeeds.golden index 1242b328..d7388834 100644 --- a/contracts/stellar-interchain-token-service/src/tests/testdata/register_canonical_token_succeeds.golden +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/register_canonical_token_succeeds.golden @@ -1,21 +1,7 @@ -InterchainTokenIdClaimedEvent { - token_id: BytesN<32>(109, 166, 189, 160, 172, 90, 4, 181, 216, 61, 219, 113, 117, 205, 99, 52, 22, 115, 242, 232, 242, 237, 220, 45, 215, 211, 154, 235, 33, 170, 162, 68), - deployer: AccountId(GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF), - salt: BytesN<32>(107, 6, 56, 52, 154, 235, 174, 176, 50, 218, 123, 158, 246, 137, 10, 190, 66, 236, 167, 168, 53, 181, 43, 178, 50, 192, 76, 143, 129, 162, 106, 130), -} - -Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5) - -interchain_token_id_claimed { - #[topic] token_id: BytesN < 32 >, - #[topic] deployer: Address, - #[topic] salt: BytesN < 32 >, -} - TokenManagerDeployedEvent { token_id: BytesN<32>(109, 166, 189, 160, 172, 90, 4, 181, 216, 61, 219, 113, 117, 205, 99, 52, 22, 115, 242, 232, 242, 237, 220, 45, 215, 211, 154, 235, 33, 170, 162, 68), token_address: Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXI7N), - token_manager: Contract(CBQHYYADS6KAHHHP34VG5V3AXWFVBIDQLCPUB5OAE6WO4YUEQAXXBMBZ), + token_manager: Contract(CCMET6VYX66Q4ATPQIG4ZE3ZZPMGROU34UT7NMO6FHDCOGJ5HOGTFHAD), token_manager_type: LockUnlock, } diff --git a/contracts/stellar-interchain-token-service/src/tests/testdata/token_manager_derivation_is_unchanged.golden b/contracts/stellar-interchain-token-service/src/tests/testdata/token_manager_derivation_is_unchanged.golden new file mode 100644 index 00000000..d594906e --- /dev/null +++ b/contracts/stellar-interchain-token-service/src/tests/testdata/token_manager_derivation_is_unchanged.golden @@ -0,0 +1 @@ +434353564447493443344849344e5859504648564744424d573333364b59464d4a3757453735564b5241354c464c35463745425a54484c5a \ No newline at end of file diff --git a/contracts/stellar-interchain-token-service/src/tests/token_id.rs b/contracts/stellar-interchain-token-service/src/tests/token_id.rs new file mode 100644 index 00000000..b2091687 --- /dev/null +++ b/contracts/stellar-interchain-token-service/src/tests/token_id.rs @@ -0,0 +1,34 @@ +use soroban_sdk::{Address, BytesN}; + +use super::utils::setup_env; + +// NOTE: This MUST NOT change after the initial deployment to avoid breaking existing logic +#[test] +fn canonical_interchain_token_id_is_unchanged() { + let (env, client, _, _, _) = setup_env(); + let token_address = Address::from_str( + &env, + "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC", + ); + + goldie::assert!(hex::encode( + client + .canonical_interchain_token_id(&token_address) + .to_array() + )); +} + +// NOTE: This MUST NOT change after the initial deployment to avoid breaking existing logic +#[test] +fn interchain_token_id_is_unchanged() { + let (env, client, _, _, _) = setup_env(); + let deployer = Address::from_str( + &env, + "GDUITDF2LI3R5HM4KYRLLNRLEWKYBFVZVOEB6HSL7EOW2KO2LD6V4GPM", + ); + let salt = BytesN::<32>::from_array(&env, &[1; 32]); + + goldie::assert!(hex::encode( + client.interchain_token_id(&deployer, &salt).to_array() + )); +} diff --git a/contracts/stellar-interchain-token-service/src/token_id.rs b/contracts/stellar-interchain-token-service/src/token_id.rs new file mode 100644 index 00000000..d6cc8211 --- /dev/null +++ b/contracts/stellar-interchain-token-service/src/token_id.rs @@ -0,0 +1,66 @@ +use soroban_sdk::xdr::ToXdr; +use soroban_sdk::{Address, BytesN, Env}; +use stellar_axelar_std::address::AddressExt; + +const PREFIX_CANONICAL_TOKEN_SALT: &str = "canonical-token-salt"; +const PREFIX_INTERCHAIN_TOKEN_SALT: &str = "interchain-token-salt"; +/// This prefix is used along with a salt to generate the token ID +const PREFIX_TOKEN_ID: &str = "its-interchain-token-id"; + +fn token_id(env: &Env, deploy_salt: BytesN<32>) -> BytesN<32> { + env.crypto() + .keccak256(&(PREFIX_TOKEN_ID, Address::zero(env), deploy_salt).to_xdr(env)) + .into() +} + +fn canonical_token_deploy_salt( + env: &Env, + chain_name_hash: BytesN<32>, + token_address: Address, +) -> BytesN<32> { + env.crypto() + .keccak256(&(PREFIX_CANONICAL_TOKEN_SALT, chain_name_hash, token_address).to_xdr(env)) + .into() +} + +pub fn canonical_interchain_token_id( + env: &Env, + chain_name_hash: BytesN<32>, + token_address: Address, +) -> BytesN<32> { + token_id( + env, + canonical_token_deploy_salt(env, chain_name_hash, token_address), + ) +} + +fn interchain_token_deploy_salt( + env: &Env, + chain_name_hash: BytesN<32>, + deployer: Address, + salt: BytesN<32>, +) -> BytesN<32> { + env.crypto() + .keccak256( + &( + PREFIX_INTERCHAIN_TOKEN_SALT, + chain_name_hash, + deployer, + salt, + ) + .to_xdr(env), + ) + .into() +} + +pub fn interchain_token_id( + env: &Env, + chain_name_hash: BytesN<32>, + deployer: Address, + salt: BytesN<32>, +) -> BytesN<32> { + token_id( + env, + interchain_token_deploy_salt(env, chain_name_hash, deployer, salt), + ) +} diff --git a/packages/stellar-axelar-std/src/address.rs b/packages/stellar-axelar-std/src/address.rs index b7a79b6a..159099da 100644 --- a/packages/stellar-axelar-std/src/address.rs +++ b/packages/stellar-axelar-std/src/address.rs @@ -7,6 +7,8 @@ pub trait AddressExt { fn zero(env: &Env) -> Address; fn to_string_bytes(&self) -> Bytes; + + fn to_raw_bytes(&self) -> [u8; STELLAR_ADDRESS_LEN]; } impl AddressExt for Address { @@ -21,6 +23,12 @@ impl AddressExt for Address { self.to_string().copy_into_slice(&mut address_string_bytes); Bytes::from_slice(self.env(), &address_string_bytes) } + + fn to_raw_bytes(&self) -> [u8; STELLAR_ADDRESS_LEN] { + let mut address_string_bytes = [0u8; STELLAR_ADDRESS_LEN]; + self.to_string().copy_into_slice(&mut address_string_bytes); + address_string_bytes + } } #[cfg(test)]