diff --git a/contracts/interchain-token-service/src/contract.rs b/contracts/interchain-token-service/src/contract.rs index cddd6723..bad64c08 100644 --- a/contracts/interchain-token-service/src/contract.rs +++ b/contracts/interchain-token-service/src/contract.rs @@ -1,5 +1,5 @@ use soroban_sdk::token::StellarAssetClient; -use soroban_sdk::xdr::{FromXdr, ToXdr}; +use soroban_sdk::xdr::ToXdr; use soroban_sdk::{contract, contractimpl, Address, Bytes, BytesN, Env, String}; use soroban_token_sdk::metadata::TokenMetadata; use stellar_axelar_gas_service::AxelarGasServiceClient; @@ -377,7 +377,7 @@ impl InterchainTokenServiceInterface for InterchainTokenService { let message = Message::InterchainTransfer(InterchainTransfer { token_id, - source_address: caller.clone().to_xdr(env), + source_address: caller.to_string_bytes(), destination_address, amount, data, @@ -674,8 +674,7 @@ impl InterchainTokenService { data, }: InterchainTransfer, ) -> Result<(), ContractError> { - let destination_address = Address::from_xdr(env, &destination_address) - .map_err(|_| ContractError::InvalidDestinationAddress)?; + let destination_address = Address::from_string_bytes(&destination_address); let token_config_value = Self::token_id_config_with_extended_ttl(env, token_id.clone())?; @@ -734,10 +733,7 @@ impl InterchainTokenService { let token_metadata = TokenMetadata::new(name, symbol, decimals as u32)?; // 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_xdr(env, &m)) - .transpose() - .map_err(|_| ContractError::InvalidMinter)?; + let minter = minter.map(|m| Address::from_string_bytes(&m)); let deployed_address = Self::deploy_interchain_token_contract(env, minter, token_id.clone(), token_metadata); diff --git a/contracts/interchain-token-service/tests/executable.rs b/contracts/interchain-token-service/tests/executable.rs index 80eacf36..f84f3d74 100644 --- a/contracts/interchain-token-service/tests/executable.rs +++ b/contracts/interchain-token-service/tests/executable.rs @@ -1,10 +1,10 @@ mod utils; use soroban_sdk::testutils::Address as _; -use soroban_sdk::xdr::ToXdr; use soroban_sdk::{token, vec, Address, Bytes, BytesN, String}; use stellar_axelar_gateway::testutils::{generate_proof, get_approve_hash}; use stellar_axelar_gateway::types::Message as GatewayMessage; +use stellar_axelar_std::address::AddressExt; use stellar_axelar_std::traits::BytesExt; use stellar_axelar_std::{assert_auth_err, events}; use stellar_interchain_token_service::types::{HubMessage, InterchainTransfer, Message}; @@ -124,7 +124,7 @@ fn interchain_transfer_execute_succeeds() { let executable_id = env.register(test::ExecutableContract, (client.address.clone(),)); - let sender = Address::generate(&env).to_xdr(&env); + let sender = Address::generate(&env).to_string_bytes(); let source_chain = client.its_hub_chain_name(); let source_address: String = client.its_hub_address(); @@ -132,6 +132,7 @@ fn interchain_transfer_execute_succeeds() { let deployer = Address::generate(&env); let token_id = setup_its_token(&env, &client, &deployer, amount); let data = Bytes::from_hex(&env, "dead"); + let destination_address = executable_id.to_string_bytes(); let original_source_chain = String::from_str(&env, "ethereum"); client .mock_all_auths() @@ -142,7 +143,7 @@ fn interchain_transfer_execute_succeeds() { message: Message::InterchainTransfer(InterchainTransfer { token_id: token_id.clone(), source_address: sender, - destination_address: executable_id.clone().to_xdr(&env), + destination_address, amount, data: Some(data.clone()), }), @@ -186,7 +187,7 @@ fn executable_fails_if_not_executed_from_its() { let executable_client = test::ExecutableContractClient::new(&env, &executable_id); let source_chain = client.its_hub_chain_name(); - let source_address = Address::generate(&env).to_xdr(&env); + let source_address = Address::generate(&env).to_string_bytes(); let amount = 1000; let token_id = BytesN::<32>::from_array(&env, &[1; 32]); let token_address = Address::generate(&env); diff --git a/contracts/interchain-token-service/tests/execute.rs b/contracts/interchain-token-service/tests/execute.rs index e408c687..8ae58737 100644 --- a/contracts/interchain-token-service/tests/execute.rs +++ b/contracts/interchain-token-service/tests/execute.rs @@ -1,10 +1,10 @@ mod utils; use soroban_sdk::testutils::Address as _; -use soroban_sdk::xdr::ToXdr; use soroban_sdk::{vec, Address, Bytes, BytesN, Env, String}; use soroban_token_sdk::metadata::TokenMetadata; use stellar_axelar_gateway::types::Message as GatewayMessage; +use stellar_axelar_std::address::AddressExt; use stellar_axelar_std::{assert_contract_err, events}; use stellar_interchain_token::InterchainTokenClient; use stellar_interchain_token_service::error::ContractError; @@ -124,8 +124,8 @@ fn execute_fails_with_invalid_source_address() { fn interchain_transfer_message_execute_succeeds() { let (env, client, gateway_client, _, signers) = setup_env(); - let sender = Address::generate(&env).to_xdr(&env); - let recipient = Address::generate(&env).to_xdr(&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"); @@ -176,8 +176,7 @@ fn deploy_interchain_token_message_execute_succeeds() { let (env, client, gateway_client, _, signers) = setup_env(); register_chains(&env, &client); - let sender = Address::generate(&env); - let sender_bytes = sender.clone().to_xdr(&env); + let sender = Address::generate(&env).to_string_bytes(); let source_chain = client.its_hub_chain_name(); let source_address = client.its_hub_address(); @@ -199,7 +198,7 @@ fn deploy_interchain_token_message_execute_succeeds() { name: token_metadata.name.clone(), symbol: token_metadata.symbol.clone(), decimals: token_metadata.decimal as u8, - minter: Some(sender_bytes), + minter: Some(sender.clone()), }), }; let payload = msg.abi_encode(&env).unwrap(); @@ -226,7 +225,7 @@ fn deploy_interchain_token_message_execute_succeeds() { let token = InterchainTokenClient::new(&env, &client.token_address(&token_id)); - assert!(token.is_minter(&sender)); + assert!(token.is_minter(&Address::from_string_bytes(&sender))); assert_eq!(token.name(), token_metadata.name); assert_eq!(token.symbol(), token_metadata.symbol); assert_eq!(token.decimals(), token_metadata.decimal); @@ -374,7 +373,7 @@ fn deploy_interchain_token_message_execute_fails_token_already_deployed() { let (env, client, gateway_client, _, signers) = setup_env(); register_chains(&env, &client); - let sender = Address::generate(&env).to_xdr(&env); + let sender = Address::generate(&env).to_string_bytes(); let source_chain = client.its_hub_chain_name(); let source_address = client.its_hub_address(); diff --git a/contracts/interchain-token-service/tests/flow_limit.rs b/contracts/interchain-token-service/tests/flow_limit.rs index 71856929..32b7cc44 100644 --- a/contracts/interchain-token-service/tests/flow_limit.rs +++ b/contracts/interchain-token-service/tests/flow_limit.rs @@ -1,11 +1,11 @@ mod utils; use soroban_sdk::testutils::{Address as _, Ledger as _}; -use soroban_sdk::xdr::ToXdr; use soroban_sdk::{vec, Address, Bytes, BytesN, Env, String}; use stellar_axelar_gateway::testutils::TestSignerSet; use stellar_axelar_gateway::types::Message as GatewayMessage; use stellar_axelar_gateway::AxelarGatewayClient; +use stellar_axelar_std::address::AddressExt; use stellar_axelar_std::traits::BytesExt; use stellar_axelar_std::{assert_auth, assert_contract_err, events}; use stellar_interchain_token_service::error::ContractError; @@ -86,8 +86,8 @@ fn approve_its_transfer( token_id: &BytesN<32>, amount: i128, ) -> ApprovedMessage { - let sender = Address::generate(env).to_xdr(env); - let recipient = Address::generate(env).to_xdr(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(); diff --git a/contracts/interchain-token-service/tests/message_routing.rs b/contracts/interchain-token-service/tests/message_routing.rs index a12917e6..b040e2e9 100644 --- a/contracts/interchain-token-service/tests/message_routing.rs +++ b/contracts/interchain-token-service/tests/message_routing.rs @@ -1,7 +1,7 @@ mod utils; use soroban_sdk::testutils::Address as _; -use soroban_sdk::xdr::ToXdr; use soroban_sdk::{Address, Bytes, String}; +use stellar_axelar_std::address::AddressExt; use stellar_axelar_std::assert_contract_err; use stellar_axelar_std::traits::BytesExt; use stellar_interchain_token_service::error::ContractError; @@ -45,7 +45,7 @@ fn send_to_untrusted_chain_fails() { &sender, &token_id, &String::from_str(&env, "untrusted_chain"), - &Address::generate(&env).to_xdr(&env), + &Address::generate(&env).to_string_bytes(), &amount, &None, &gas_token, 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 3077e3f4..0de2fa39 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(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXI7N) -topics: (Symbol(executed), String(ethereum), 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, 12), 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(CBQXZFUKGANX4DCFGMAEZNEIZ6KEN5RRIUPP25SRMZ6WGLVGFAFT5XSD), 1000) +topics: (Symbol(executed), String(ethereum), String(test), 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), 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(CBQXZFUKGANX4DCFGMAEZNEIZ6KEN5RRIUPP25SRMZ6WGLVGFAFT5XSD), 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 c4952b85..5701ef0a 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(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAX5) -topics: (Symbol(interchain_transfer_received), String(ethereum), 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, 11), Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYRE5), 1000) +topics: (Symbol(interchain_transfer_received), String(ethereum), 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(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), Contract(CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYRE5), 1000) data: (None) \ No newline at end of file diff --git a/packages/axelar-std/src/address.rs b/packages/axelar-std/src/address.rs index 5813e0cd..b7a79b6a 100644 --- a/packages/axelar-std/src/address.rs +++ b/packages/axelar-std/src/address.rs @@ -1,16 +1,94 @@ -use soroban_sdk::{Address, Env, String}; +use soroban_sdk::{Address, Bytes, Env, String}; const ZERO_ADDRESS: &str = "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF"; +const STELLAR_ADDRESS_LEN: usize = ZERO_ADDRESS.len(); pub trait AddressExt { fn zero(env: &Env) -> Address; + + fn to_string_bytes(&self) -> Bytes; } impl AddressExt for Address { - /// Returns Stellar's "dead" address, represented by the constant `ZERO_ADDRESS`. - /// # Reference - /// - Stellar [GitHub](https://github.com/stellar/js-stellar-base/blob/master/test/unit/address_test.js) + /// Returns Stellar's ["dead"](https://github.com/stellar/js-stellar-base/blob/master/test/unit/address_test.js) address, represented by the constant `ZERO_ADDRESS`. fn zero(env: &Env) -> Address { Self::from_string(&String::from_str(env, ZERO_ADDRESS)) } + + /// Converts Stellar address to a string represented as bytes + fn to_string_bytes(&self) -> Bytes { + let mut address_string_bytes = [0u8; STELLAR_ADDRESS_LEN]; + self.to_string().copy_into_slice(&mut address_string_bytes); + Bytes::from_slice(self.env(), &address_string_bytes) + } +} + +#[cfg(test)] +mod tests { + use soroban_sdk::testutils::Address as _; + use soroban_sdk::{Address, Bytes, Env, String}; + + use super::{AddressExt, ZERO_ADDRESS}; + + #[test] + fn zero_address_to_string() { + let env = &Env::default(); + assert_eq!( + Address::zero(env).to_string(), + String::from_str(env, ZERO_ADDRESS) + ); + } + + #[test] + fn string_to_address_to_string() { + let env = &Env::default(); + let cases = [ + ZERO_ADDRESS, + "GC7OHFPWPSWXL4HMN6TXAG54MTZSMJIASWHO6KVRQNHNCXEAHWDSGGC3", + "CB6743BTQ2TZHTUCRSUFAH2X5ICOZGI6I3UCQBY3VFSSJ7IERGXUM7TX", + ] + .into_iter() + .map(|s| Bytes::from_slice(env, s.as_bytes())); + + for address_bytes in cases { + let address = Address::from_string_bytes(&address_bytes); + assert_eq!(address.to_string_bytes(), address_bytes); + } + } + + #[test] + fn address_to_string_to_address() { + let env = &Env::default(); + + let cases = [ + Address::zero(env), + Address::generate(env), + env.register_stellar_asset_contract_v2(Address::generate(env)) + .address(), + ]; + + for address in cases { + let address_bytes = address.to_string_bytes(); + assert_eq!(Address::from_string_bytes(&address_bytes), address); + } + } + + #[test] + #[should_panic(expected = "HostError: Error(Value, InvalidInput)")] + fn unsupported_muxed_address_format_fails_on_conversion() { + let env = &Env::default(); + + let unsupported_address = + "MA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAAAAAAAACJUQ"; + Address::from_string(&String::from_str(env, unsupported_address)); + } + + #[test] + #[should_panic(expected = "HostError: Error(Value, InvalidInput)")] + fn unsupported_signed_payload_address_format_fails_on_conversion() { + let env = &Env::default(); + + let unsupported_address = "PA7QYNF7SOWQ3GLR2BGMZEHXAVIRZA4KVWLTJJFC7MGXUA74P7UJUAAAAAOQCAQDAQCQMBYIBEFAWDANBYHRAEISCMKBKFQXDAMRUGY4DUAAAAFGBU"; + Address::from_string(&String::from_str(env, unsupported_address)); + } }