diff --git a/move/its/sources/discovery.move b/move/its/sources/discovery.move index 4fe27264..c4c5a86b 100644 --- a/move/its/sources/discovery.move +++ b/move/its/sources/discovery.move @@ -19,6 +19,8 @@ module its::discovery { 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; public fun get_interchain_transfer_info(payload: vector): (TokenId, address, u64, vector) { let mut reader = abi::new_reader(payload); @@ -66,9 +68,16 @@ module its::discovery { )); } - public fun get_call_info(self: &ITS, payload: vector): Transaction { + public fun get_call_info(self: &ITS, mut payload: vector): Transaction { let mut reader = abi::new_reader(payload); - let message_type = reader.read_u256(); + let mut message_type = reader.read_u256(); + + if (message_type == MESSAGE_TYPE_RECEIVE_FROM_HUB) { + reader.skip_slot(); + payload = reader.read_bytes(); + reader = abi::new_reader(payload); + message_type = reader.read_u256(); + }; if (message_type == MESSAGE_TYPE_INTERCHAIN_TRANSFER) { get_interchain_transfer_tx(self, &mut reader) diff --git a/move/its/sources/flow_limit.move b/move/its/sources/flow_limit.move index 1485ffe0..5a6bbe95 100644 --- a/move/its/sources/flow_limit.move +++ b/move/its/sources/flow_limit.move @@ -23,7 +23,7 @@ module its::flow_limit { fun update_epoch(self: &mut FlowLimit, clock: &Clock) { let epoch = clock.timestamp_ms() / EPOCH_TIME; - if(epoch > self.current_epoch) { + if (epoch > self.current_epoch) { self.current_epoch = epoch; self.flow_in = 0; self.flow_out = 0; diff --git a/move/its/sources/service.move b/move/its/sources/service.move index 97650906..b66bc012 100644 --- a/move/its/sources/service.move +++ b/move/its/sources/service.move @@ -27,6 +27,14 @@ module its::service { 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; + + // 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; @@ -43,6 +51,7 @@ module its::service { const ENonZeroTotalSupply: u64 = 7; const EUnregisteredCoinHasUrl: u64 = 8; const EMalformedTrustedAddresses: u64 = 9; + const EUntrustedChain: u64 = 10; public struct CoinRegistered has copy, drop { token_id: TokenId, @@ -273,20 +282,47 @@ module its::service { /// Decode an approved call and check that the source chain is trusted. fun decode_approved_message(self: &mut ITS, approved_message: ApprovedMessage): (String, vector) { let ( - source_chain, + mut source_chain, _, source_address, - payload + mut payload ) = self.channel_mut().consume_approved_message(approved_message); assert!(self.is_trusted_address(source_chain, source_address), EUntrustedAddress); + let mut reader = abi::new_reader(payload); + if (reader.read_u256() == MESSAGE_TYPE_RECEIVE_FROM_HUB) { + assert!(source_chain.into_bytes() == ITS_HUB_CHAIN_NAME, EUntrustedChain); + + source_chain = ascii::string(reader.read_bytes()); + payload = reader.read_bytes(); + + assert!(self.get_trusted_address(source_chain).into_bytes() == ITS_HUB_ROUTING_IDENTIFIER, EUntrustedChain); + } else { + assert!(source_chain.into_bytes() != ITS_HUB_CHAIN_NAME, EUntrustedChain); + }; + (source_chain, payload) } /// Send a payload to a destination chain. The destination chain needs to have a trusted address. - fun send_payload(self: &mut ITS, destination_chain: String, payload: vector) { - let destination_address = self.get_trusted_address(destination_chain); + fun send_payload(self: &mut ITS, mut destination_chain: String, mut payload: vector) { + let mut destination_address = self.get_trusted_address(destination_chain); + + // Prevent sending directly to the ITS Hub chain. This is not supported yet, so fail early to prevent the user from having their funds stuck. + assert!(destination_chain.into_bytes() != ITS_HUB_CHAIN_NAME, EUntrustedChain); + + // Check whether the ITS call should be routed via ITS hub for this destination chain + if (destination_address.into_bytes() == ITS_HUB_ROUTING_IDENTIFIER) { + let mut writer = abi::new_writer(3); + writer.write_u256(MESSAGE_TYPE_SEND_TO_HUB); + writer.write_bytes(destination_chain.into_bytes()); + writer.write_bytes(payload); + payload = writer.into_bytes(); + destination_chain = ascii::string(ITS_HUB_CHAIN_NAME); + destination_address = self.get_trusted_address(destination_chain); + }; + gateway::call_contract(self.channel_mut(), destination_chain, destination_address, payload); } } diff --git a/move/squid/sources/squid/coin_bag.move b/move/squid/sources/squid/coin_bag.move index 014ba484..393e0fa3 100644 --- a/move/squid/sources/squid/coin_bag.move +++ b/move/squid/sources/squid/coin_bag.move @@ -19,7 +19,7 @@ module squid::coin_bag { public(package) fun store_balance(self: &mut CoinBag, balance: Balance) { let key = get_balance_key(); - if(self.bag.contains(key)) { + if (self.bag.contains(key)) { self.bag.borrow_mut>(key).join( balance, ); @@ -34,7 +34,7 @@ module squid::coin_bag { public(package) fun get_balance(self: &mut CoinBag): Option> { let key = get_balance_key(); - if(self.bag.contains(key)) { + if (self.bag.contains(key)) { option::some(self.bag.remove>(key)) } else { option::none>() @@ -45,7 +45,7 @@ module squid::coin_bag { public(package) fun get_balance_amount(self: &CoinBag): u64 { let key = get_balance_key(); - if(self.bag.contains(key)) { + if (self.bag.contains(key)) { self.bag.borrow>(key).value() } else { 0 @@ -56,7 +56,7 @@ module squid::coin_bag { public(package) fun store_estimate(self: &mut CoinBag, estimate: u64) { let key = get_estimate_key(); - if(self.bag.contains(key)) { + if (self.bag.contains(key)) { let previous = self.bag.borrow_mut(key); *previous = *previous + estimate; } else { @@ -70,7 +70,7 @@ module squid::coin_bag { public(package) fun get_estimate(self: &mut CoinBag): u64 { let key = get_estimate_key(); - if(self.bag.contains(key)) { + if (self.bag.contains(key)) { self.bag.remove(key) } else { 0 @@ -80,7 +80,7 @@ module squid::coin_bag { public(package) fun get_estimate_amount(self: &CoinBag): u64 { let key = get_estimate_key(); - if(self.bag.contains(key)) { + if (self.bag.contains(key)) { *self.bag.borrow(key) } else { 0 diff --git a/move/squid/sources/squid/deepbook_v2.move b/move/squid/sources/squid/deepbook_v2.move index 71488b1e..a842c83f 100644 --- a/move/squid/sources/squid/deepbook_v2.move +++ b/move/squid/sources/squid/deepbook_v2.move @@ -190,7 +190,7 @@ module squid::deepbook_v2 { ); amount_left = amount_left - used; output = output + max_out; - if(amount_left < lot_size) break; + if (amount_left < lot_size) break; }; (amount_left, output) @@ -226,7 +226,7 @@ module squid::deepbook_v2 { public fun estimate(self: &mut SwapInfo, pool: &Pool, clock: &Clock) { let data = self.get_data_estimating(); - if(data.length() == 0) return; + if (data.length() == 0) return; let mut bcs = bcs::new(data); @@ -249,7 +249,7 @@ module squid::deepbook_v2 { let lot_size = bcs.peel_u64(); let should_sweep = bcs.peel_bool(); - if(has_base) { + if (has_base) { let (amount_left, output) = predict_base_for_quote( pool, // these are run in sequence before anything is done with balances, so `get_estimate` is the correct function to use. @@ -257,11 +257,11 @@ module squid::deepbook_v2 { lot_size, clock, ); - if(min_output > output) { + if (min_output > output) { self.skip_swap(); return }; - if(!should_sweep) self.coin_bag().store_estimate(amount_left); + if (!should_sweep) self.coin_bag().store_estimate(amount_left); self.coin_bag().store_estimate(output); } else { let (amount_left, output) = predict_quote_for_base( @@ -270,18 +270,18 @@ module squid::deepbook_v2 { lot_size, clock, ); - if(min_output > output) { + if (min_output > output) { self.skip_swap(); return }; - if(!should_sweep) self.coin_bag().store_estimate(amount_left); + if (!should_sweep) self.coin_bag().store_estimate(amount_left); self.coin_bag().store_estimate(output); } } public fun swap(self: &mut SwapInfo, pool: &mut Pool, squid: &mut Squid, clock: &Clock, ctx: &mut TxContext) { let data = self.get_data_swapping(); - if(data.length() == 0) return; + if (data.length() == 0) return; let swap_data = peel_swap_data(data); assert!(swap_data.swap_type == SWAP_TYPE, EWrongSwapType); @@ -298,11 +298,11 @@ module squid::deepbook_v2 { EWrongCoinType, ); - if(swap_data.has_base) { + if (swap_data.has_base) { let mut base_balance = self.coin_bag().get_balance().destroy_some(); let leftover = base_balance.value() % swap_data.lot_size; - if(leftover > 0) { - if(swap_data.should_sweep) { + if (leftover > 0) { + if (swap_data.should_sweep) { squid.coin_bag().store_balance( base_balance.split(leftover) ); @@ -331,7 +331,7 @@ module squid::deepbook_v2 { ); assert!(swap_data.min_output <= base_coin.value(), ENotEnoughOutput); self.coin_bag().store_balance(base_coin.into_balance()); - if(swap_data.should_sweep) { + if (swap_data.should_sweep) { squid.coin_bag().store_balance(quote_coin.into_balance()); } else { self.coin_bag().store_balance(quote_coin.into_balance()); diff --git a/move/squid/sources/squid/swap_info.move b/move/squid/sources/squid/swap_info.move index 274a09a5..cd74ed10 100644 --- a/move/squid/sources/squid/swap_info.move +++ b/move/squid/sources/squid/swap_info.move @@ -37,14 +37,14 @@ module squid::swap_info { public(package) fun get_data_swapping(self: &mut SwapInfo): vector { let index = self.swap_index; - if(index == 0 && self.status == ESTIMATING) { + if (index == 0 && self.status == ESTIMATING) { assert!(self.estimate_index == self.swap_data.length(), ENotDoneEstimating); self.status = SWAPPING; }; assert!(index < self.swap_data.length(), EOutOfSwaps); self.swap_index = index + 1; - if(self.status == SKIP_SWAP) { + if (self.status == SKIP_SWAP) { vector[] } else { assert!(self.status == SWAPPING, ENotSwapping); diff --git a/move/squid/sources/squid/transfers.move b/move/squid/sources/squid/transfers.move index d8afccf1..f9c3ea17 100644 --- a/move/squid/sources/squid/transfers.move +++ b/move/squid/sources/squid/transfers.move @@ -99,7 +99,7 @@ module squid::transfers { ); let option = swap_info.coin_bag().get_balance(); - if(option.is_none()) { + if (option.is_none()) { option.destroy_none(); return }; @@ -120,7 +120,7 @@ module squid::transfers { ); let option = swap_info.coin_bag().get_balance(); - if(option.is_none()) { + if (option.is_none()) { option.destroy_none(); return };