diff --git a/move/axelar_gateway/Move.lock b/move/axelar_gateway/Move.lock index 1c88f022..370d6384 100644 --- a/move/axelar_gateway/Move.lock +++ b/move/axelar_gateway/Move.lock @@ -2,7 +2,7 @@ [move] version = 2 -manifest_digest = "B3F1114AF6420DAFC5501B8A333236271B0467A5D00AF701F02D79AEC565E6E5" +manifest_digest = "1EB54C26C3FA638760DAA54E631C0B13A09FFC472C1CAA730C9A7879E463B455" deps_digest = "F8BBB0CCB2491CA29A3DF03D6F92277A4F3574266507ACD77214D37ECA3F3082" dependencies = [ { name = "Sui" }, diff --git a/move/axelar_gateway/Move.toml b/move/axelar_gateway/Move.toml index 758ef53b..c02e7735 100644 --- a/move/axelar_gateway/Move.toml +++ b/move/axelar_gateway/Move.toml @@ -1,12 +1,11 @@ [package] name = "AxelarGateway" version = "0.1.0" -published-at = "0x571841ce66dce70e51a123299ed140d6509566c97eb905331b5f28732c707c56" edition = "2024.beta" [dependencies] Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "mainnet-v1.25.3" } [addresses] -axelar_gateway = "0x100" +axelar_gateway = "0xcd391ade5ab55a218f8988df8f3aa38ec019651c217244f7f689616c87d8c289" clock = "0x6" diff --git a/move/squid/sources/squid/deepbook_v2.move b/move/squid/sources/squid/deepbook_v2.move index 99604f14..55a3868e 100644 --- a/move/squid/sources/squid/deepbook_v2.move +++ b/move/squid/sources/squid/deepbook_v2.move @@ -1,6 +1,6 @@ module squid::deepbook_v2 { use std::type_name; - use std::ascii; + use std::ascii::{Self, String}; use sui::coin::{Self, Coin}; use sui::clock::Clock; @@ -25,7 +25,32 @@ module squid::deepbook_v2 { const EWrongCoinType: u64 = 2; const ENotEnoughOutput: u64 = 3; - public fun swap_base(pool: &mut Pool, coin: Coin, clock: &Clock, ctx: &mut TxContext): (Coin, Coin) { + public struct DeepbookV2SwapData has drop{ + swap_type: u8, + pool_id: address, + has_base: bool, + min_output: u64, + base_type: String, + quote_type: String, + lot_size: u64, + should_sweep: bool, + } + + fun new(data: vector): DeepbookV2SwapData { + let mut bcs = bcs::new(data); + DeepbookV2SwapData { + swap_type: bcs.peel_u8(), + pool_id: bcs.peel_address(), + has_base: bcs.peel_bool(), + min_output: bcs.peel_u64(), + base_type: ascii::string(bcs.peel_vec_u8()), + quote_type: ascii::string(bcs.peel_vec_u8()), + lot_size: bcs.peel_u64(), + should_sweep: bcs.peel_bool(), + } + } + + fun swap_base(pool: &mut Pool, coin: Coin, clock: &Clock, ctx: &mut TxContext): (Coin, Coin) { let account = clob::create_account(ctx); let (base_coin, quote_coin, _) = pool.swap_exact_base_for_quote( 0, @@ -40,7 +65,7 @@ module squid::deepbook_v2 { (base_coin, quote_coin) } - public fun swap_quote(pool: &mut Pool, coin: Coin, clock: &Clock, ctx: &mut TxContext): (Coin, Coin) { + fun swap_quote(pool: &mut Pool, coin: Coin, clock: &Clock, ctx: &mut TxContext): (Coin, Coin) { let account = clob::create_account(ctx); let (base_coin, quote_coin, _) = pool.swap_exact_quote_for_base( 0, @@ -149,7 +174,7 @@ module squid::deepbook_v2 { (filled_quote_quantity, filled_base_quantity) } - public fun predict_base_for_quote(pool: &Pool, amount: u64, lot_size: u64, clock: &Clock): (u64, u64) { + fun predict_base_for_quote(pool: &Pool, amount: u64, lot_size: u64, clock: &Clock): (u64, u64) { let max_price = (1u128 << 64 - 1 as u64); let (prices, depths) = clob::get_level2_book_status_bid_side(pool, 0, max_price, clock); let mut amount_left = amount; @@ -171,7 +196,7 @@ module squid::deepbook_v2 { } - public fun predict_quote_for_base(pool: &Pool, amount: u64, lot_size: u64, clock: &Clock): (u64, u64) { + fun predict_quote_for_base(pool: &Pool, amount: u64, lot_size: u64, clock: &Clock): (u64, u64) { let max_price = (1u128 << 64 - 1 as u64); let (prices, depths) = clob::get_level2_book_status_ask_side(pool, 0, max_price, clock); @@ -257,32 +282,27 @@ module squid::deepbook_v2 { 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; - let mut bcs = bcs::new(data); + let swap_data = new(data); - assert!(bcs.peel_u8() == SWAP_TYPE, EWrongSwapType); + assert!(swap_data.swap_type == SWAP_TYPE, EWrongSwapType); - assert!(bcs.peel_address() == object::id_address(pool), EWrongPool); - - let has_base = bcs.peel_bool(); - let min_output = bcs.peel_u64(); + assert!(swap_data.pool_id == object::id_address(pool), EWrongPool); assert!( - &bcs.peel_vec_u8() == &type_name::get().into_string().into_bytes(), + &swap_data.base_type == &type_name::get().into_string(), EWrongCoinType, ); assert!( - &bcs.peel_vec_u8() == &type_name::get().into_string().into_bytes(), + &swap_data.quote_type == &type_name::get().into_string(), EWrongCoinType, ); - let lot_size = bcs.peel_u64(); - let should_sweep = bcs.peel_bool(); - if(has_base) { + if(swap_data.has_base) { let mut base_balance = self.coin_bag().get_balance().destroy_some(); - let leftover = base_balance.value() % lot_size; + let leftover = base_balance.value() % swap_data.lot_size; if(leftover > 0) { - if(should_sweep) { + if(swap_data.should_sweep) { squid.coin_bag().store_balance( base_balance.split(leftover) ); @@ -298,7 +318,7 @@ module squid::deepbook_v2 { clock, ctx, ); - assert!(min_output <= quote_coin.value(), ENotEnoughOutput); + assert!(swap_data.min_output <= quote_coin.value(), ENotEnoughOutput); base_coin.destroy_zero(); self.coin_bag().store_balance(quote_coin.into_balance()); } else { @@ -309,9 +329,9 @@ module squid::deepbook_v2 { clock, ctx, ); - assert!(min_output <= base_coin.value(), ENotEnoughOutput); + assert!(swap_data.min_output <= base_coin.value(), ENotEnoughOutput); self.coin_bag().store_balance(base_coin.into_balance()); - if(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/transfers.move b/move/squid/sources/squid/transfers.move index a362d38b..10ce3711 100644 --- a/move/squid/sources/squid/transfers.move +++ b/move/squid/sources/squid/transfers.move @@ -1,6 +1,6 @@ module squid::transfers { use std::type_name; - use std::ascii; + use std::ascii::{Self, String}; use sui::bcs::{Self, BCS}; use sui::coin; @@ -9,7 +9,7 @@ module squid::transfers { use its::service; use its::its::ITS; - use its::token_id; + use its::token_id::{Self, TokenId}; use squid::swap_info::{SwapInfo}; @@ -19,15 +19,51 @@ module squid::transfers { const EWrongSwapType: u64 = 0; const EWrongCoinType: u64 = 1; + public struct SuiTransferSwapData has drop { + swap_type: u8, + coin_type: String, + recipient: address, + } + + public struct ItsTransferSwapData has drop { + swap_type: u8, + coin_type: String, + token_id: TokenId, + destination_chain: String, + destination_address: vector, + metadata: vector, + } + + fun new_sui_transfer_swap_data(data: vector): SuiTransferSwapData { + let mut bcs = bcs::new(data); + SuiTransferSwapData { + swap_type: bcs.peel_u8(), + coin_type: ascii::string(bcs.peel_vec_u8()), + recipient: bcs.peel_address(), + } + } + + fun new_its_transfer_swap_data(data: vector): ItsTransferSwapData { + let mut bcs = bcs::new(data); + ItsTransferSwapData { + swap_type: bcs.peel_u8(), + coin_type: ascii::string(bcs.peel_vec_u8()), + token_id: token_id::from_address(bcs.peel_address()), + destination_chain: ascii::string(bcs.peel_vec_u8()), + destination_address: bcs.peel_vec_u8(), + metadata: bcs.peel_vec_u8(), + } + } + public fun sui_estimate(swap_info: &mut SwapInfo) { let data = swap_info.get_data_estimating(); if (data.length() == 0) return; - let mut bcs = bcs::new(data); + let swap_data = new_sui_transfer_swap_data(data); - assert!(bcs.peel_u8() == SWAP_TYPE_SUI_TRANSFER, EWrongSwapType); + assert!(swap_data.swap_type == SWAP_TYPE_SUI_TRANSFER, EWrongSwapType); assert!( - &bcs.peel_vec_u8() == &type_name::get().into_string().into_bytes(), + &swap_data.coin_type == &type_name::get().into_string(), EWrongCoinType, ); @@ -37,12 +73,12 @@ module squid::transfers { public fun its_estimate(swap_info: &mut SwapInfo) { let data = swap_info.get_data_estimating(); if (data.length() == 0) return; - let mut bcs = bcs::new(data); + let swap_data = new_its_transfer_swap_data(data); - assert!(bcs.peel_u8() == SWAP_TYPE_ITS_TRANSFER, EWrongSwapType); + assert!(swap_data.swap_type == SWAP_TYPE_ITS_TRANSFER, EWrongSwapType); assert!( - &bcs.peel_vec_u8() == &type_name::get().into_string().into_bytes(), + &swap_data.coin_type == &type_name::get().into_string(), EWrongCoinType, ); @@ -52,12 +88,12 @@ module squid::transfers { public fun sui_transfer(swap_info: &mut SwapInfo, ctx: &mut TxContext) { let data = swap_info.get_data_swapping(); if (data.length() == 0) return; - let mut bcs = bcs::new(data); + let swap_data = new_sui_transfer_swap_data(data); - assert!(bcs.peel_u8() == SWAP_TYPE_SUI_TRANSFER, EWrongSwapType); + assert!(swap_data.swap_type == SWAP_TYPE_SUI_TRANSFER, EWrongSwapType); assert!( - &bcs.peel_vec_u8() == &type_name::get().into_string().into_bytes(), + &swap_data.coin_type == &type_name::get().into_string(), EWrongCoinType, ); @@ -66,19 +102,19 @@ module squid::transfers { option.destroy_none(); return }; - let address = bcs.peel_address(); - transfer::public_transfer(coin::from_balance(option.destroy_some(), ctx), address); + + transfer::public_transfer(coin::from_balance(option.destroy_some(), ctx), swap_data.recipient); } public fun its_transfer(swap_info: &mut SwapInfo, its: &mut ITS, ctx: &mut TxContext) { let data = swap_info.get_data_swapping(); if (data.length() == 0) return; - let mut bcs = bcs::new(data); + let swap_data = new_its_transfer_swap_data(data); - assert!(bcs.peel_u8() == SWAP_TYPE_SUI_TRANSFER, EWrongSwapType); + assert!(swap_data.swap_type == SWAP_TYPE_SUI_TRANSFER, EWrongSwapType); assert!( - &bcs.peel_vec_u8() == &type_name::get().into_string().into_bytes(), + &swap_data.coin_type == &type_name::get().into_string(), EWrongCoinType, ); @@ -87,17 +123,14 @@ module squid::transfers { option.destroy_none(); return }; - let token_id = token_id::from_address(bcs.peel_address()); - let destination_chain = ascii::string(bcs.peel_vec_u8()); - let destination_address = bcs.peel_vec_u8(); - let metadata = bcs.peel_vec_u8(); + service::interchain_transfer( its, - token_id, + swap_data.token_id, coin::from_balance(option.destroy_some(), ctx), - destination_chain, - destination_address, - metadata, + swap_data.destination_chain, + swap_data.destination_address, + swap_data.metadata, ctx, ); } diff --git a/scripts/bcs.js b/scripts/bcs.js new file mode 100644 index 00000000..a3148e5a --- /dev/null +++ b/scripts/bcs.js @@ -0,0 +1,122 @@ +const { bcs } = require('@mysten/sui.js/bcs'); + +function getAxelarStructs() { + const Bytes32 = bcs.struct('Bytes32', { + bytes: bcs.Address, + }); + + const Message = bcs.struct('Message', { + source_chain: bcs.String, + message_id: bcs.String, + source_address: bcs.String, + destination_id: bcs.Address, + payload_hash: Bytes32, + }); + + const WeightedSigner = bcs.struct('WeightedSigner', { + pubkey: bcs.vector(bcs.U8), + weight: bcs.U128, + }); + + const WeightedSigners = bcs.struct('WeightedSigners', { + signers: bcs.vector(WeightedSigner), + threshold: bcs.U128, + nonce: Bytes32, + }); + + const Signature = bcs.struct('Signature', { + bytes: bcs.vector(bcs.U8), + }); + + const Proof = bcs.struct('Proof', { + signers: WeightedSigners, + signatures: bcs.vector(Signature), + }); + + const MessageToSign = bcs.struct('MessageToSign', { + domain_separator: Bytes32, + signers_hash: Bytes32, + data_hash: Bytes32, + }); + + const Function = bcs.struct('Function', { + package_id: bcs.Address, + module_name: bcs.String, + name: bcs.String, + }); + + /// Arguments are prefixed with: + /// - 0 for objects followed by exactly 32 bytes that cointain the object id + /// - 1 for pures followed by the bcs encoded form of the pure + /// - 2 for the call contract object, followed by nothing (to be passed into the target function) + /// - 3 for the payload of the contract call (to be passed into the intermediate function) + /// - 4 for an argument returned from a previous move call, followed by a u8 specified which call to get the return of (0 for the first transaction AFTER the one that gets ApprovedMessage out), and then another u8 specifying which argument to input. + const MoveCall = bcs.struct('MoveCall', { + function: Function, + arguments: bcs.vector(bcs.vector(bcs.U8)), + type_arguments: bcs.vector(bcs.String), + }); + + const Transaction = bcs.struct('Transaction', { + is_final: bcs.Bool, + move_calls: bcs.vector(MoveCall), + }); + + const EncodedMessage = bcs.struct('EncodedMessage', { + message_type: bcs.U8, + data: bcs.vector(bcs.U8), + }); + + return { + Bytes32, + Message, + WeightedSigner, + WeightedSigners, + Signature, + Proof, + MessageToSign, + Function, + MoveCall, + Transaction, + EncodedMessage, + }; +} + +function getSquidStructs() { + const DeepbookV2SwapData = bcs.struct('DeepbookV2SwapData', { + swap_type: bcs.U8, + pool_id: bcs.Address, + has_base: bcs.Bool, + min_output: bcs.U64, + base_type: bcs.String, + quote_type: bcs.String, + lot_size: bcs.U64, + should_sweep: bcs.Bool, + }); + + const SuiTransferSwapData = bcs.struct('SuiTransferSwapData', { + swap_type: bcs.U8, + coin_type: bcs.String, + recipient: bcs.Address, + }); + + const ItsTransferSwapData = bcs.struct('ItsTransferSwapData', { + swap_type: bcs.U8, + coin_type: bcs.String, + token_id: bcs.Address, + destination_chain: bcs.String, + destination_address: bcs.vector(bcs.U8), + metadata: bcs.vector(bcs.U8), + }); + + return { + DeepbookV2SwapData, + SuiTransferSwapData, + ItsTransferSwapData, + }; +} + +module.exports = { + axelarStructs: getAxelarStructs(), + squidStructs: getSquidStructs(), +}; diff --git a/scripts/publish-package.js b/scripts/publish-package.js index 767d0e28..9c5e7046 100644 --- a/scripts/publish-package.js +++ b/scripts/publish-package.js @@ -49,7 +49,7 @@ async function publishPackage(packageName, client, keypair) { }); if (publishTxn.effects?.status.status !== 'success') throw new Error('Publish Tx failed'); - const packageId = (publishTxn.objectChanges?.filter((a) => a.type === 'published') ?? [])[0].packageId; + const packageId = (publishTxn.objectChanges?.find((a) => a.type === 'published') ?? []).packageId; console.info(`Published package ${packageId} from address ${address}}`); @@ -71,6 +71,7 @@ if (require.main === module) { const privKey = Buffer.from(process.env.SUI_PRIVATE_KEY, 'hex'); const keypair = Ed25519Keypair.fromSecretKey(privKey); const address = keypair.getPublicKey().toSuiAddress(); + console.log(address); // create a new SuiClient object pointing to the network you want to use const client = new SuiClient({ url: env.url }); diff --git a/scripts/tx-builder.js b/scripts/tx-builder.js index 0f830a60..6ecac2b3 100644 --- a/scripts/tx-builder.js +++ b/scripts/tx-builder.js @@ -188,6 +188,7 @@ class TxBuilder { ...options, }, }); + return result; }