@@ -5709,4 +5709,388 @@ describe('V2 Wallet:', function () {
57095709 } ) ;
57105710 } ) ;
57115711 } ) ;
5712+
5713+ describe ( 'Aptos Custom transaction Flow' , function ( ) {
5714+ const sandbox = sinon . createSandbox ( ) ;
5715+
5716+ // Set up test data and wallets
5717+ const tapt = bitgo . coin ( 'tapt' ) ;
5718+ const aptosWalletData = {
5719+ id : '5b34252f1bf349930e34020a00000001' ,
5720+ coin : 'tapt' ,
5721+ keys : [
5722+ '598f606cd8fc24710d2ebad89dce86c3' ,
5723+ '598f606cc8e43aef09fcb785221d9dd3' ,
5724+ '5935d59cf660764331bafcade1855fd8' ,
5725+ ] ,
5726+ coinSpecific : { } ,
5727+ multisigType : 'tss' ,
5728+ } ;
5729+
5730+ const tssAptosWallet = new Wallet ( bitgo , tapt , aptosWalletData ) ;
5731+ const custodialTssAptosWallet = new Wallet ( bitgo , tapt , {
5732+ ...aptosWalletData ,
5733+ type : 'custodial' ,
5734+ } ) ;
5735+
5736+ const reqId = new RequestTracer ( ) ;
5737+
5738+ const aptosTxRequest : TxRequest = {
5739+ txRequestId : 'aptos-tx-id' ,
5740+ transactions : [ ] ,
5741+ intent : {
5742+ intentType : 'customTx' ,
5743+ } ,
5744+ date : new Date ( ) . toISOString ( ) ,
5745+ latest : true ,
5746+ state : 'pendingUserSignature' ,
5747+ userId : 'userId' ,
5748+ walletType : 'hot' ,
5749+ policiesChecked : false ,
5750+ version : 1 ,
5751+ walletId : 'walletId' ,
5752+ unsignedTxs : [
5753+ {
5754+ serializedTxHex : 'aptos123abcd' ,
5755+ signableHex : 'aptosdeadbeef' ,
5756+ feeInfo : {
5757+ fee : 1000 ,
5758+ feeString : '1000' ,
5759+ } ,
5760+ derivationPath : 'm/0' ,
5761+ } ,
5762+ ] ,
5763+ } ;
5764+
5765+ afterEach ( function ( ) {
5766+ sandbox . verifyAndRestore ( ) ;
5767+ } ) ;
5768+
5769+ const testCustomSmartContractCall = {
5770+ moduleName : '0x1::coin' ,
5771+ functionName : 'transfer' ,
5772+ typeArguments : [ '0x1::aptos_coin::AptosCoin' ] ,
5773+ functionArguments : [ '0x742d35Cc6634C0532925a3b8D400E65AAD801c0b' , '1000000' ] ,
5774+ } ;
5775+
5776+ const testAptosRecipients = [
5777+ {
5778+ address : '0x742d35Cc6634C0532925a3b8D400E65AAD801c0b' ,
5779+ amount : '1000000' ,
5780+ } ,
5781+ ] ;
5782+
5783+ describe ( 'prebuildTransaction with aptosCustomTransaction type' , function ( ) {
5784+ it ( 'should call prebuildTxWithIntent with correct parameters for custom smart contract call' , async function ( ) {
5785+ const prebuildTxWithIntent = sandbox . stub ( TssUtils . prototype , 'prebuildTxWithIntent' ) ;
5786+ prebuildTxWithIntent . resolves ( aptosTxRequest ) ;
5787+ prebuildTxWithIntent . calledOnceWithExactly ( {
5788+ reqId,
5789+ intentType : 'customTx' ,
5790+ aptosCustomTransactionParams : testCustomSmartContractCall ,
5791+ recipients : testAptosRecipients ,
5792+ } ) ;
5793+
5794+ const txPrebuild = await tssAptosWallet . prebuildTransaction ( {
5795+ reqId,
5796+ type : 'customTx' ,
5797+ aptosCustomTransactionParams : testCustomSmartContractCall ,
5798+ recipients : testAptosRecipients ,
5799+ } ) ;
5800+
5801+ txPrebuild . should . deepEqual ( {
5802+ walletId : tssAptosWallet . id ( ) ,
5803+ wallet : tssAptosWallet ,
5804+ txRequestId : 'aptos-tx-id' ,
5805+ txHex : 'aptos123abcd' ,
5806+ buildParams : {
5807+ type : 'customTx' ,
5808+ aptosCustomTransactionParams : testCustomSmartContractCall ,
5809+ recipients : testAptosRecipients ,
5810+ } ,
5811+ feeInfo : {
5812+ fee : 1000 ,
5813+ feeString : '1000' ,
5814+ } ,
5815+ } ) ;
5816+ } ) ;
5817+
5818+ it ( 'should handle aptosCustomTransaction with empty recipients' , async function ( ) {
5819+ const prebuildTxWithIntent = sandbox . stub ( TssUtils . prototype , 'prebuildTxWithIntent' ) ;
5820+ prebuildTxWithIntent . resolves ( aptosTxRequest ) ;
5821+ prebuildTxWithIntent . calledOnceWithExactly ( {
5822+ reqId,
5823+ intentType : 'customTx' ,
5824+ aptosCustomTransactionParams : testCustomSmartContractCall ,
5825+ recipients : [ ] ,
5826+ } ) ;
5827+
5828+ const txPrebuild = await tssAptosWallet . prebuildTransaction ( {
5829+ reqId,
5830+ type : 'customTx' ,
5831+ aptosCustomTransactionParams : testCustomSmartContractCall ,
5832+ } ) ;
5833+
5834+ txPrebuild . should . deepEqual ( {
5835+ walletId : tssAptosWallet . id ( ) ,
5836+ wallet : tssAptosWallet ,
5837+ txRequestId : 'aptos-tx-id' ,
5838+ txHex : 'aptos123abcd' ,
5839+ buildParams : {
5840+ type : 'customTx' ,
5841+ aptosCustomTransactionParams : testCustomSmartContractCall ,
5842+ } ,
5843+ feeInfo : {
5844+ fee : 1000 ,
5845+ feeString : '1000' ,
5846+ } ,
5847+ } ) ;
5848+ } ) ;
5849+
5850+ it ( 'should handle custom smart contract call with type arguments' , async function ( ) {
5851+ const accountCreationCall = {
5852+ moduleName : '0x1::aptos_account' ,
5853+ functionName : 'create_account' ,
5854+ typeArguments : [ ] ,
5855+ functionArguments : [ '0x123def456abc789fed012345abcdef67890' ] ,
5856+ } ;
5857+
5858+ const prebuildTxWithIntent = sandbox . stub ( TssUtils . prototype , 'prebuildTxWithIntent' ) ;
5859+ prebuildTxWithIntent . resolves ( aptosTxRequest ) ;
5860+ prebuildTxWithIntent . calledOnceWithExactly ( {
5861+ reqId,
5862+ intentType : 'customTx' ,
5863+ aptosCustomTransactionParams : accountCreationCall ,
5864+ recipients : testAptosRecipients ,
5865+ } ) ;
5866+
5867+ const txPrebuild = await tssAptosWallet . prebuildTransaction ( {
5868+ reqId,
5869+ type : 'customTx' ,
5870+ aptosCustomTransactionParams : accountCreationCall ,
5871+ recipients : testAptosRecipients ,
5872+ } ) ;
5873+
5874+ txPrebuild . should . deepEqual ( {
5875+ walletId : tssAptosWallet . id ( ) ,
5876+ wallet : tssAptosWallet ,
5877+ txRequestId : 'aptos-tx-id' ,
5878+ txHex : 'aptos123abcd' ,
5879+ buildParams : {
5880+ type : 'customTx' ,
5881+ aptosCustomTransactionParams : accountCreationCall ,
5882+ recipients : testAptosRecipients ,
5883+ } ,
5884+ feeInfo : {
5885+ fee : 1000 ,
5886+ feeString : '1000' ,
5887+ } ,
5888+ } ) ;
5889+ } ) ;
5890+
5891+ it ( 'should handle aptosCustomTransaction with ABI information' , async function ( ) {
5892+ const customCallWithAbi = {
5893+ ...testCustomSmartContractCall ,
5894+ abi : {
5895+ name : 'transfer' ,
5896+ visibility : 'public' ,
5897+ is_entry : true ,
5898+ generic_type_params : [ ] ,
5899+ params : [ 'address' , 'u64' ] ,
5900+ return : [ ] ,
5901+ } ,
5902+ } ;
5903+
5904+ const prebuildTxWithIntent = sandbox . stub ( TssUtils . prototype , 'prebuildTxWithIntent' ) ;
5905+ prebuildTxWithIntent . resolves ( aptosTxRequest ) ;
5906+ prebuildTxWithIntent . calledOnceWithExactly ( {
5907+ reqId,
5908+ intentType : 'customTx' ,
5909+ aptosCustomTransactionParams : customCallWithAbi ,
5910+ recipients : testAptosRecipients ,
5911+ } ) ;
5912+
5913+ const txPrebuild = await tssAptosWallet . prebuildTransaction ( {
5914+ reqId,
5915+ type : 'customTx' ,
5916+ aptosCustomTransactionParams : customCallWithAbi ,
5917+ recipients : testAptosRecipients ,
5918+ } ) ;
5919+
5920+ txPrebuild . should . deepEqual ( {
5921+ walletId : tssAptosWallet . id ( ) ,
5922+ wallet : tssAptosWallet ,
5923+ txRequestId : 'aptos-tx-id' ,
5924+ txHex : 'aptos123abcd' ,
5925+ buildParams : {
5926+ type : 'customTx' ,
5927+ aptosCustomTransactionParams : customCallWithAbi ,
5928+ recipients : testAptosRecipients ,
5929+ } ,
5930+ feeInfo : {
5931+ fee : 1000 ,
5932+ feeString : '1000' ,
5933+ } ,
5934+ } ) ;
5935+ } ) ;
5936+
5937+ it ( 'should handle aptosCustomTransaction with pending approval ID' , async function ( ) {
5938+ const prebuildTxWithIntent = sandbox . stub ( TssUtils . prototype , 'prebuildTxWithIntent' ) ;
5939+ prebuildTxWithIntent . resolves ( {
5940+ ...aptosTxRequest ,
5941+ state : 'pendingApproval' ,
5942+ pendingApprovalId : 'aptos-approval-id' ,
5943+ } ) ;
5944+ prebuildTxWithIntent . calledOnceWithExactly ( {
5945+ reqId,
5946+ intentType : 'customTx' ,
5947+ aptosCustomTransactionParams : testCustomSmartContractCall ,
5948+ recipients : testAptosRecipients ,
5949+ } ) ;
5950+
5951+ const txPrebuild = await custodialTssAptosWallet . prebuildTransaction ( {
5952+ reqId,
5953+ type : 'customTx' ,
5954+ aptosCustomTransactionParams : testCustomSmartContractCall ,
5955+ recipients : testAptosRecipients ,
5956+ } ) ;
5957+
5958+ txPrebuild . should . deepEqual ( {
5959+ walletId : custodialTssAptosWallet . id ( ) ,
5960+ wallet : custodialTssAptosWallet ,
5961+ txRequestId : 'aptos-tx-id' ,
5962+ txHex : 'aptos123abcd' ,
5963+ pendingApprovalId : 'aptos-approval-id' ,
5964+ buildParams : {
5965+ type : 'customTx' ,
5966+ aptosCustomTransactionParams : testCustomSmartContractCall ,
5967+ recipients : testAptosRecipients ,
5968+ } ,
5969+ feeInfo : {
5970+ fee : 1000 ,
5971+ feeString : '1000' ,
5972+ } ,
5973+ } ) ;
5974+ } ) ;
5975+
5976+ it ( 'should throw error for missing aptosCustomTransactionParams' , async function ( ) {
5977+ await tssAptosWallet
5978+ . prebuildTransaction ( {
5979+ reqId,
5980+ type : 'customTx' ,
5981+ recipients : testAptosRecipients ,
5982+ } )
5983+ . should . be . rejectedWith ( `'aptosCustomTransactionParams' is a required parameter for customTx intent` ) ;
5984+ } ) ;
5985+
5986+ it ( 'should support aptosCustomTransaction for cold wallets' , async function ( ) {
5987+ const prebuildTxWithIntent = sandbox . stub ( TssUtils . prototype , 'prebuildTxWithIntent' ) ;
5988+ aptosTxRequest . walletType = 'cold' ;
5989+ prebuildTxWithIntent . resolves ( aptosTxRequest ) ;
5990+ prebuildTxWithIntent . calledOnceWithExactly ( {
5991+ reqId,
5992+ intentType : 'customTx' ,
5993+ aptosCustomTransactionParams : testCustomSmartContractCall ,
5994+ recipients : testAptosRecipients ,
5995+ } ) ;
5996+
5997+ const txPrebuild = await tssAptosWallet . prebuildTransaction ( {
5998+ reqId,
5999+ type : 'customTx' ,
6000+ aptosCustomTransactionParams : testCustomSmartContractCall ,
6001+ recipients : testAptosRecipients ,
6002+ } ) ;
6003+
6004+ txPrebuild . should . deepEqual ( {
6005+ walletId : tssAptosWallet . id ( ) ,
6006+ wallet : tssAptosWallet ,
6007+ txRequestId : 'aptos-tx-id' ,
6008+ txHex : 'aptos123abcd' ,
6009+ buildParams : {
6010+ type : 'customTx' ,
6011+ aptosCustomTransactionParams : testCustomSmartContractCall ,
6012+ recipients : testAptosRecipients ,
6013+ } ,
6014+ feeInfo : {
6015+ fee : 1000 ,
6016+ feeString : '1000' ,
6017+ } ,
6018+ } ) ;
6019+ } ) ;
6020+
6021+ it ( 'should build a standard Aptos transfer without breaking custom functionality' , async function ( ) {
6022+ const standardTransferRequest = {
6023+ ...aptosTxRequest ,
6024+ intent : {
6025+ intentType : 'payment' ,
6026+ } ,
6027+ } ;
6028+
6029+ const prebuildTxWithIntent = sandbox . stub ( TssUtils . prototype , 'prebuildTxWithIntent' ) ;
6030+ prebuildTxWithIntent . resolves ( standardTransferRequest ) ;
6031+ prebuildTxWithIntent . calledOnceWithExactly ( {
6032+ reqId,
6033+ recipients : testAptosRecipients ,
6034+ intentType : 'payment' ,
6035+ } ) ;
6036+
6037+ const txPrebuild = await tssAptosWallet . prebuildTransaction ( {
6038+ reqId,
6039+ recipients : testAptosRecipients ,
6040+ type : 'transfer' ,
6041+ } ) ;
6042+
6043+ txPrebuild . should . deepEqual ( {
6044+ walletId : tssAptosWallet . id ( ) ,
6045+ wallet : tssAptosWallet ,
6046+ txRequestId : 'aptos-tx-id' ,
6047+ txHex : 'aptos123abcd' ,
6048+ buildParams : {
6049+ recipients : testAptosRecipients ,
6050+ type : 'transfer' ,
6051+ } ,
6052+ feeInfo : {
6053+ fee : 1000 ,
6054+ feeString : '1000' ,
6055+ } ,
6056+ } ) ;
6057+ } ) ;
6058+
6059+ it ( 'should validate aptosCustomTransactionParams structure for smart contract calls' , async function ( ) {
6060+ const invalidParams = {
6061+ moduleName : '0x1::coin' ,
6062+ // Missing required functionName
6063+ typeArguments : [ '0x1::aptos_coin::AptosCoin' ] ,
6064+ functionArguments : [ '0x742d35Cc6634C0532925a3b8D400E65AAD801c0b' , '1000000' ] ,
6065+ } as any ; // Type assertion to test invalid params
6066+
6067+ const prebuildTxWithIntent = sandbox . stub ( TssUtils . prototype , 'prebuildTxWithIntent' ) ;
6068+ prebuildTxWithIntent . rejects ( new Error ( 'Function name is required and must be a non-empty string' ) ) ;
6069+
6070+ try {
6071+ await tssAptosWallet . prebuildTransaction ( {
6072+ reqId,
6073+ type : 'customTx' ,
6074+ aptosCustomTransactionParams : invalidParams ,
6075+ recipients : testAptosRecipients ,
6076+ } ) ;
6077+ throw new Error ( 'Expected promise to be rejected' ) ;
6078+ } catch ( error ) {
6079+ error . message . should . equal ( 'Function name is required and must be a non-empty string' ) ;
6080+ }
6081+
6082+ prebuildTxWithIntent . should . have . been . calledOnceWith ( {
6083+ reqId,
6084+ intentType : 'customTx' ,
6085+ sequenceId : undefined ,
6086+ comment : undefined ,
6087+ solInstructions : undefined ,
6088+ aptosCustomTransactionParams : invalidParams ,
6089+ recipients : testAptosRecipients ,
6090+ nonce : undefined ,
6091+ feeOptions : undefined ,
6092+ } ) ;
6093+ } ) ;
6094+ } ) ;
6095+ } ) ;
57126096} ) ;
0 commit comments