@@ -14,6 +14,7 @@ import { TavaxP } from '@bitgo/sdk-coin-avaxp';
1414import { decodeTransaction , parseTransaction , walletSimpleABI } from './helpers' ;
1515import * as sinon from 'sinon' ;
1616import { BN } from 'ethereumjs-util' ;
17+ import { EthereumNetwork } from '@bitgo/statics' ;
1718
1819nock . enableNetConnect ( ) ;
1920
@@ -375,6 +376,35 @@ describe('Avalanche C-Chain', function () {
375376 halfSignedRawTx . halfSigned . recipients [ 0 ] . amount . should . equals ( customRecipients [ 0 ] . amount ) ;
376377 halfSignedRawTx . halfSigned . recipients [ 0 ] . data . should . equals ( customRecipients [ 0 ] . data ) ;
377378 } ) ;
379+
380+ it ( 'should include isBatch, contractSequenceId, and sequenceId in half-signed txParams for batch transactions' , async function ( ) {
381+ const builder = getBuilder ( 'tavaxc' ) as TransactionBuilder ;
382+ builder . fee ( {
383+ fee : '280000000000' ,
384+ gasLimit : '7000000' ,
385+ } ) ;
386+ builder . counter ( 1 ) ;
387+ builder . type ( TransactionType . Send ) ;
388+ builder . contract ( account_1 . address ) ;
389+ builder . transfer ( ) . amount ( '1' ) . to ( account_2 . address ) . expirationTime ( 10000 ) . contractSequenceId ( 1 ) ;
390+
391+ const unsignedTx = await builder . build ( ) ;
392+ const unsignedTxForBroadcasting = unsignedTx . toBroadcastFormat ( ) ;
393+
394+ const halfSignedRawTx = await tavaxCoin . signTransaction ( {
395+ txPrebuild : {
396+ txHex : unsignedTxForBroadcasting ,
397+ isBatch : true ,
398+ nextContractSequenceId : 42 ,
399+ } ,
400+ prv : account_1 . owner_2 ,
401+ sequenceId : '7' ,
402+ } ) ;
403+
404+ halfSignedRawTx . halfSigned . isBatch . should . equal ( true ) ;
405+ halfSignedRawTx . halfSigned . contractSequenceId . should . equal ( 42 ) ;
406+ halfSignedRawTx . halfSigned . sequenceId . should . equal ( '7' ) ;
407+ } ) ;
378408 } ) ;
379409
380410 describe ( 'Transaction Verification' , ( ) => {
@@ -783,6 +813,176 @@ describe('Avalanche C-Chain', function () {
783813 . verifyTransaction ( { txParams, txPrebuild, wallet, verification } )
784814 . should . be . rejectedWith ( 'coin in txPrebuild did not match that in txParams supplied by client' ) ;
785815 } ) ;
816+
817+ describe ( 'Batch transaction verification' , ( ) => {
818+ let batcherContractAddress : string ;
819+
820+ beforeEach ( function ( ) {
821+ batcherContractAddress = ( tavaxCoin . staticsCoin ?. network as EthereumNetwork ) ?. batcherContractAddress as string ;
822+ } ) ;
823+ it ( 'should verify a native coin batch transaction with matching total amount' , async function ( ) {
824+ const wallet = new Wallet ( bitgo , tavaxCoin , { } ) ;
825+
826+ const txParams = {
827+ recipients : [
828+ { amount : '1000000000000' , address : address1 } ,
829+ { amount : '2500000000000' , address : address2 } ,
830+ ] ,
831+ wallet : wallet ,
832+ walletPassphrase : 'fakeWalletPassphrase' ,
833+ } ;
834+
835+ const txPrebuild = {
836+ recipients : [ { amount : '3500000000000' , address : batcherContractAddress } ] ,
837+ nextContractSequenceId : 0 ,
838+ gasPrice : 20000000000 ,
839+ gasLimit : 500000 ,
840+ isBatch : true ,
841+ coin : 'tavaxc' ,
842+ walletId : 'fakeWalletId' ,
843+ walletContractAddress : 'fakeWalletContractAddress' ,
844+ } ;
845+
846+ const verification = { } ;
847+
848+ const isTransactionVerified = await tavaxCoin . verifyTransaction ( {
849+ txParams,
850+ txPrebuild,
851+ wallet,
852+ verification,
853+ } ) ;
854+ isTransactionVerified . should . equal ( true ) ;
855+ } ) ;
856+
857+ it ( 'should verify a token batch transaction with zero native amount' , async function ( ) {
858+ const wallet = new Wallet ( bitgo , tavaxCoin , { } ) ;
859+
860+ const txParams = {
861+ recipients : [
862+ { amount : '1000000000000' , address : address1 } ,
863+ { amount : '2500000000000' , address : address2 } ,
864+ ] ,
865+ wallet : wallet ,
866+ walletPassphrase : 'fakeWalletPassphrase' ,
867+ tokenName : 'tavaxc:USDC' ,
868+ } ;
869+
870+ const txPrebuild = {
871+ recipients : [ { amount : '0' , address : batcherContractAddress } ] ,
872+ nextContractSequenceId : 0 ,
873+ gasPrice : 20000000000 ,
874+ gasLimit : 500000 ,
875+ isBatch : true ,
876+ coin : 'tavaxc' ,
877+ walletId : 'fakeWalletId' ,
878+ walletContractAddress : 'fakeWalletContractAddress' ,
879+ } ;
880+
881+ const verification = { } ;
882+
883+ const isTransactionVerified = await tavaxCoin . verifyTransaction ( {
884+ txParams,
885+ txPrebuild,
886+ wallet,
887+ verification,
888+ } ) ;
889+ isTransactionVerified . should . equal ( true ) ;
890+ } ) ;
891+
892+ it ( 'should reject a token batch transaction with non-zero native amount' , async function ( ) {
893+ const wallet = new Wallet ( bitgo , tavaxCoin , { } ) ;
894+
895+ const txParams = {
896+ recipients : [
897+ { amount : '1000000000000' , address : address1 } ,
898+ { amount : '2500000000000' , address : address2 } ,
899+ ] ,
900+ wallet : wallet ,
901+ walletPassphrase : 'fakeWalletPassphrase' ,
902+ tokenName : 'tavaxc:USDC' ,
903+ } ;
904+
905+ const txPrebuild = {
906+ recipients : [ { amount : '1000000000000' , address : batcherContractAddress } ] ,
907+ nextContractSequenceId : 0 ,
908+ gasPrice : 20000000000 ,
909+ gasLimit : 500000 ,
910+ isBatch : true ,
911+ coin : 'tavaxc' ,
912+ walletId : 'fakeWalletId' ,
913+ walletContractAddress : 'fakeWalletContractAddress' ,
914+ } ;
915+
916+ const verification = { } ;
917+
918+ await tavaxCoin
919+ . verifyTransaction ( { txParams, txPrebuild, wallet, verification } )
920+ . should . be . rejectedWith ( 'batch token transaction amount in txPrebuild should be zero for token transfers' ) ;
921+ } ) ;
922+
923+ it ( 'should reject a native coin batch transaction with mismatched total amount' , async function ( ) {
924+ const wallet = new Wallet ( bitgo , tavaxCoin , { } ) ;
925+
926+ const txParams = {
927+ recipients : [
928+ { amount : '1000000000000' , address : address1 } ,
929+ { amount : '2500000000000' , address : address2 } ,
930+ ] ,
931+ wallet : wallet ,
932+ walletPassphrase : 'fakeWalletPassphrase' ,
933+ } ;
934+
935+ const txPrebuild = {
936+ recipients : [ { amount : '9999999999999' , address : batcherContractAddress } ] ,
937+ nextContractSequenceId : 0 ,
938+ gasPrice : 20000000000 ,
939+ gasLimit : 500000 ,
940+ isBatch : true ,
941+ coin : 'tavaxc' ,
942+ walletId : 'fakeWalletId' ,
943+ walletContractAddress : 'fakeWalletContractAddress' ,
944+ } ;
945+
946+ const verification = { } ;
947+
948+ await tavaxCoin
949+ . verifyTransaction ( { txParams, txPrebuild, wallet, verification } )
950+ . should . be . rejectedWith (
951+ 'batch transaction amount in txPrebuild received from BitGo servers does not match txParams supplied by client'
952+ ) ;
953+ } ) ;
954+
955+ it ( 'should reject a batch transaction sent to wrong batcher contract address' , async function ( ) {
956+ const wallet = new Wallet ( bitgo , tavaxCoin , { } ) ;
957+ const wrongBatcherAddress = '0x0000000000000000000000000000000000000001' ;
958+
959+ const txParams = {
960+ recipients : [
961+ { amount : '1000000000000' , address : address1 } ,
962+ { amount : '2500000000000' , address : address2 } ,
963+ ] ,
964+ wallet : wallet ,
965+ walletPassphrase : 'fakeWalletPassphrase' ,
966+ } ;
967+
968+ const txPrebuild = {
969+ recipients : [ { amount : '3500000000000' , address : wrongBatcherAddress } ] ,
970+ nextContractSequenceId : 0 ,
971+ gasPrice : 20000000000 ,
972+ gasLimit : 500000 ,
973+ isBatch : true ,
974+ coin : 'tavaxc' ,
975+ walletId : 'fakeWalletId' ,
976+ walletContractAddress : 'fakeWalletContractAddress' ,
977+ } ;
978+
979+ const verification = { } ;
980+
981+ await tavaxCoin
982+ . verifyTransaction ( { txParams, txPrebuild, wallet, verification } )
983+ . should . be . rejectedWith ( 'recipient address of txPrebuild does not match batcher address' ) ;
984+ } ) ;
985+ } ) ;
786986 } ) ;
787987
788988 describe ( 'Hop Transaction Parameters' , ( ) => {
0 commit comments