diff --git a/move/governance/sources/governance/governance.move b/move/governance/sources/governance/governance.move index 04164c88..d3148c3b 100644 --- a/move/governance/sources/governance/governance.move +++ b/move/governance/sources/governance/governance.move @@ -123,6 +123,22 @@ use sui::test_scenario; #[test_only] use sui::test_utils; +#[test_only] +public fun new_for_testing( + trusted_source_chain: String, + trusted_source_address: String, + message_type: u256, + ctx: &mut TxContext, +): Governance { + Governance { + id: object::new(ctx), + trusted_source_chain, + trusted_source_address, + message_type, + channel: channel::new(ctx), + caps: table::new(ctx), + } +} #[test] fun test_new() { let trusted_source_chain = ascii::string(b"Axelar"); @@ -130,30 +146,26 @@ fun test_new() { let message_type = 2; let mut ctx = tx_context::dummy(); let package_id = object::id_from_bytes( - hex::decode(type_name::get().get_address().into_bytes()), + hex::decode( + type_name::get().get_address().into_bytes() + ) ); let upgrade_cap = package::test_publish(package_id, &mut ctx); let initial_owner = @0x1; let mut scenario = test_scenario::begin(initial_owner); - { + { test_scenario::sender(&scenario); - new( - trusted_source_chain, - trusted_source_address, - message_type, - upgrade_cap, - &mut ctx, - ); + new(trusted_source_chain, trusted_source_address, message_type, upgrade_cap, &mut ctx); }; test_scenario::next_tx(&mut scenario, initial_owner); - { + { let governance = test_scenario::take_shared(&scenario); test_scenario::return_shared(governance); }; test_scenario::end(scenario); -} +} #[test] #[expected_failure(abort_code = ENotSelfUpgradeCap)] diff --git a/move/its/sources/address_tracker.move b/move/its/sources/address_tracker.move index cd513b60..116b308e 100644 --- a/move/its/sources/address_tracker.move +++ b/move/its/sources/address_tracker.move @@ -5,6 +5,9 @@ module its::address_tracker { use sui::table::{Self, Table}; + // ------ + // Errors + // ------ /// Attempt to borrow a trusted address but it's not registered. const ENoAddress: u64 = 0; @@ -13,6 +16,9 @@ module its::address_tracker { trusted_addresses: Table } + // ------ + // Getters + // ------ /// Get the trusted address for a chain. public fun get_trusted_address( self: &InterchainAddressTracker, chain_name: String @@ -55,6 +61,7 @@ module its::address_tracker { } } + // === Tests === #[test] fun test_address_tracker() { let ctx = &mut sui::tx_context::dummy(); @@ -87,6 +94,22 @@ module its::address_tracker { assert!(self.is_trusted_address(chain2, address1) == true, 10); assert!(self.is_trusted_address(chain2, address2) == false, 11); + assert!(self.trusted_addresses.contains(chain1), 12); + assert!(self.trusted_addresses.contains(chain2), 13); + + self.set_trusted_address(chain1, std::ascii::string(b"")); + self.set_trusted_address(chain2, std::ascii::string(b"")); + + assert!(!self.trusted_addresses.contains(chain1), 14); + assert!(!self.trusted_addresses.contains(chain2), 15); + + + self.set_trusted_address(chain1, std::ascii::string(b"")); + self.set_trusted_address(chain2, std::ascii::string(b"")); + + assert!(!self.trusted_addresses.contains(chain1), 16); + assert!(!self.trusted_addresses.contains(chain2), 17); + sui::test_utils::destroy(self); } diff --git a/move/its/sources/coin_info.move b/move/its/sources/coin_info.move index f7eebf00..56dbd508 100644 --- a/move/its/sources/coin_info.move +++ b/move/its/sources/coin_info.move @@ -68,6 +68,7 @@ module its::coin_info { &self.metadata } + // === Tests === #[test_only] public fun drop(coin_info: CoinInfo) { let CoinInfo {name: _, symbol: _, decimals: _, remote_decimals: _, metadata } = coin_info; @@ -77,4 +78,27 @@ module its::coin_info { metadata.destroy_none() } } + + #[test] + fun test_from_metadata() { + let ctx = &mut tx_context::dummy(); + let metadata = its::coin::create_metadata(b"Symbol", 8, ctx); + let metadata_bytes = sui::bcs::to_bytes(&metadata); + + let name = metadata.get_name(); + let symbol = metadata.get_symbol(); + let decimals = metadata.get_decimals(); + let remote_decimals = 31; + + let coin_info = from_metadata(metadata, remote_decimals); + + assert!(coin_info.name() == name, 0); + assert!(coin_info.symbol() == symbol, 1); + assert!(coin_info.decimals() == decimals, 2); + assert!(coin_info.remote_decimals() == remote_decimals, 3); + assert!(sui::bcs::to_bytes(coin_info.metadata().borrow()) == metadata_bytes, 4); + assert!(coin_info.scaling() == utils::pow(10, remote_decimals - decimals), 5); + + sui::test_utils::destroy(coin_info); + } } diff --git a/move/its/sources/coin_management.move b/move/its/sources/coin_management.move index f3ca48f6..5332fc05 100644 --- a/move/its/sources/coin_management.move +++ b/move/its/sources/coin_management.move @@ -10,6 +10,9 @@ module its::coin_management { use its::flow_limit::{Self, FlowLimit}; + // ------ + // Errors + // ------ /// Trying to add a distributor to a `CoinManagement` that does not /// have a `TreasuryCap`. const EDistributorNeedsTreasuryCap: u64 = 0; @@ -26,6 +29,9 @@ module its::coin_management { dust: u256, } + // ------ + // Public Functions to create CoinManagement + // ------ /// Create a new `CoinManagement` with a `TreasuryCap`. /// This type of `CoinManagement` allows minting and burning of coins. public fun new_with_cap(treasury_cap: TreasuryCap): CoinManagement { @@ -54,6 +60,10 @@ module its::coin_management { } } + + // ------ + // Functions that modify CoinManagement + // ------ /// Adds the distributor address to the `CoinManagement`. /// Only works for a `CoinManagement` with a `TreasuryCap`. public fun add_distributor(self: &mut CoinManagement, distributor: address) { @@ -136,7 +146,7 @@ module its::coin_management { self.treasury_cap.is_some() } - + // === Tests === #[test_only] public struct COIN_MANAGEMENT has drop {} diff --git a/move/its/sources/discovery.move b/move/its/sources/discovery.move index c4c5a86b..517a0712 100644 --- a/move/its/sources/discovery.move +++ b/move/its/sources/discovery.move @@ -178,6 +178,7 @@ module its::discovery { ) } + // === Tests === #[test_only] fun get_initial_tx(self: &ITS): Transaction { let mut arg = vector[0]; @@ -209,7 +210,7 @@ module its::discovery { #[test] fun test_discovery_initial() { let ctx = &mut sui::tx_context::dummy(); - let mut its = its::its::new(); + let mut its = its::its::new_for_testing(); let mut discovery = axelar_gateway::discovery::new(ctx); register_transaction(&mut its, &mut discovery); @@ -224,7 +225,7 @@ module its::discovery { #[test] fun test_discovery_interchain_transfer() { let ctx = &mut sui::tx_context::dummy(); - let mut its = its::its::new(); + let mut its = its::its::new_for_testing(); let mut discovery = axelar_gateway::discovery::new(ctx); register_transaction(&mut its, &mut discovery); @@ -245,7 +246,7 @@ module its::discovery { let payload = writer.into_bytes(); let type_arg = std::type_name::get(); - its.test_add_registered_coin_type(its::token_id::from_address(token_id), type_arg); + its.add_registered_coin_type_for_testing(its::token_id::from_address(token_id), type_arg); let tx_block = get_call_info(&its, payload); let mut reader = abi::new_reader(payload); @@ -277,7 +278,7 @@ module its::discovery { #[test] fun test_discovery_interchain_transfer_with_data() { let ctx = &mut sui::tx_context::dummy(); - let mut its = its::its::new(); + let mut its = its::its::new_for_testing(); let mut discovery = axelar_gateway::discovery::new(ctx); register_transaction(&mut its, &mut discovery); @@ -305,7 +306,7 @@ module its::discovery { .write_bytes(data); let payload = writer.into_bytes(); - its.test_add_registered_coin_type(its::token_id::from_address(token_id), std::type_name::get()); + its.add_registered_coin_type_for_testing(its::token_id::from_address(token_id), std::type_name::get()); let mut reader = abi::new_reader(payload); reader.skip_slot(); // skip message_type @@ -319,7 +320,7 @@ module its::discovery { #[test] fun test_discovery_deploy_token() { let ctx = &mut sui::tx_context::dummy(); - let mut its = its::its::new(); + let mut its = its::its::new_for_testing(); let mut discovery = axelar_gateway::discovery::new(ctx); register_transaction(&mut its, &mut discovery); @@ -340,7 +341,7 @@ module its::discovery { let payload = writer.into_bytes(); let type_arg = std::type_name::get(); - its.test_add_unregistered_coin_type(its::token_id::unregistered_token_id(&ascii::string(symbol), (decimals as u8)), type_arg); + its.add_unregistered_coin_type_for_testing(its::token_id::unregistered_token_id(&ascii::string(symbol), (decimals as u8)), type_arg); let tx_block = get_call_info(&its, payload); let mut reader = abi::new_reader(payload); diff --git a/move/its/sources/its.move b/move/its/sources/its.move index d07d3a12..6a0cd04a 100644 --- a/move/its/sources/its.move +++ b/move/its/sources/its.move @@ -2,7 +2,7 @@ module its::its { use std::string; - use std::ascii::String; + use std::ascii::{Self, String}; use std::type_name::{Self, TypeName}; use sui::bag::{Self, Bag}; @@ -16,10 +16,14 @@ module its::its { use its::address_tracker::{Self, InterchainAddressTracker}; use its::coin_info::CoinInfo; use its::coin_management::CoinManagement; + use its::trusted_addresses::TrustedAddresses; - /// Trying to read a token that doesn't exist. - const ENotFound: u64 = 0; - const EUnregisteredCoin: u64 = 1; + + // ------ + // Errors + // ------ + /// Trying to find a coin that doesn't exist. + const EUnregisteredCoin: u64 = 0; public struct ITS has key { id: UID, @@ -46,6 +50,7 @@ module its::its { coin_metadata: CoinMetadata, } + // === Initializer === fun init(ctx: &mut TxContext) { transfer::share_object(ITS { id: object::new(ctx), @@ -65,24 +70,13 @@ module its::its { }); } - public(package) fun set_relayer_discovery_id(self: &mut ITS, relayer_discovery: &RelayerDiscovery) { - self.relayer_discovery_id = object::id(relayer_discovery); - } - - public(package) fun relayer_discovery_id(self: &ITS): ID { - self.relayer_discovery_id - } - - public(package) fun set_trusted_address(self: &mut ITS, chain_name: String, trusted_address: String) { - self.address_tracker.set_trusted_address(chain_name, trusted_address); - } - + // === Getters === public fun get_unregistered_coin_type( self: &ITS, symbol: &String, decimals: u8 ): &TypeName { let key = token_id::unregistered_token_id(symbol, decimals); - assert!(self.unregistered_coin_types.contains(key), ENotFound); + assert!(self.unregistered_coin_types.contains(key), EUnregisteredCoin); &self.unregistered_coin_types[key] } @@ -96,15 +90,6 @@ module its::its { &self.registered_coins[token_id] } - public(package) fun get_coin_data_mut(self: &mut ITS, token_id: TokenId): &mut CoinData { - assert!(self.registered_coins.contains(token_id), EUnregisteredCoin); - &mut self.registered_coins[token_id] - } - - public(package) fun get_coin_scaling(self: &CoinData): u256 { - self.coin_info.scaling() - } - public fun get_coin_info(self: &ITS, token_id: TokenId): &CoinInfo { &get_coin_data(self, token_id).coin_info } @@ -138,6 +123,40 @@ module its::its { } // === Friend-only === + public(package) fun set_relayer_discovery_id(self: &mut ITS, relayer_discovery: &RelayerDiscovery) { + self.relayer_discovery_id = object::id(relayer_discovery); + } + + public(package) fun relayer_discovery_id(self: &ITS): ID { + self.relayer_discovery_id + } + + public(package) fun set_trusted_address(self: &mut ITS, chain_name: String, trusted_address: String) { + self.address_tracker.set_trusted_address(chain_name, trusted_address); + } + + public(package) fun set_trusted_addresses(self: &mut ITS, trusted_addresses: TrustedAddresses) { + let (mut chain_names, mut trusted_addresses) = trusted_addresses.destroy(); + let length = chain_names.length(); + let mut i = 0; + while(i < length) { + self.set_trusted_address( + ascii::string(chain_names.pop_back()), + ascii::string(trusted_addresses.pop_back()), + ); + i = i + 1; + } + } + + public(package) fun get_coin_data_mut(self: &mut ITS, token_id: TokenId): &mut CoinData { + assert!(self.registered_coins.contains(token_id), EUnregisteredCoin); + &mut self.registered_coins[token_id] + } + + public(package) fun get_coin_scaling(self: &CoinData): u256 { + self.coin_info.scaling() + } + public(package) fun channel(self: &ITS): &Channel { &self.channel } @@ -196,7 +215,6 @@ module its::its { } // === Private === - fun add_unregistered_coin_type(self: &mut ITS, token_id: UnregisteredTokenId, type_name: TypeName) { self.unregistered_coin_types.add(token_id, type_name); } @@ -210,14 +228,15 @@ module its::its { } #[allow(unused_function)] - fun remove_registered_coin_type(self: &mut ITS, token_id: TokenId): TypeName { + fun remove_registered_coin_type_for_testing(self: &mut ITS, token_id: TokenId): TypeName { self.registered_coin_types.remove(token_id) } + // === Tests === #[test_only] - public fun new(): ITS { + public fun new_for_testing(): ITS { let ctx = &mut sui::tx_context::dummy(); - ITS { + let mut its = ITS { id: object::new(ctx), channel: axelar_gateway::channel::new(ctx), @@ -232,26 +251,30 @@ module its::its { unregistered_coin_types: table::new(ctx), relayer_discovery_id: object::id_from_address(@0x0) - } + }; + + its.set_trusted_address(std::ascii::string(b"Chain Name"), std::ascii::string(b"Address")); + + its } #[test_only] - public fun test_add_unregistered_coin_type(self: &mut ITS, token_id: UnregisteredTokenId, type_name: TypeName) { + public fun add_unregistered_coin_type_for_testing(self: &mut ITS, token_id: UnregisteredTokenId, type_name: TypeName) { self.add_unregistered_coin_type(token_id, type_name); } #[test_only] - public fun test_remove_unregistered_coin_type(self: &mut ITS, token_id: UnregisteredTokenId): TypeName { + public fun remove_unregistered_coin_type_for_testing(self: &mut ITS, token_id: UnregisteredTokenId): TypeName { self.remove_unregistered_coin_type(token_id) } #[test_only] - public fun test_add_registered_coin_type(self: &mut ITS, token_id: TokenId, type_name: TypeName) { + public fun add_registered_coin_type_for_testing(self: &mut ITS, token_id: TokenId, type_name: TypeName) { self.add_registered_coin_type(token_id, type_name); } #[test_only] - public fun test_remove_registered_coin_type(self: &mut ITS, token_id: TokenId): TypeName { - self.remove_registered_coin_type(token_id) + public fun test_remove_registered_coin_type_for_testing(self: &mut ITS, token_id: TokenId): TypeName { + self.remove_registered_coin_type_for_testing(token_id) } } diff --git a/move/its/sources/service.move b/move/its/sources/service.move index b66bc012..8b679d23 100644 --- a/move/its/sources/service.move +++ b/move/its/sources/service.move @@ -20,27 +20,32 @@ module its::service { use its::token_id::{Self, TokenId}; use its::coin_management::{Self, CoinManagement}; use its::utils as its_utils; + use its::trusted_addresses; use axelar_gateway::gateway; use axelar_gateway::channel::Channel; + // === MESSAGE TYPES === const MESSAGE_TYPE_INTERCHAIN_TRANSFER: u256 = 0; const MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN: u256 = 1; //const MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER: u256 = 2; const MESSAGE_TYPE_SEND_TO_HUB: u256 = 3; - const MESSAGE_TYPE_RECEIVE_FROM_HUB: u256 = 4; + const MESSAGE_TYPE_RECEIVE_FROM_HUB: u256 = 4; + // address::to_u256(address::from_bytes(keccak256(b"sui-set-trusted-addresses"))); + const MESSAGE_TYPE_SET_TRUSTED_ADDRESSES: u256 = 0x2af37a0d5d48850a855b1aaaf57f726c107eb99b40eabf4cc1ba30410cfa2f68; + + + // === HUB CONSTANTS === // Chain name for Axelar. This is used for routing ITS calls via ITS hub on Axelar. const ITS_HUB_CHAIN_NAME: vector = b"Axelarnet"; - // Identifier to be used as destination address for chains that route to hub. For Sui this will probably be every supported chain. const ITS_HUB_ROUTING_IDENTIFIER: vector = b"hub"; - // address::to_u256(address::from_bytes(keccak256(b"sui-set-trusted-addresses"))); - const MESSAGE_TYPE_SET_TRUSTED_ADDRESSES: u256 = 0x2af37a0d5d48850a855b1aaaf57f726c107eb99b40eabf4cc1ba30410cfa2f68; - + // === The maximum number of decimals allowed === const DECIMALS_CAP: u8 = 9; + // === ERRROS CODES === const EUntrustedAddress: u64 = 0; const EInvalidMessageType: u64 = 1; const EWrongDestination: u64 = 2; @@ -50,23 +55,27 @@ module its::service { const ENotDistributor: u64 = 6; const ENonZeroTotalSupply: u64 = 7; const EUnregisteredCoinHasUrl: u64 = 8; - const EMalformedTrustedAddresses: u64 = 9; - const EUntrustedChain: u64 = 10; + const EUntrustedChain: u64 = 9; + const ERemainingData: u64 = 19; + // === Events === public struct CoinRegistered has copy, drop { token_id: TokenId, } + // === Public Functions === public fun register_coin( self: &mut ITS, coin_info: CoinInfo, coin_management: CoinManagement - ) { + ): TokenId { let token_id = token_id::from_coin_data(&coin_info, &coin_management); self.add_registered_coin(token_id, coin_management, coin_info); event::emit(CoinRegistered { token_id - }) + }); + + token_id } public fun deploy_remote_interchain_token( @@ -177,9 +186,8 @@ module its::service { let name = string::utf8(reader.read_bytes()); let symbol = ascii::string(reader.read_bytes()); let remote_decimals = (reader.read_u256() as u8); - let distributor = address::from_bytes(reader.read_bytes()); + let distributor_bytes = reader.read_bytes(); let decimals = if (remote_decimals > DECIMALS_CAP) DECIMALS_CAP else remote_decimals; - let (treasury_cap, mut coin_metadata) = self.remove_unregistered_coin( token_id::unregistered_token_id(&symbol, decimals) ); @@ -190,7 +198,10 @@ module its::service { let mut coin_management = coin_management::new_with_cap(treasury_cap); let coin_info = coin_info::from_metadata(coin_metadata, remote_decimals); - coin_management.add_distributor(distributor); + if (distributor_bytes.length() > 0) { + let distributor = address::from_bytes(distributor_bytes); + coin_management.add_distributor(distributor); + }; self.add_registered_coin(token_id, coin_management, coin_info); } @@ -219,17 +230,27 @@ module its::service { self: &mut ITS, channel: &Channel, token_id: TokenId, - to: address, amount: u64, ctx: &mut TxContext - ) { + ): Coin { let coin_management = self.coin_management_mut(token_id); let distributor = channel.to_address(); assert!(coin_management.is_distributor(distributor), ENotDistributor); - let coin = coin_management.mint(amount, ctx); - transfer::public_transfer(coin, to) + coin_management.mint(amount, ctx) + } + + public fun mint_to_as_distributor( + self: &mut ITS, + channel: &Channel, + token_id: TokenId, + to: address, + amount: u64, + ctx: &mut TxContext + ) { + let coin = mint_as_distributor(self, channel, token_id, amount, ctx); + transfer::public_transfer(coin, to); } public fun burn_as_distributor( @@ -258,23 +279,12 @@ module its::service { let message_type = reader.read_u256(); assert!(message_type == MESSAGE_TYPE_SET_TRUSTED_ADDRESSES, EInvalidMessageType); - let mut trusted_address_info = bcs::new(reader.read_bytes()); + let mut bcs = bcs::new(reader.read_bytes()); + let trusted_addresses = trusted_addresses::peel(&mut bcs); - let mut chain_names = trusted_address_info.peel_vec_vec_u8(); - let mut trusted_addresses = trusted_address_info.peel_vec_vec_u8(); + assert!(bcs.into_remainder_bytes().length() == 0, ERemainingData); - let length = chain_names.length(); - - assert!(length == trusted_addresses.length(), EMalformedTrustedAddresses); - - let mut i = 0; - while(i < length) { - its.set_trusted_address( - ascii::string(chain_names.pop_back()), - ascii::string(trusted_addresses.pop_back()), - ); - i = i + 1; - } + its.set_trusted_addresses(trusted_addresses); } // === Internal functions === @@ -325,4 +335,1100 @@ module its::service { gateway::call_contract(self.channel_mut(), destination_chain, destination_address, payload); } + + // === Tests === + #[test_only] + use its::coin::COIN; + + #[test_only] + public fun create_unregistered_coin(self: &mut ITS, symbol: vector, decimals: u8, ctx: &mut TxContext) { + let (treasury_cap, coin_metadata) = its::coin::create_treasury_and_metadata(symbol, decimals, ctx); + let token_id = token_id::unregistered_token_id(&ascii::string(symbol), decimals); + + self.add_unregistered_coin(token_id, treasury_cap, coin_metadata); + } + + #[test] + fun test_register_coin() { + let mut its = its::its::new_for_testing(); + + let coin_info = its::coin_info::from_info( + string::utf8(b"Name"), + ascii::string(b"Symbol"), + 10, + 12, + ); + let coin_management = its::coin_management::new_locked(); + + register_coin(&mut its, coin_info, coin_management); + + sui::test_utils::destroy(its); + } + + #[test] + fun test_deploy_remote_interchain_token() { + let mut its = its::its::new_for_testing(); + + let coin_info = its::coin_info::from_info( + string::utf8(b"Name"), + ascii::string(b"Symbol"), + 10, + 12, + ); + let coin_management = its::coin_management::new_locked(); + + let token_id = register_coin(&mut its, coin_info, coin_management); + let destination_chain = ascii::string(b"Chain Name"); + deploy_remote_interchain_token(&mut its, token_id, destination_chain); + + sui::test_utils::destroy(its); + } + + #[test] + fun test_deploy_interchain_transfer() { + let ctx = &mut tx_context::dummy(); + let mut its = its::its::new_for_testing(); + + let coin_info = its::coin_info::from_info( + string::utf8(b"Name"), + ascii::string(b"Symbol"), + 10, + 12, + ); + let coin_management = its::coin_management::new_locked(); + + let token_id = register_coin(&mut its, coin_info, coin_management); + let amount = 1234; + let coin = sui::coin::mint_for_testing(amount, ctx); + let destination_chain = ascii::string(b"Chain Name"); + let destination_address = b"address"; + let metadata = b""; + let clock = sui::clock::create_for_testing(ctx); + interchain_transfer(&mut its, token_id, coin, destination_chain, destination_address, metadata, &clock, ctx); + + clock.destroy_for_testing(); + sui::test_utils::destroy(its); + } + + #[test] + fun test_receive_interchain_transfer() { + let ctx = &mut tx_context::dummy(); + let clock = sui::clock::create_for_testing(ctx); + let mut its = its::its::new_for_testing(); + + let coin_info = its::coin_info::from_info( + string::utf8(b"Name"), + ascii::string(b"Symbol"), + 10, + 12, + ); + + let amount = 1234; + let mut coin_management = its::coin_management::new_locked(); + let coin = sui::coin::mint_for_testing(amount, ctx); + coin_management.take_coin(coin, &clock); + + let token_id = register_coin(&mut its, coin_info, coin_management); + let source_chain = ascii::string(b"Chain Name"); + let message_id = ascii::string(b"Message Id"); + let message_source_address = ascii::string(b"Address"); + let its_source_address = b"Source Address"; + let destination_address = @0x1; + + let mut writer = abi::new_writer(6); + writer + .write_u256(MESSAGE_TYPE_INTERCHAIN_TRANSFER) + .write_u256(token_id.to_u256()) + .write_bytes(its_source_address) + .write_bytes(destination_address.to_bytes()) + .write_u256((amount as u256)) + .write_bytes(b""); + let payload = writer.into_bytes(); + + let approved_message = channel::new_approved_message( + source_chain, + message_id, + message_source_address, + its.channel().to_address(), + payload, + ); + + receive_interchain_transfer(&mut its, approved_message, &clock, ctx); + + clock.destroy_for_testing(); + sui::test_utils::destroy(its); + } + + #[test] + #[expected_failure(abort_code = EInvalidMessageType)] + fun test_receive_interchain_transfer_invalid_message_type() { + let ctx = &mut tx_context::dummy(); + let clock = sui::clock::create_for_testing(ctx); + let mut its = its::its::new_for_testing(); + + let coin_info = its::coin_info::from_info( + string::utf8(b"Name"), + ascii::string(b"Symbol"), + 10, + 12, + ); + + let amount = 1234; + let mut coin_management = its::coin_management::new_locked(); + let coin = sui::coin::mint_for_testing(amount, ctx); + coin_management.take_coin(coin, &clock); + + let token_id = register_coin(&mut its, coin_info, coin_management); + let source_chain = ascii::string(b"Chain Name"); + let message_id = ascii::string(b"Message Id"); + let message_source_address = ascii::string(b"Address"); + let its_source_address = b"Source Address"; + let destination_address = @0x1; + + let mut writer = abi::new_writer(6); + writer + .write_u256(MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN) + .write_u256(token_id.to_u256()) + .write_bytes(its_source_address) + .write_bytes(destination_address.to_bytes()) + .write_u256((amount as u256)) + .write_bytes(b""); + let payload = writer.into_bytes(); + + let approved_message = channel::new_approved_message( + source_chain, + message_id, + message_source_address, + its.channel().to_address(), + payload, + ); + + receive_interchain_transfer(&mut its, approved_message, &clock, ctx); + + clock.destroy_for_testing(); + sui::test_utils::destroy(its); + } + + #[test] + #[expected_failure(abort_code = EInterchainTransferHasData)] + fun test_receive_interchain_transfer_passed_data() { + let ctx = &mut tx_context::dummy(); + let clock = sui::clock::create_for_testing(ctx); + let mut its = its::its::new_for_testing(); + + let coin_info = its::coin_info::from_info( + string::utf8(b"Name"), + ascii::string(b"Symbol"), + 10, + 12, + ); + + let amount = 1234; + let mut coin_management = its::coin_management::new_locked(); + let coin = sui::coin::mint_for_testing(amount, ctx); + coin_management.take_coin(coin, &clock); + + let token_id = register_coin(&mut its, coin_info, coin_management); + let source_chain = ascii::string(b"Chain Name"); + let message_id = ascii::string(b"Message Id"); + let message_source_address = ascii::string(b"Address"); + let its_source_address = b"Source Address"; + let destination_address = @0x1; + + let mut writer = abi::new_writer(6); + writer + .write_u256(MESSAGE_TYPE_INTERCHAIN_TRANSFER) + .write_u256(token_id.to_u256()) + .write_bytes(its_source_address) + .write_bytes(destination_address.to_bytes()) + .write_u256((amount as u256)) + .write_bytes(b"some data"); + let payload = writer.into_bytes(); + + let approved_message = channel::new_approved_message( + source_chain, + message_id, + message_source_address, + its.channel().to_address(), + payload, + ); + + receive_interchain_transfer(&mut its, approved_message, &clock, ctx); + + clock.destroy_for_testing(); + sui::test_utils::destroy(its); + } + + #[test] + fun test_receive_interchain_transfer_with_data() { + let ctx = &mut tx_context::dummy(); + let clock = sui::clock::create_for_testing(ctx); + let mut its = its::its::new_for_testing(); + + let coin_info = its::coin_info::from_info( + string::utf8(b"Name"), + ascii::string(b"Symbol"), + 10, + 12, + ); + let scaling = coin_info.scaling(); + + let amount = 1234; + let data = b"some_data"; + let mut coin_management = its::coin_management::new_locked(); + let coin = sui::coin::mint_for_testing(amount, ctx); + coin_management.take_coin(coin, &clock); + + let token_id = register_coin(&mut its, coin_info, coin_management); + let source_chain = ascii::string(b"Chain Name"); + let message_id = ascii::string(b"Message Id"); + let message_source_address = ascii::string(b"Address"); + let its_source_address = b"Source Address"; + let channel = channel::new(ctx); + let destination_address = channel.to_address(); + + let mut writer = abi::new_writer(6); + writer + .write_u256(MESSAGE_TYPE_INTERCHAIN_TRANSFER) + .write_u256(token_id.to_u256()) + .write_bytes(its_source_address) + .write_bytes(destination_address.to_bytes()) + .write_u256((amount as u256)) + .write_bytes(data); + let payload = writer.into_bytes(); + + let approved_message = channel::new_approved_message( + source_chain, + message_id, + message_source_address, + its.channel().to_address(), + payload, + ); + + let (received_source_chain, received_source_address, received_data, received_coin) = receive_interchain_transfer_with_data(&mut its, approved_message, &channel, &clock, ctx); + + assert!(received_source_chain == source_chain, 0); + assert!(received_source_address == its_source_address, 1); + assert!(received_data == data, 2); + assert!(received_coin.value() == amount / (scaling as u64), 3); + + clock.destroy_for_testing(); + channel.destroy(); + sui::test_utils::destroy(its); + sui::test_utils::destroy(received_coin); + } + + #[test] + #[expected_failure(abort_code = EInvalidMessageType)] + fun test_receive_interchain_transfer_with_data_invalid_message_type() { + let ctx = &mut tx_context::dummy(); + let clock = sui::clock::create_for_testing(ctx); + let mut its = its::its::new_for_testing(); + + let coin_info = its::coin_info::from_info( + string::utf8(b"Name"), + ascii::string(b"Symbol"), + 10, + 12, + ); + + let amount = 1234; + let mut coin_management = its::coin_management::new_locked(); + let coin = sui::coin::mint_for_testing(amount, ctx); + coin_management.take_coin(coin, &clock); + + let token_id = register_coin(&mut its, coin_info, coin_management); + let source_chain = ascii::string(b"Chain Name"); + let message_id = ascii::string(b"Message Id"); + let message_source_address = ascii::string(b"Address"); + let its_source_address = b"Source Address"; + let channel = channel::new(ctx); + let destination_address = channel.to_address(); + + let mut writer = abi::new_writer(6); + writer + .write_u256(MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN) + .write_u256(token_id.to_u256()) + .write_bytes(its_source_address) + .write_bytes(destination_address.to_bytes()) + .write_u256((amount as u256)) + .write_bytes(b"some_data"); + let payload = writer.into_bytes(); + + let approved_message = channel::new_approved_message( + source_chain, + message_id, + message_source_address, + its.channel().to_address(), + payload, + ); + + let (_, _, _, received_coin) = receive_interchain_transfer_with_data(&mut its, approved_message, &channel, &clock, ctx); + + clock.destroy_for_testing(); + channel.destroy(); + sui::test_utils::destroy(its); + sui::test_utils::destroy(received_coin); + } + + #[test] + #[expected_failure(abort_code = EWrongDestination)] + fun test_receive_interchain_transfer_with_data_wrong_destination() { + let ctx = &mut tx_context::dummy(); + let clock = sui::clock::create_for_testing(ctx); + let mut its = its::its::new_for_testing(); + + let coin_info = its::coin_info::from_info( + string::utf8(b"Name"), + ascii::string(b"Symbol"), + 10, + 12, + ); + + let amount = 1234; + let mut coin_management = its::coin_management::new_locked(); + let coin = sui::coin::mint_for_testing(amount, ctx); + coin_management.take_coin(coin, &clock); + + let token_id = register_coin(&mut its, coin_info, coin_management); + let source_chain = ascii::string(b"Chain Name"); + let message_id = ascii::string(b"Message Id"); + let message_source_address = ascii::string(b"Address"); + let its_source_address = b"Source Address"; + let channel = channel::new(ctx); + let destination_address = @0x1; + + let mut writer = abi::new_writer(6); + writer + .write_u256(MESSAGE_TYPE_INTERCHAIN_TRANSFER) + .write_u256(token_id.to_u256()) + .write_bytes(its_source_address) + .write_bytes(destination_address.to_bytes()) + .write_u256((amount as u256)) + .write_bytes(b"some_data"); + let payload = writer.into_bytes(); + + let approved_message = channel::new_approved_message( + source_chain, + message_id, + message_source_address, + its.channel().to_address(), + payload, + ); + + let (_, _, _, received_coin) = receive_interchain_transfer_with_data(&mut its, approved_message, &channel, &clock, ctx); + + clock.destroy_for_testing(); + channel.destroy(); + sui::test_utils::destroy(its); + sui::test_utils::destroy(received_coin); + } + + #[test] + #[expected_failure(abort_code = EInterchainTransferHasNoData)] + fun test_receive_interchain_transfer_with_data_no_data() { + let ctx = &mut tx_context::dummy(); + let clock = sui::clock::create_for_testing(ctx); + let mut its = its::its::new_for_testing(); + + let coin_info = its::coin_info::from_info( + string::utf8(b"Name"), + ascii::string(b"Symbol"), + 10, + 12, + ); + + let amount = 1234; + let mut coin_management = its::coin_management::new_locked(); + let coin = sui::coin::mint_for_testing(amount, ctx); + coin_management.take_coin(coin, &clock); + + let token_id = register_coin(&mut its, coin_info, coin_management); + let source_chain = ascii::string(b"Chain Name"); + let message_id = ascii::string(b"Message Id"); + let message_source_address = ascii::string(b"Address"); + let its_source_address = b"Source Address"; + let channel = channel::new(ctx); + let destination_address = channel.to_address(); + + let mut writer = abi::new_writer(6); + writer + .write_u256(MESSAGE_TYPE_INTERCHAIN_TRANSFER) + .write_u256(token_id.to_u256()) + .write_bytes(its_source_address) + .write_bytes(destination_address.to_bytes()) + .write_u256((amount as u256)) + .write_bytes(b""); + let payload = writer.into_bytes(); + + let approved_message = channel::new_approved_message( + source_chain, + message_id, + message_source_address, + its.channel().to_address(), + payload, + ); + + let (_, _, _, received_coin) = receive_interchain_transfer_with_data(&mut its, approved_message, &channel, &clock, ctx); + + clock.destroy_for_testing(); + channel.destroy(); + sui::test_utils::destroy(its); + sui::test_utils::destroy(received_coin); + } + + #[test] + fun test_receive_deploy_interchain_token() { + let ctx = &mut tx_context::dummy(); + let clock = sui::clock::create_for_testing(ctx); + let mut its = its::its::new_for_testing(); + + let source_chain = ascii::string(b"Chain Name"); + let message_id = ascii::string(b"Message Id"); + let source_address = ascii::string(b"Address"); + let name = b"Token Name"; + let symbol = b"Symbol"; + let remote_decimals = 12; + let decimals = if (remote_decimals > DECIMALS_CAP) DECIMALS_CAP else remote_decimals; + let token_id: u256 = 1234; + + create_unregistered_coin(&mut its, symbol, decimals, ctx); + + let mut writer = abi::new_writer(6); + writer + .write_u256(MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN) + .write_u256(token_id) + .write_bytes(name) + .write_bytes(symbol) + .write_u256((remote_decimals as u256)) + .write_bytes(vector::empty()); + let payload = writer.into_bytes(); + + let approved_message = channel::new_approved_message( + source_chain, + message_id, + source_address, + its.channel().to_address(), + payload, + ); + + receive_deploy_interchain_token(&mut its, approved_message); + + clock.destroy_for_testing(); + sui::test_utils::destroy(its); + } + + #[test] + fun test_receive_deploy_interchain_token_with_distributor() { + let ctx = &mut tx_context::dummy(); + let clock = sui::clock::create_for_testing(ctx); + let mut its = its::its::new_for_testing(); + + let source_chain = ascii::string(b"Chain Name"); + let message_id = ascii::string(b"Message Id"); + let source_address = ascii::string(b"Address"); + let name = b"Token Name"; + let symbol = b"Symbol"; + let remote_decimals = 8; + let decimals = if (remote_decimals > DECIMALS_CAP) DECIMALS_CAP else remote_decimals; + let token_id: u256 = 1234; + let distributor = @0x1; + + create_unregistered_coin(&mut its, symbol, decimals, ctx); + + let mut writer = abi::new_writer(6); + writer + .write_u256(MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN) + .write_u256(token_id) + .write_bytes(name) + .write_bytes(symbol) + .write_u256((remote_decimals as u256)) + .write_bytes(distributor.to_bytes()); + let payload = writer.into_bytes(); + + let approved_message = channel::new_approved_message( + source_chain, + message_id, + source_address, + its.channel().to_address(), + payload, + ); + + receive_deploy_interchain_token(&mut its, approved_message); + + clock.destroy_for_testing(); + sui::test_utils::destroy(its); + } + + #[test] + #[expected_failure(abort_code = EInvalidMessageType)] + fun test_receive_deploy_interchain_token_invalid_message_type() { + let ctx = &mut tx_context::dummy(); + let clock = sui::clock::create_for_testing(ctx); + let mut its = its::its::new_for_testing(); + + let source_chain = ascii::string(b"Chain Name"); + let message_id = ascii::string(b"Message Id"); + let source_address = ascii::string(b"Address"); + let name = b"Token Name"; + let symbol = b"Symbol"; + let remote_decimals = 8; + let decimals = if (remote_decimals > DECIMALS_CAP) DECIMALS_CAP else remote_decimals; + let token_id: u256 = 1234; + + create_unregistered_coin(&mut its, symbol, decimals, ctx); + + let mut writer = abi::new_writer(6); + writer + .write_u256(MESSAGE_TYPE_INTERCHAIN_TRANSFER) + .write_u256(token_id) + .write_bytes(name) + .write_bytes(symbol) + .write_u256((remote_decimals as u256)) + .write_bytes(b""); + let payload = writer.into_bytes(); + + let approved_message = channel::new_approved_message( + source_chain, + message_id, + source_address, + its.channel().to_address(), + payload, + ); + + receive_deploy_interchain_token(&mut its, approved_message); + + clock.destroy_for_testing(); + sui::test_utils::destroy(its); + } + + #[test] + fun test_give_unregistered_coin() { + let symbol = b"COIN"; + let decimals = 12; + let ctx = &mut tx_context::dummy(); + let mut its = its::its::new_for_testing(); + + let (treasury_cap, coin_metadata) = its::coin::create_treasury_and_metadata(symbol, decimals, ctx); + + give_unregistered_coin(&mut its, treasury_cap, coin_metadata); + + sui::test_utils::destroy(its); + } + + #[test] + #[expected_failure(abort_code = ENonZeroTotalSupply)] + fun test_give_unregistered_coin_not_zero_total_supply() { + let symbol = b"COIN"; + let decimals = 12; + let ctx = &mut tx_context::dummy(); + let mut its = its::its::new_for_testing(); + + let (mut treasury_cap, coin_metadata) = its::coin::create_treasury_and_metadata(symbol, decimals, ctx); + let coin = treasury_cap.mint(1, ctx); + + give_unregistered_coin(&mut its, treasury_cap, coin_metadata); + + sui::test_utils::destroy(its); + sui::test_utils::destroy(coin); + } + + #[test] + #[expected_failure(abort_code = EUnregisteredCoinHasUrl)] + fun test_give_unregistered_coin_with_url() { + let name = b"Coin"; + let symbol = b"COIN"; + let decimals = 12; + let ctx = &mut tx_context::dummy(); + let mut its = its::its::new_for_testing(); + let url = sui::url::new_unsafe_from_bytes(b"url"); + + let (treasury_cap, coin_metadata) = its::coin::create_treasury_and_metadata_custom(name, symbol, decimals, option::some(url), ctx); + + give_unregistered_coin(&mut its, treasury_cap, coin_metadata); + + sui::test_utils::destroy(its); + } + + #[test] + #[expected_failure(abort_code = EModuleNameDoesNotMatchSymbol)] + fun test_give_unregistered_coin_module_name_missmatch() { + let symbol = b"SYMBOL"; + let decimals = 12; + let ctx = &mut tx_context::dummy(); + let mut its = its::its::new_for_testing(); + + let (treasury_cap, coin_metadata) = its::coin::create_treasury_and_metadata(symbol, decimals, ctx); + + give_unregistered_coin(&mut its, treasury_cap, coin_metadata); + + sui::test_utils::destroy(its); + } + + #[test] + fun test_mint_as_distributor() { + let mut its = its::its::new_for_testing(); + let ctx = &mut tx_context::dummy(); + let symbol = b"COIN"; + let decimals = 9; + let remote_decimals = 18; + + let (treasury_cap, coin_metadata) = its::coin::create_treasury_and_metadata(symbol, decimals, ctx); + let coin_info = its::coin_info::from_metadata( + coin_metadata, + remote_decimals, + ); + let mut coin_management = its::coin_management::new_with_cap(treasury_cap); + + let channel = channel::new(ctx); + coin_management.add_distributor(channel.to_address()); + let amount = 1234; + + let token_id = register_coin(&mut its, coin_info, coin_management); + let coin = mint_as_distributor(&mut its, &channel, token_id, amount, ctx); + + assert!(coin.value() == amount); + + sui::test_utils::destroy(its); + sui::test_utils::destroy(coin); + channel.destroy(); + } + + #[test] + #[expected_failure(abort_code = ENotDistributor)] + fun test_mint_as_distributor_not_distributor() { + let mut its = its::its::new_for_testing(); + let ctx = &mut tx_context::dummy(); + let symbol = b"COIN"; + let decimals = 9; + let remote_decimals = 18; + + let (treasury_cap, coin_metadata) = its::coin::create_treasury_and_metadata(symbol, decimals, ctx); + let coin_info = its::coin_info::from_metadata( + coin_metadata, + remote_decimals, + ); + let mut coin_management = its::coin_management::new_with_cap(treasury_cap); + + let channel = channel::new(ctx); + coin_management.add_distributor(@0x1); + let amount = 1234; + + let token_id = register_coin(&mut its, coin_info, coin_management); + let coin = mint_as_distributor(&mut its, &channel, token_id, amount, ctx); + + assert!(coin.value() == amount); + + sui::test_utils::destroy(its); + sui::test_utils::destroy(coin); + channel.destroy(); + } + + #[test] + fun test_mint_to_as_distributor() { + let mut its = its::its::new_for_testing(); + let ctx = &mut tx_context::dummy(); + let symbol = b"COIN"; + let decimals = 9; + let remote_decimals = 18; + + let (treasury_cap, coin_metadata) = its::coin::create_treasury_and_metadata(symbol, decimals, ctx); + let coin_info = its::coin_info::from_metadata( + coin_metadata, + remote_decimals, + ); + let mut coin_management = its::coin_management::new_with_cap(treasury_cap); + + let channel = channel::new(ctx); + coin_management.add_distributor(channel.to_address()); + let amount = 1234; + + let token_id = register_coin(&mut its, coin_info, coin_management); + mint_to_as_distributor(&mut its, &channel, token_id, @0x2, amount, ctx); + + sui::test_utils::destroy(its); + channel.destroy(); + } + + #[test] + fun test_burn_as_distributor() { + let mut its = its::its::new_for_testing(); + let ctx = &mut tx_context::dummy(); + let symbol = b"COIN"; + let decimals = 9; + let remote_decimals = 18; + let amount = 1234; + + let (mut treasury_cap, coin_metadata) = its::coin::create_treasury_and_metadata(symbol, decimals, ctx); + let coin = treasury_cap.mint(amount, ctx); + let coin_info = its::coin_info::from_metadata( + coin_metadata, + remote_decimals, + ); + let mut coin_management = its::coin_management::new_with_cap(treasury_cap); + + let channel = channel::new(ctx); + coin_management.add_distributor(channel.to_address()); + + let token_id = register_coin(&mut its, coin_info, coin_management); + burn_as_distributor(&mut its, &channel, token_id, coin); + + sui::test_utils::destroy(its); + channel.destroy(); + } + + #[test] + #[expected_failure(abort_code = ENotDistributor)] + fun test_burn_as_distributor_not_distributor() { + let mut its = its::its::new_for_testing(); + let ctx = &mut tx_context::dummy(); + let symbol = b"COIN"; + let decimals = 9; + let remote_decimals = 18; + let amount = 1234; + + let (mut treasury_cap, coin_metadata) = its::coin::create_treasury_and_metadata(symbol, decimals, ctx); + let coin = treasury_cap.mint(amount, ctx); + let coin_info = its::coin_info::from_metadata( + coin_metadata, + remote_decimals, + ); + let mut coin_management = its::coin_management::new_with_cap(treasury_cap); + + let channel = channel::new(ctx); + coin_management.add_distributor(@0x1); + + let token_id = register_coin(&mut its, coin_info, coin_management); + burn_as_distributor(&mut its, &channel, token_id, coin); + + sui::test_utils::destroy(its); + channel.destroy(); + } + + #[test] + fun test_set_trusted_address() { + let mut its = its::its::new_for_testing(); + let trusted_source_chain = ascii::string(b"Axelar"); + let trusted_source_address = ascii::string(b"Trusted Address"); + let message_type = (123 as u256); + let message_id = ascii::string(b"message_id"); + let ctx = &mut tx_context::dummy(); + + let governance = governance::new_for_testing( + trusted_source_chain, + trusted_source_address, + message_type, + ctx, + ); + + let trusted_chains = vector[ + b"Ethereum", + b"Avalance", + b"Axelar", + ]; + let trusted_addresses = vector[ + b"ethereum address", + ITS_HUB_ROUTING_IDENTIFIER, + b"hub address", + ]; + let trusted_addresses_data = bcs::to_bytes(&its::trusted_addresses::new_for_testing(trusted_chains, trusted_addresses)); + + let mut writer = abi::new_writer(2); + writer + .write_u256(MESSAGE_TYPE_SET_TRUSTED_ADDRESSES) + .write_bytes(trusted_addresses_data); + let payload = writer.into_bytes(); + + let approved_message = channel::new_approved_message( + trusted_source_chain, + message_id, + trusted_source_address, + its.channel().to_address(), + payload, + ); + + set_trusted_addresses(&mut its, &governance, approved_message); + + sui::test_utils::destroy(its); + sui::test_utils::destroy(governance); + } + + #[test] + #[expected_failure(abort_code = EUntrustedAddress)] + fun test_set_trusted_address_untrusted_address() { + let mut its = its::its::new_for_testing(); + let trusted_source_chain = ascii::string(b"Axelar"); + let trusted_source_address = ascii::string(b"Trusted Address"); + let untrusted_source_address = ascii::string(b"Untrusted Address"); + let message_type = (123 as u256); + let message_id = ascii::string(b"message_id"); + let ctx = &mut tx_context::dummy(); + + let governance = governance::new_for_testing( + trusted_source_chain, + trusted_source_address, + message_type, + ctx, + ); + + let trusted_chains = vector[ + b"Ethereum", + b"Avalance", + b"Axelar", + ]; + let trusted_addresses = vector[ + b"ethereum address", + ITS_HUB_ROUTING_IDENTIFIER, + b"hub address", + ]; + let trusted_addresses_data = bcs::to_bytes(&its::trusted_addresses::new_for_testing(trusted_chains, trusted_addresses)); + + let mut writer = abi::new_writer(2); + writer + .write_u256(MESSAGE_TYPE_SET_TRUSTED_ADDRESSES) + .write_bytes(trusted_addresses_data); + let payload = writer.into_bytes(); + + let approved_message = channel::new_approved_message( + trusted_source_chain, + message_id, + untrusted_source_address, + its.channel().to_address(), + payload, + ); + + set_trusted_addresses(&mut its, &governance, approved_message); + + sui::test_utils::destroy(its); + sui::test_utils::destroy(governance); + } + + #[test] + #[expected_failure(abort_code = EInvalidMessageType)] + fun test_set_trusted_address_invalid_message_type() { + let mut its = its::its::new_for_testing(); + let trusted_source_chain = ascii::string(b"Axelar"); + let trusted_source_address = ascii::string(b"Trusted Address"); + let message_type = (123 as u256); + let message_id = ascii::string(b"message_id"); + let ctx = &mut tx_context::dummy(); + + let governance = governance::new_for_testing( + trusted_source_chain, + trusted_source_address, + message_type, + ctx, + ); + + let trusted_chains = vector[ + b"Ethereum", + b"Avalance", + b"Axelar", + ]; + let trusted_addresses = vector[ + b"ethereum address", + ITS_HUB_ROUTING_IDENTIFIER, + b"hub address", + ]; + let trusted_addresses_data = bcs::to_bytes(&its::trusted_addresses::new_for_testing(trusted_chains, trusted_addresses)); + + let mut writer = abi::new_writer(2); + writer + .write_u256(MESSAGE_TYPE_INTERCHAIN_TRANSFER) + .write_bytes(trusted_addresses_data); + let payload = writer.into_bytes(); + + let approved_message = channel::new_approved_message( + trusted_source_chain, + message_id, + trusted_source_address, + its.channel().to_address(), + payload, + ); + + set_trusted_addresses(&mut its, &governance, approved_message); + + sui::test_utils::destroy(its); + sui::test_utils::destroy(governance); + } + + #[test] + #[expected_failure(abort_code = ERemainingData)] + fun test_set_trusted_address_remaining_data() { + let mut its = its::its::new_for_testing(); + let trusted_source_chain = ascii::string(b"Axelar"); + let trusted_source_address = ascii::string(b"Trusted Address"); + let message_type = (123 as u256); + let message_id = ascii::string(b"message_id"); + let ctx = &mut tx_context::dummy(); + + let governance = governance::new_for_testing( + trusted_source_chain, + trusted_source_address, + message_type, + ctx, + ); + + let trusted_chains = vector[ + b"Ethereum", + b"Avalance", + b"Axelar", + ]; + let trusted_addresses = vector[ + b"ethereum address", + ITS_HUB_ROUTING_IDENTIFIER, + b"hub address", + ]; + let mut trusted_addresses_data = bcs::to_bytes(&its::trusted_addresses::new_for_testing(trusted_chains, trusted_addresses)); + trusted_addresses_data.push_back(0); + + let mut writer = abi::new_writer(2); + writer + .write_u256(MESSAGE_TYPE_SET_TRUSTED_ADDRESSES) + .write_bytes(trusted_addresses_data); + let payload = writer.into_bytes(); + + let approved_message = channel::new_approved_message( + trusted_source_chain, + message_id, + trusted_source_address, + its.channel().to_address(), + payload, + ); + + set_trusted_addresses(&mut its, &governance, approved_message); + + sui::test_utils::destroy(its); + sui::test_utils::destroy(governance); + } + + #[test] + #[expected_failure(abort_code = EUntrustedAddress)] + fun test_decode_approved_message_untrusted_address() { + let mut its = its::its::new_for_testing(); + let source_chain = ascii::string(b"Chain Name"); + let source_address = ascii::string(b"Untusted Address"); + let message_id = ascii::string(b"message_id"); + + let payload = b"payload"; + + let approved_message = channel::new_approved_message( + source_chain, + message_id, + source_address, + its.channel().to_address(), + payload, + ); + + decode_approved_message(&mut its, approved_message); + + sui::test_utils::destroy(its); + } + + #[test] + fun test_decode_approved_message_axelar_hub_sender() { + let mut its = its::its::new_for_testing(); + let source_chain = ascii::string(ITS_HUB_CHAIN_NAME); + let source_address = ascii::string(b"Address"); + let message_id = ascii::string(b"message_id"); + let origin_chain = ascii::string(b"Source Chain"); + let payload = b"payload"; + + let mut writer = abi::new_writer(3); + writer.write_u256(MESSAGE_TYPE_RECEIVE_FROM_HUB); + writer.write_bytes(origin_chain.into_bytes()); + writer.write_bytes(payload); + let payload = writer.into_bytes(); + + its.set_trusted_address(source_chain, source_address); + its.set_trusted_address(origin_chain, ascii::string(ITS_HUB_ROUTING_IDENTIFIER)); + + let approved_message = channel::new_approved_message( + source_chain, + message_id, + source_address, + its.channel().to_address(), + payload, + ); + + decode_approved_message(&mut its, approved_message); + + sui::test_utils::destroy(its); + } + + #[test] + #[expected_failure(abort_code = EUntrustedChain)] + fun test_decode_approved_message_sender_not_hub() { + let mut its = its::its::new_for_testing(); + let source_chain = ascii::string(b"Chain Name"); + let source_address = ascii::string(b"Address"); + let message_id = ascii::string(b"message_id"); + + let mut writer = abi::new_writer(3); + writer.write_u256(MESSAGE_TYPE_RECEIVE_FROM_HUB); + writer.write_bytes(b"Source Chain"); + writer.write_bytes(b"payload"); + let payload = writer.into_bytes(); + + let approved_message = channel::new_approved_message( + source_chain, + message_id, + source_address, + its.channel().to_address(), + payload, + ); + + decode_approved_message(&mut its, approved_message); + + sui::test_utils::destroy(its); + } + + #[test] + #[expected_failure(abort_code = EUntrustedChain)] + fun test_decode_approved_message_origin_not_hub_routed() { + let mut its = its::its::new_for_testing(); + let source_chain = ascii::string(ITS_HUB_CHAIN_NAME); + let source_address = ascii::string(b"Address"); + let message_id = ascii::string(b"message_id"); + let origin_chain = ascii::string(b"Source Chain"); + let origin_trusted_address = ascii::string(b"Origin Trusted Address"); + let payload = b"payload"; + + let mut writer = abi::new_writer(3); + writer.write_u256(MESSAGE_TYPE_RECEIVE_FROM_HUB); + writer.write_bytes(origin_chain.into_bytes()); + writer.write_bytes(payload); + let payload = writer.into_bytes(); + + its.set_trusted_address(source_chain, source_address); + its.set_trusted_address(origin_chain, origin_trusted_address); + + let approved_message = channel::new_approved_message( + source_chain, + message_id, + source_address, + its.channel().to_address(), + payload, + ); + + decode_approved_message(&mut its, approved_message); + + sui::test_utils::destroy(its); + } + + #[test] + fun test_send_payload_to_hub() { + let mut its = its::its::new_for_testing(); + let destination_chain = ascii::string(b"Destination Chain"); + let hub_address = ascii::string(b"Address"); + + let payload = b"payload"; + + its.set_trusted_address(ascii::string(ITS_HUB_CHAIN_NAME), hub_address); + its.set_trusted_address(destination_chain, ascii::string(ITS_HUB_ROUTING_IDENTIFIER)); + + send_payload(&mut its, destination_chain, payload); + + sui::test_utils::destroy(its); + } } diff --git a/move/its/sources/token_id.move b/move/its/sources/token_id.move index b8d21361..83a15ff4 100644 --- a/move/its/sources/token_id.move +++ b/move/its/sources/token_id.move @@ -72,6 +72,7 @@ module its::token_id { UnregisteredTokenId { id } } + // === Tests === #[test] fun test() { use std::string; diff --git a/move/its/sources/trusted_addresses.move b/move/its/sources/trusted_addresses.move new file mode 100644 index 00000000..d68b04cb --- /dev/null +++ b/move/its/sources/trusted_addresses.move @@ -0,0 +1,38 @@ +module its::trusted_addresses { + use sui::bcs::BCS; + + const EMalformedTrustedAddresses: u64 = 0; + + public struct TrustedAddresses has copy, drop { + trusted_chains: vector>, + trusted_addresses: vector>, + } + + public fun peel(bcs: &mut BCS): TrustedAddresses { + let trusted_chains = bcs.peel_vec_vec_u8(); + let trusted_addresses = bcs.peel_vec_vec_u8(); + + let length = trusted_chains.length(); + + assert!(length == trusted_addresses.length(), EMalformedTrustedAddresses); + + TrustedAddresses { + trusted_chains, + trusted_addresses, + } + } + + public fun destroy(self: TrustedAddresses): (vector>, vector>) { + let TrustedAddresses { trusted_chains, trusted_addresses } = self; + (trusted_chains, trusted_addresses) + } + + // === Tests === + #[test_only] + public fun new_for_testing(trusted_chains: vector>, trusted_addresses: vector>): TrustedAddresses { + TrustedAddresses { + trusted_chains, + trusted_addresses, + } + } +} diff --git a/move/its/sources/utils.move b/move/its/sources/utils.move index 2dd17a7d..db15d80c 100644 --- a/move/its/sources/utils.move +++ b/move/its/sources/utils.move @@ -83,6 +83,9 @@ module its::utils { res } + // ----- + // Tests + // ----- #[test] fun test_get_module_from_symbol() { let symbol = ascii::string(b"1(TheCool1234Coin) _ []!rdt"); diff --git a/move/its/tests/coin.move b/move/its/tests/coin.move new file mode 100644 index 00000000..f6c3bfcf --- /dev/null +++ b/move/its/tests/coin.move @@ -0,0 +1,52 @@ +#[test_only] +module its::coin { + use sui::url::{Url}; + use sui::coin::{Self, CoinMetadata, TreasuryCap}; + + public struct COIN has drop {} + + + // ----- + // Coin creation functions. + // ----- + + public fun create_treasury_and_metadata(symbol: vector, decimals: u8, ctx: &mut TxContext): (TreasuryCap, CoinMetadata) { + coin::create_currency( + COIN {}, + decimals, + symbol, + b"Name", + b"", + option::none(), + ctx + ) + } + + public fun create_treasury_and_metadata_custom(name: vector, symbol: vector, decimals: u8, url: Option, ctx: &mut TxContext): (TreasuryCap, CoinMetadata) { + coin::create_currency( + COIN {}, + decimals, + symbol, + name, + b"", + url, + ctx + ) + } + + public fun create_treasury(symbol: vector, decimals: u8, ctx: &mut TxContext): TreasuryCap { + let (treasury, metadata) = create_treasury_and_metadata(symbol, decimals, ctx); + + sui::test_utils::destroy(metadata); + + treasury + } + + public fun create_metadata(symbol: vector, decimals: u8, ctx: &mut TxContext): CoinMetadata { + let (treasury, metadata) = create_treasury_and_metadata(symbol, decimals, ctx); + + sui::test_utils::destroy(treasury); + + metadata + } +}