diff --git a/modules/abstract-cosmos/src/cosmosCoin.ts b/modules/abstract-cosmos/src/cosmosCoin.ts index 22051bb295..1cb04b2259 100644 --- a/modules/abstract-cosmos/src/cosmosCoin.ts +++ b/modules/abstract-cosmos/src/cosmosCoin.ts @@ -22,7 +22,7 @@ import { VerifyTransactionOptions, } from '@bitgo/sdk-core'; import { EcdsaPaillierProof, EcdsaRangeProof, EcdsaTypes } from '@bitgo/sdk-lib-mpc'; -import { CoinFamily, BaseCoin as StaticsBaseCoin } from '@bitgo/statics'; +import { BaseCoin as StaticsBaseCoin, CoinFamily } from '@bitgo/statics'; import { bip32 } from '@bitgo/utxo-lib'; import { Coin } from '@cosmjs/stargate'; import { BigNumber } from 'bignumber.js'; diff --git a/modules/abstract-cosmos/src/lib/transaction.ts b/modules/abstract-cosmos/src/lib/transaction.ts index 26f87704f0..da0248a2d2 100644 --- a/modules/abstract-cosmos/src/lib/transaction.ts +++ b/modules/abstract-cosmos/src/lib/transaction.ts @@ -11,9 +11,7 @@ import { BaseCoin as CoinConfig } from '@bitgo/statics'; import { fromHex, toBase64 } from '@cosmjs/encoding'; import { makeSignBytes } from '@cosmjs/proto-signing'; import { TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx'; - import { UNAVAILABLE_TEXT } from './constants'; - import { CosmosLikeTransaction, DelegateOrUndelegeteMessage, @@ -174,64 +172,65 @@ export class CosmosTransaction extends BaseTransaction { */ explainTransactionInternal(json: TxData, explanationResult: TransactionExplanation): TransactionExplanation { let outputs: TransactionRecipient[]; - let message; let outputAmount; switch (json.type) { case TransactionType.Send: explanationResult.type = TransactionType.Send; - message = json.sendMessages[0].value as SendMessage; - outputAmount = message.amount[0].amount; - outputs = [ - { - address: message.toAddress, - amount: outputAmount, - }, - ]; + outputAmount = BigInt(0); + outputs = json.sendMessages.map((message) => { + const sendMessage = message.value as SendMessage; + outputAmount = outputAmount + BigInt(sendMessage.amount[0].amount); + return { + address: sendMessage.toAddress, + amount: sendMessage.amount[0].amount, + }; + }); break; case TransactionType.StakingActivate: explanationResult.type = TransactionType.StakingActivate; - message = json.sendMessages[0].value as DelegateOrUndelegeteMessage; - outputAmount = message.amount.amount; - outputs = [ - { - address: message.validatorAddress, - amount: outputAmount, - }, - ]; + outputAmount = BigInt(0); + outputs = json.sendMessages.map((message) => { + const delegateMessage = message.value as DelegateOrUndelegeteMessage; + outputAmount = outputAmount + BigInt(delegateMessage.amount.amount); + return { + address: delegateMessage.validatorAddress, + amount: delegateMessage.amount.amount, + }; + }); break; case TransactionType.StakingDeactivate: explanationResult.type = TransactionType.StakingDeactivate; - message = json.sendMessages[0].value as DelegateOrUndelegeteMessage; - outputAmount = message.amount.amount; - outputs = [ - { - address: message.validatorAddress, - amount: outputAmount, - }, - ]; + outputAmount = BigInt(0); + outputs = json.sendMessages.map((message) => { + const delegateMessage = message.value as DelegateOrUndelegeteMessage; + outputAmount = outputAmount + BigInt(delegateMessage.amount.amount); + return { + address: delegateMessage.validatorAddress, + amount: delegateMessage.amount.amount, + }; + }); break; case TransactionType.StakingWithdraw: explanationResult.type = TransactionType.StakingWithdraw; - message = json.sendMessages[0].value as WithdrawDelegatorRewardsMessage; - outputs = [ - { - address: message.validatorAddress, + outputs = json.sendMessages.map((message) => { + const withdrawMessage = message.value as WithdrawDelegatorRewardsMessage; + return { + address: withdrawMessage.validatorAddress, amount: UNAVAILABLE_TEXT, - }, - ]; - outputAmount = UNAVAILABLE_TEXT; + }; + }); break; case TransactionType.ContractCall: explanationResult.type = TransactionType.ContractCall; - message = json.sendMessages[0].value as ExecuteContractMessage; - outputAmount = message.funds?.[0]?.amount ?? '0'; - outputs = [ - { - address: message.contract, - amount: outputAmount, - }, - ]; - outputAmount = outputAmount; + outputAmount = BigInt(0); + outputs = json.sendMessages.map((message) => { + const executeContractMessage = message.value as ExecuteContractMessage; + outputAmount = outputAmount + BigInt(executeContractMessage.funds?.[0]?.amount ?? '0'); + return { + address: executeContractMessage.contract, + amount: executeContractMessage.funds?.[0]?.amount ?? '0', + }; + }); break; default: throw new InvalidTransactionError('Transaction type not supported'); @@ -243,7 +242,7 @@ export class CosmosTransaction extends BaseTransaction { } return { ...explanationResult, - outputAmount, + outputAmount: outputAmount?.toString(), outputs, }; } @@ -262,56 +261,64 @@ export class CosmosTransaction extends BaseTransaction { const inputs: Entry[] = []; switch (this.type) { case TransactionType.Send: - const message = this.cosmosLikeTransaction.sendMessages[0].value as SendMessage; - inputs.push({ - address: message.fromAddress, - value: message.amount[0].amount, - coin: this._coinConfig.name, - }); - outputs.push({ - address: message.toAddress, - value: message.amount[0].amount, - coin: this._coinConfig.name, + this.cosmosLikeTransaction.sendMessages.forEach((message) => { + const sendMessage = message.value as SendMessage; + inputs.push({ + address: sendMessage.fromAddress, + value: sendMessage.amount[0].amount, + coin: this._coinConfig.name, + }); + outputs.push({ + address: sendMessage.toAddress, + value: sendMessage.amount[0].amount, + coin: this._coinConfig.name, + }); }); break; case TransactionType.StakingActivate: case TransactionType.StakingDeactivate: - const delegateMessage = this.cosmosLikeTransaction.sendMessages[0].value as DelegateOrUndelegeteMessage; - inputs.push({ - address: delegateMessage.delegatorAddress, - value: delegateMessage.amount.amount, - coin: this._coinConfig.name, - }); - outputs.push({ - address: delegateMessage.validatorAddress, - value: delegateMessage.amount.amount, - coin: this._coinConfig.name, + this.cosmosLikeTransaction.sendMessages.forEach((message) => { + const delegateMessage = message.value as DelegateOrUndelegeteMessage; + inputs.push({ + address: delegateMessage.delegatorAddress, + value: delegateMessage.amount.amount, + coin: this._coinConfig.name, + }); + outputs.push({ + address: delegateMessage.validatorAddress, + value: delegateMessage.amount.amount, + coin: this._coinConfig.name, + }); }); break; case TransactionType.StakingWithdraw: - const withdrawMessage = this.cosmosLikeTransaction.sendMessages[0].value as WithdrawDelegatorRewardsMessage; - inputs.push({ - address: withdrawMessage.delegatorAddress, - value: UNAVAILABLE_TEXT, - coin: this._coinConfig.name, - }); - outputs.push({ - address: withdrawMessage.validatorAddress, - value: UNAVAILABLE_TEXT, - coin: this._coinConfig.name, + this.cosmosLikeTransaction.sendMessages.forEach((message) => { + const withdrawMessage = message.value as WithdrawDelegatorRewardsMessage; + inputs.push({ + address: withdrawMessage.delegatorAddress, + value: UNAVAILABLE_TEXT, + coin: this._coinConfig.name, + }); + outputs.push({ + address: withdrawMessage.validatorAddress, + value: UNAVAILABLE_TEXT, + coin: this._coinConfig.name, + }); }); break; case TransactionType.ContractCall: - const executeContractMessage = this.cosmosLikeTransaction.sendMessages[0].value as ExecuteContractMessage; - inputs.push({ - address: executeContractMessage.sender, - value: executeContractMessage.funds?.[0]?.amount ?? '0', - coin: this._coinConfig.name, - }); - outputs.push({ - address: executeContractMessage.contract, - value: executeContractMessage.funds?.[0]?.amount ?? '0', - coin: this._coinConfig.name, + this.cosmosLikeTransaction.sendMessages.forEach((message) => { + const executeContractMessage = message.value as ExecuteContractMessage; + inputs.push({ + address: executeContractMessage.sender, + value: executeContractMessage.funds?.[0]?.amount ?? '0', + coin: this._coinConfig.name, + }); + outputs.push({ + address: executeContractMessage.contract, + value: executeContractMessage.funds?.[0]?.amount ?? '0', + coin: this._coinConfig.name, + }); }); break; default: diff --git a/modules/sdk-coin-atom/src/lib/transaction.ts b/modules/sdk-coin-atom/src/lib/transaction.ts index a6de281607..0a3e9a520c 100644 --- a/modules/sdk-coin-atom/src/lib/transaction.ts +++ b/modules/sdk-coin-atom/src/lib/transaction.ts @@ -11,7 +11,6 @@ import { BaseCoin as CoinConfig } from '@bitgo/statics'; import { fromHex, toBase64 } from '@cosmjs/encoding'; import { makeSignBytes } from '@cosmjs/proto-signing'; import { TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx'; - import { UNAVAILABLE_TEXT } from './constants'; import { AtomTransaction, @@ -184,51 +183,53 @@ export class Transaction extends BaseTransaction { */ explainTransactionInternal(json: TxData, explanationResult: TransactionExplanation): TransactionExplanation { let outputs: TransactionRecipient[]; - let message; let outputAmount; switch (json.type) { case TransactionType.Send: explanationResult.type = TransactionType.Send; - message = json.sendMessages[0].value as SendMessage; - outputAmount = message.amount[0].amount; - outputs = [ - { - address: message.toAddress, - amount: outputAmount, - }, - ]; + outputAmount = BigInt(0); + outputs = json.sendMessages.map((message) => { + const sendMessage = message.value as SendMessage; + outputAmount = outputAmount + BigInt(sendMessage.amount[0].amount); + return { + address: sendMessage.toAddress, + amount: sendMessage.amount[0].amount, + }; + }); break; case TransactionType.StakingActivate: explanationResult.type = TransactionType.StakingActivate; - message = json.sendMessages[0].value as DelegateOrUndelegeteMessage; - outputAmount = message.amount.amount; - outputs = [ - { - address: message.validatorAddress, - amount: outputAmount, - }, - ]; + outputAmount = BigInt(0); + outputs = json.sendMessages.map((message) => { + const delegateMessage = message.value as DelegateOrUndelegeteMessage; + outputAmount = outputAmount + BigInt(delegateMessage.amount.amount); + return { + address: delegateMessage.validatorAddress, + amount: delegateMessage.amount.amount, + }; + }); break; case TransactionType.StakingDeactivate: explanationResult.type = TransactionType.StakingDeactivate; - message = json.sendMessages[0].value as DelegateOrUndelegeteMessage; - outputAmount = message.amount.amount; - outputs = [ - { - address: message.validatorAddress, - amount: outputAmount, - }, - ]; + outputAmount = BigInt(0); + outputs = json.sendMessages.map((message) => { + const delegateMessage = message.value as DelegateOrUndelegeteMessage; + outputAmount = outputAmount + BigInt(delegateMessage.amount.amount); + return { + address: delegateMessage.validatorAddress, + amount: delegateMessage.amount.amount, + }; + }); break; case TransactionType.StakingWithdraw: explanationResult.type = TransactionType.StakingWithdraw; - message = json.sendMessages[0].value as WithdrawDelegatorRewardsMessage; - outputs = [ - { - address: message.validatorAddress, + outputs = json.sendMessages.map((message) => { + const withdrawMessage = message.value as WithdrawDelegatorRewardsMessage; + return { + address: withdrawMessage.validatorAddress, amount: UNAVAILABLE_TEXT, - }, - ]; + }; + }); break; default: throw new InvalidTransactionError('Transaction type not supported'); @@ -240,7 +241,7 @@ export class Transaction extends BaseTransaction { } return { ...explanationResult, - outputAmount, + outputAmount: outputAmount?.toString(), outputs, }; } @@ -254,43 +255,49 @@ export class Transaction extends BaseTransaction { const inputs: Entry[] = []; switch (this.type) { case TransactionType.Send: - const message = this.atomTransaction.sendMessages[0].value as SendMessage; - inputs.push({ - address: message.fromAddress, - value: message.amount[0].amount, - coin: this._coinConfig.name, - }); - outputs.push({ - address: message.toAddress, - value: message.amount[0].amount, - coin: this._coinConfig.name, + this.atomTransaction.sendMessages.forEach((message) => { + const sendMessage = message.value as SendMessage; + inputs.push({ + address: sendMessage.fromAddress, + value: sendMessage.amount[0].amount, + coin: this._coinConfig.name, + }); + outputs.push({ + address: sendMessage.toAddress, + value: sendMessage.amount[0].amount, + coin: this._coinConfig.name, + }); }); break; case TransactionType.StakingActivate: case TransactionType.StakingDeactivate: - const delegateMessage = this.atomTransaction.sendMessages[0].value as DelegateOrUndelegeteMessage; - inputs.push({ - address: delegateMessage.delegatorAddress, - value: delegateMessage.amount.amount, - coin: this._coinConfig.name, - }); - outputs.push({ - address: delegateMessage.validatorAddress, - value: delegateMessage.amount.amount, - coin: this._coinConfig.name, + this.atomTransaction.sendMessages.forEach((message) => { + const delegateMessage = message.value as DelegateOrUndelegeteMessage; + inputs.push({ + address: delegateMessage.delegatorAddress, + value: delegateMessage.amount.amount, + coin: this._coinConfig.name, + }); + outputs.push({ + address: delegateMessage.validatorAddress, + value: delegateMessage.amount.amount, + coin: this._coinConfig.name, + }); }); break; case TransactionType.StakingWithdraw: - const withdrawMessage = this.atomTransaction.sendMessages[0].value as WithdrawDelegatorRewardsMessage; - inputs.push({ - address: withdrawMessage.delegatorAddress, - value: UNAVAILABLE_TEXT, - coin: this._coinConfig.name, - }); - outputs.push({ - address: withdrawMessage.validatorAddress, - value: UNAVAILABLE_TEXT, - coin: this._coinConfig.name, + this.atomTransaction.sendMessages.forEach((message) => { + const withdrawMessage = message.value as WithdrawDelegatorRewardsMessage; + inputs.push({ + address: withdrawMessage.delegatorAddress, + value: UNAVAILABLE_TEXT, + coin: this._coinConfig.name, + }); + outputs.push({ + address: withdrawMessage.validatorAddress, + value: UNAVAILABLE_TEXT, + coin: this._coinConfig.name, + }); }); break; default: diff --git a/modules/sdk-coin-atom/test/resources/atom.ts b/modules/sdk-coin-atom/test/resources/atom.ts index 7e09453641..1ffcfc3c07 100644 --- a/modules/sdk-coin-atom/test/resources/atom.ts +++ b/modules/sdk-coin-atom/test/resources/atom.ts @@ -44,6 +44,52 @@ export const TEST_SEND_TX = { }, }; +export const TEST_SEND_MANY_TX = { + hash: 'F104E7246AF4BC292B5DDD8CEAE131E599B061C38ED5AE67BC6029FFD9FCF4BE', + signature: 'z63EmmgJ0A5i+YMkVRr+wNkuaJEP0SqDapmGAtT4TuMv5+bI0DgtwLUgFrDxF+Yc8twgAIn+7tB6HwLzSSdibA==', + pubKey: 'Ah0Xud1r7fdljxs/dx9Rud1Un55KArr94VMtAMoUXBsO', + privateKey: 'cGQSjk/xusPlqScte8OnHMAaW7Zjt1wu7R6W4eYzLUg=', + signedTxBase64: + 'Cp4CCowBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmwKLWNvc21vczFzbWVma3E5eWF4Z3c5YzlmaHltNnFzcDc0NnE4ODRjemVhY24zMhItY29zbW9zMTZnaG45YzZmNXl1YTA5enF3N3k3OTRtdmMzMGg0eTRtZDdja3VrGgwKBXVhdG9tEgM1MDAKjAEKHC9jb3Ntb3MuYmFuay52MWJldGExLk1zZ1NlbmQSbAotY29zbW9zMXNtZWZrcTl5YXhndzljOWZoeW02cXNwNzQ2cTg4NGN6ZWFjbjMyEi1jb3Ntb3MxeXRlejA2eXgwdTN5anpqamptMDJ4eXgzbWgyNWFrZW56cWwzbjgaDAoFdWF0b20SAzUwMBJpClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECHRe53Wvt92WPGz93H1G53VSfnkoCuv3hUy0AyhRcGw4SBAoCCAEYFhIVCg8KBXVhdG9tEgYzMDAwMDAQkKEPGkDPrcSaaAnQDmL5gyRVGv7A2S5okQ/RKoNqmYYC1PhO4y/n5sjQOC3AtSAWsPEX5hzy3CAAif7u0HofAvNJJ2Js', + sender: 'cosmos1smefkq9yaxgw9c9fhym6qsp746q884czeacn32', + chainId: 'theta-testnet-001', + accountNumber: 723763, + sequence: 22, + memo: '', + sendMessages: [ + { + typeUrl: '/cosmos.bank.v1beta1.MsgSend', + value: { + amount: [ + { + denom: 'uatom', + amount: '500', + }, + ], + toAddress: 'cosmos16ghn9c6f5yua09zqw7y794mvc30h4y4md7ckuk', + fromAddress: 'cosmos1smefkq9yaxgw9c9fhym6qsp746q884czeacn32', + }, + }, + { + typeUrl: '/cosmos.bank.v1beta1.MsgSend', + value: { + amount: [ + { + denom: 'uatom', + amount: '500', + }, + ], + toAddress: 'cosmos1ytez06yx0u3yjzjjjm02xyx3mh25akenzql3n8', + fromAddress: 'cosmos1smefkq9yaxgw9c9fhym6qsp746q884czeacn32', + }, + }, + ], + gasBudget: { + amount: [{ denom: 'uatom', amount: '300000' }], + gasLimit: 250000, + }, +}; + export const TEST_DELEGATE_TX = { hash: '3FF02D82E6799633A33AA07CE26093913E6D7598AA9116A13D70B13302410BA3', signature: 'bxQvMdcZqetn4LAxYAhNeyluqfeDPIn/U7HMCw/0tIh4NY4JuHcZmSFFVxxks2oxWHzcnblxZnOSDtDpVIvHwQ==', diff --git a/modules/sdk-coin-atom/test/unit/atom.ts b/modules/sdk-coin-atom/test/unit/atom.ts index 20a2063416..b2a4c77417 100644 --- a/modules/sdk-coin-atom/test/unit/atom.ts +++ b/modules/sdk-coin-atom/test/unit/atom.ts @@ -1,23 +1,24 @@ import { BitGoAPI } from '@bitgo/sdk-api'; +import { EcdsaRangeProof, EcdsaTypes } from '@bitgo/sdk-lib-mpc'; import { mockSerializedChallengeWithProofs, TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test'; +import { coins } from '@bitgo/statics'; import BigNumber from 'bignumber.js'; -import should = require('should'); import sinon from 'sinon'; import { Atom, Tatom, Transaction } from '../../src'; -import { EcdsaRangeProof, EcdsaTypes } from '@bitgo/sdk-lib-mpc'; +import { GAS_AMOUNT } from '../../src/lib/constants'; +import { SendMessage } from '../../src/lib/iface'; import utils from '../../src/lib/utils'; import { address, TEST_DELEGATE_TX, + TEST_SEND_MANY_TX, TEST_SEND_TX, + TEST_TX_WITH_MEMO, TEST_UNDELEGATE_TX, TEST_WITHDRAW_REWARDS_TX, - TEST_TX_WITH_MEMO, wrwUser, } from '../resources/atom'; -import { coins } from '@bitgo/statics'; -import { SendMessage } from '../../src/lib/iface'; -import { GAS_AMOUNT } from '../../src/lib/constants'; +import should = require('should'); describe('ATOM', function () { let bitgo: TestBitGoAPI; @@ -121,6 +122,28 @@ describe('ATOM', function () { isTransactionVerified.should.equal(true); }); + it('should succeed to verify sendMany transaction', async function () { + const txPrebuild = { + txHex: TEST_SEND_MANY_TX.signedTxBase64, + txInfo: {}, + }; + const txParams = { + recipients: [ + { + address: 'cosmos16ghn9c6f5yua09zqw7y794mvc30h4y4md7ckuk', + amount: '500', + }, + { + address: 'cosmos1ytez06yx0u3yjzjjjm02xyx3mh25akenzql3n8', + amount: '500', + }, + ], + }; + const verification = {}; + const isTransactionVerified = await basecoin.verifyTransaction({ txParams, txPrebuild, verification }); + isTransactionVerified.should.equal(true); + }); + it('should succeed to verify delegate transaction', async function () { const txPrebuild = { txHex: TEST_DELEGATE_TX.signedTxBase64, @@ -209,6 +232,31 @@ describe('ATOM', function () { }); }); + it('should explain sendMany transfer transaction', async function () { + const explainedTransaction = await basecoin.explainTransaction({ + txHex: TEST_SEND_MANY_TX.signedTxBase64, + }); + explainedTransaction.should.deepEqual({ + displayOrder: ['id', 'outputs', 'outputAmount', 'changeOutputs', 'changeAmount', 'fee', 'type'], + id: TEST_SEND_MANY_TX.hash, + outputs: [ + { + address: 'cosmos16ghn9c6f5yua09zqw7y794mvc30h4y4md7ckuk', + amount: '500', + }, + { + address: 'cosmos1ytez06yx0u3yjzjjjm02xyx3mh25akenzql3n8', + amount: '500', + }, + ], + outputAmount: '1000', + changeOutputs: [], + changeAmount: '0', + fee: { fee: TEST_SEND_MANY_TX.gasBudget.amount[0].amount }, + type: 0, + }); + }); + it('should explain a delegate transaction', async function () { const explainedTransaction = await basecoin.explainTransaction({ txHex: TEST_DELEGATE_TX.signedTxBase64, diff --git a/modules/sdk-coin-atom/test/unit/transactionBuilder/transferBuilder.ts b/modules/sdk-coin-atom/test/unit/transactionBuilder/transferBuilder.ts index 03918ab00c..05f2c81bff 100644 --- a/modules/sdk-coin-atom/test/unit/transactionBuilder/transferBuilder.ts +++ b/modules/sdk-coin-atom/test/unit/transactionBuilder/transferBuilder.ts @@ -2,7 +2,6 @@ import { TransactionType } from '@bitgo/sdk-core'; import { fromBase64, toHex } from '@cosmjs/encoding'; import axios from 'axios'; import should from 'should'; - import { KeyPair } from '../../../src'; import * as testData from '../../resources/atom'; import { getBuilderFactory } from '../getBuilderFactory'; @@ -146,6 +145,53 @@ describe('Atom Transfer Builder', () => { ]); }); + it('should build a sendMany Transfer tx', async function () { + const testSendManyTx = testData.TEST_SEND_MANY_TX; + const txBuilder = factory.getTransferBuilder(); + txBuilder.sequence(testSendManyTx.sequence); + txBuilder.gasBudget(testSendManyTx.gasBudget); + txBuilder.messages(testSendManyTx.sendMessages.map((msg) => msg.value)); + txBuilder.publicKey(toHex(fromBase64(testSendManyTx.pubKey))); + txBuilder.chainId(testSendManyTx.chainId); + txBuilder.accountNumber(testSendManyTx.accountNumber); + txBuilder.memo(testSendManyTx.memo); + txBuilder.addSignature( + { pub: toHex(fromBase64(testSendManyTx.pubKey)) }, + Buffer.from(testSendManyTx.signature, 'base64') + ); + + const tx = await txBuilder.build(); + const json = await (await txBuilder.build()).toJson(); + should.equal(tx.type, TransactionType.Send); + should.deepEqual(json.gasBudget, testSendManyTx.gasBudget); + should.deepEqual(json.sendMessages, testSendManyTx.sendMessages); + should.deepEqual(json.publicKey, toHex(fromBase64(testSendManyTx.pubKey))); + should.deepEqual(json.sequence, testSendManyTx.sequence); + should.deepEqual( + tx.inputs, + testSendManyTx.sendMessages.map((msg) => { + return { + address: msg.value.fromAddress, + value: msg.value.amount[0].amount, + coin: 'tatom', + }; + }) + ); + should.deepEqual( + tx.outputs, + testSendManyTx.sendMessages.map((msg) => { + return { + address: msg.value.toAddress, + value: msg.value.amount[0].amount, + coin: 'tatom', + }; + }) + ); + should.equal(tx.id, testSendManyTx.hash); + const rawTx = tx.toBroadcastFormat(); + should.equal(rawTx, testSendManyTx.signedTxBase64); + }); + xit('should submit a send transaction', async () => { const keyPair = new KeyPair({ prv: toHex(fromBase64(testTx.privateKey)) }); const axiosConfig = { diff --git a/modules/sdk-coin-bld/test/unit/bld.ts b/modules/sdk-coin-bld/test/unit/bld.ts index 47e9cf4c33..7fff94f511 100644 --- a/modules/sdk-coin-bld/test/unit/bld.ts +++ b/modules/sdk-coin-bld/test/unit/bld.ts @@ -1,7 +1,7 @@ import { CosmosTransaction, SendMessage } from '@bitgo/abstract-cosmos'; import { BitGoAPI } from '@bitgo/sdk-api'; import { EcdsaRangeProof, EcdsaTypes } from '@bitgo/sdk-lib-mpc'; -import { TestBitGo, TestBitGoAPI, mockSerializedChallengeWithProofs } from '@bitgo/sdk-test'; +import { mockSerializedChallengeWithProofs, TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test'; import { coins } from '@bitgo/statics'; import BigNumber from 'bignumber.js'; import { beforeEach } from 'mocha'; @@ -9,12 +9,12 @@ import sinon from 'sinon'; import { Bld, Tbld } from '../../src'; import utils from '../../src/lib/utils'; import { + address, TEST_DELEGATE_TX, TEST_SEND_TX, TEST_TX_WITH_MEMO, TEST_UNDELEGATE_TX, TEST_WITHDRAW_REWARDS_TX, - address, wrwUser, } from '../resources/bld'; import should = require('should'); @@ -265,7 +265,7 @@ describe('BLD', function () { amount: 'UNAVAILABLE', }, ], - outputAmount: 'UNAVAILABLE', + outputAmount: undefined, changeOutputs: [], changeAmount: '0', fee: { fee: TEST_WITHDRAW_REWARDS_TX.gasBudget.amount[0].amount }, diff --git a/modules/sdk-coin-hash/test/unit/hash.ts b/modules/sdk-coin-hash/test/unit/hash.ts index d4b5bd667b..e7fd4900fa 100644 --- a/modules/sdk-coin-hash/test/unit/hash.ts +++ b/modules/sdk-coin-hash/test/unit/hash.ts @@ -1,7 +1,7 @@ import { CosmosTransaction, SendMessage } from '@bitgo/abstract-cosmos'; import { BitGoAPI } from '@bitgo/sdk-api'; import { EcdsaRangeProof, EcdsaTypes } from '@bitgo/sdk-lib-mpc'; -import { TestBitGo, TestBitGoAPI, mockSerializedChallengeWithProofs } from '@bitgo/sdk-test'; +import { mockSerializedChallengeWithProofs, TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test'; import { coins } from '@bitgo/statics'; import BigNumber from 'bignumber.js'; import { beforeEach } from 'mocha'; @@ -9,12 +9,12 @@ import sinon from 'sinon'; import { Hash, Thash } from '../../src'; import utils from '../../src/lib/utils'; import { + address, TEST_DELEGATE_TX, TEST_SEND_TX, TEST_TX_WITH_MEMO, TEST_UNDELEGATE_TX, TEST_WITHDRAW_REWARDS_TX, - address, wrwUser, } from '../resources/hash'; import should = require('should'); @@ -265,7 +265,7 @@ describe('HASH', function () { amount: 'UNAVAILABLE', }, ], - outputAmount: 'UNAVAILABLE', + outputAmount: undefined, changeOutputs: [], changeAmount: '0', fee: { fee: TEST_WITHDRAW_REWARDS_TX.gasBudget.amount[0].amount }, diff --git a/modules/sdk-coin-injective/test/unit/injective.ts b/modules/sdk-coin-injective/test/unit/injective.ts index 50cd92643d..2a4496f7af 100644 --- a/modules/sdk-coin-injective/test/unit/injective.ts +++ b/modules/sdk-coin-injective/test/unit/injective.ts @@ -1,7 +1,7 @@ import { CosmosTransaction, SendMessage } from '@bitgo/abstract-cosmos'; import { BitGoAPI } from '@bitgo/sdk-api'; import { EcdsaRangeProof, EcdsaTypes } from '@bitgo/sdk-lib-mpc'; -import { TestBitGo, TestBitGoAPI, mockSerializedChallengeWithProofs } from '@bitgo/sdk-test'; +import { mockSerializedChallengeWithProofs, TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test'; import { coins } from '@bitgo/statics'; import BigNumber from 'bignumber.js'; import { beforeEach } from 'mocha'; @@ -9,13 +9,13 @@ import sinon from 'sinon'; import { Injective, Tinjective } from '../../src'; import utils from '../../src/lib/utils'; import { + address, + mockAccountDetailsResponse, TEST_DELEGATE_TX, TEST_SEND_TX, TEST_TX_WITH_MEMO, TEST_UNDELEGATE_TX, TEST_WITHDRAW_REWARDS_TX, - address, - mockAccountDetailsResponse, wrwUser, } from '../resources/injective'; import should = require('should'); @@ -267,7 +267,7 @@ describe('INJ', function () { amount: 'UNAVAILABLE', }, ], - outputAmount: 'UNAVAILABLE', + outputAmount: undefined, changeOutputs: [], changeAmount: '0', fee: { fee: TEST_WITHDRAW_REWARDS_TX.gasBudget.amount[0].amount }, diff --git a/modules/sdk-coin-osmo/test/unit/osmo.ts b/modules/sdk-coin-osmo/test/unit/osmo.ts index a2cedc1e09..bfabb0dd18 100644 --- a/modules/sdk-coin-osmo/test/unit/osmo.ts +++ b/modules/sdk-coin-osmo/test/unit/osmo.ts @@ -1,24 +1,24 @@ +import { CosmosTransaction, SendMessage } from '@bitgo/abstract-cosmos'; import { BitGoAPI } from '@bitgo/sdk-api'; -import { TestBitGo, TestBitGoAPI, mockSerializedChallengeWithProofs } from '@bitgo/sdk-test'; +import { EcdsaRangeProof, EcdsaTypes } from '@bitgo/sdk-lib-mpc'; +import { mockSerializedChallengeWithProofs, TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test'; +import { coins } from '@bitgo/statics'; import BigNumber from 'bignumber.js'; +import { beforeEach } from 'mocha'; import sinon from 'sinon'; import { Osmo, Tosmo } from '../../src'; import utils from '../../src/lib/utils'; import { + address, TEST_DELEGATE_TX, + TEST_EXECUTE_CONTRACT_TRANSACTION, TEST_SEND_TX, TEST_TX_WITH_MEMO, TEST_UNDELEGATE_TX, TEST_WITHDRAW_REWARDS_TX, - TEST_EXECUTE_CONTRACT_TRANSACTION, - address, wrwUser, } from '../resources/osmo'; import should = require('should'); -import { coins } from '@bitgo/statics'; -import { beforeEach } from 'mocha'; -import { EcdsaRangeProof, EcdsaTypes } from '@bitgo/sdk-lib-mpc'; -import { CosmosTransaction, SendMessage } from '@bitgo/abstract-cosmos'; describe('OSMO', function () { let bitgo: TestBitGoAPI; @@ -302,7 +302,7 @@ describe('OSMO', function () { amount: 'UNAVAILABLE', }, ], - outputAmount: 'UNAVAILABLE', + outputAmount: undefined, changeOutputs: [], changeAmount: '0', fee: { fee: TEST_WITHDRAW_REWARDS_TX.gasBudget.amount[0].amount }, diff --git a/modules/sdk-coin-sei/test/unit/sei.ts b/modules/sdk-coin-sei/test/unit/sei.ts index 8a3f346191..b2dbffe988 100644 --- a/modules/sdk-coin-sei/test/unit/sei.ts +++ b/modules/sdk-coin-sei/test/unit/sei.ts @@ -1,7 +1,7 @@ import { CosmosTransaction, SendMessage } from '@bitgo/abstract-cosmos'; import { BitGoAPI } from '@bitgo/sdk-api'; import { EcdsaRangeProof, EcdsaTypes } from '@bitgo/sdk-lib-mpc'; -import { TestBitGo, TestBitGoAPI, mockSerializedChallengeWithProofs } from '@bitgo/sdk-test'; +import { mockSerializedChallengeWithProofs, TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test'; import { coins } from '@bitgo/statics'; import BigNumber from 'bignumber.js'; import { beforeEach } from 'mocha'; @@ -9,12 +9,12 @@ import sinon from 'sinon'; import { Sei, Tsei } from '../../src'; import utils from '../../src/lib/utils'; import { + address, TEST_DELEGATE_TX, TEST_SEND_TX, TEST_TX_WITH_MEMO, TEST_UNDELEGATE_TX, TEST_WITHDRAW_REWARDS_TX, - address, wrwUser, } from '../resources/sei'; import should = require('should'); @@ -265,7 +265,7 @@ describe('SEI', function () { amount: 'UNAVAILABLE', }, ], - outputAmount: 'UNAVAILABLE', + outputAmount: undefined, changeOutputs: [], changeAmount: '0', fee: { fee: TEST_WITHDRAW_REWARDS_TX.gasBudget.amount[0].amount }, diff --git a/modules/sdk-coin-tia/test/resources/tia.ts b/modules/sdk-coin-tia/test/resources/tia.ts index 5a62919b1f..564dc2ef87 100644 --- a/modules/sdk-coin-tia/test/resources/tia.ts +++ b/modules/sdk-coin-tia/test/resources/tia.ts @@ -44,6 +44,131 @@ export const TEST_SEND_TX = { }, }; +export const TEST_SEND_MANY_TX = { + hash: 'F72944EA53BA7795E8AB7B07232445946752DB8BFBB2CA01B9F7D8803AA5FAFB', + signature: 'w49jpRImzNo77zag0IdWyVK5iiUnt2TH4QmvNkiF8Kd6YKn0rVLaPmhJt7aJAGnX2pT8AyCNT4x45Aj0RVnYtg==', + pubKey: 'Aybzx++eXuqQDnbXAxMHEBIShkNdUN3YhD9M586N/SfA', + privateKey: 'c3J/YwqMZ8WhKSmmmHi7tNZuvoYyqdZMzAVIBK19IBE=', + signedTxBase64: + 'CqoCCpIBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnIKL2NlbGVzdGlhMXluM3Q4cXVqbXR4anNueDdhbmdqdWEzamh2a2p4eTVuNXhnczBuEi9jZWxlc3RpYTE1dmtldmhjODQ5Znh4YzVjcHYzNjByZzN0NTZoMGswcnlna2tueRoOCgR1dGlhEgYxMDAwMDAKkgEKHC9jb3Ntb3MuYmFuay52MWJldGExLk1zZ1NlbmQScgovY2VsZXN0aWExeW4zdDhxdWptdHhqc254N2FuZ2p1YTNqaHZranh5NW41eGdzMG4SL2NlbGVzdGlhMXBlNmx1ZDQ4cWV0bGg0ODYwdXZyazJyOTRhcW4wNjRqbjZqeHNrGg4KBHV0aWESBjEwMDAwMBJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDJvPH755e6pAOdtcDEwcQEhKGQ11Q3diEP0znzo39J8ASBAoCCAEYLRITCg0KBHV0aWESBTIwMDAwEMCaDBpAw49jpRImzNo77zag0IdWyVK5iiUnt2TH4QmvNkiF8Kd6YKn0rVLaPmhJt7aJAGnX2pT8AyCNT4x45Aj0RVnYtg==', + sender: 'cosmos1smefkq9yaxgw9c9fhym6qsp746q884czeacn32', + chainId: 'mocha-4', + accountNumber: 47998, + sequence: 45, + sendMessages: [ + { + typeUrl: '/cosmos.bank.v1beta1.MsgSend', + value: { + amount: [ + { + denom: 'utia', + amount: '100000', + }, + ], + toAddress: 'celestia15vkevhc849fxxc5cpv360rg3t56h0k0rygkkny', + fromAddress: 'celestia1yn3t8qujmtxjsnx7angjua3jhvkjxy5n5xgs0n', + }, + }, + { + typeUrl: '/cosmos.bank.v1beta1.MsgSend', + value: { + amount: [ + { + denom: 'utia', + amount: '100000', + }, + ], + toAddress: 'celestia1pe6lud48qetlh4860uvrk2r94aqn064jn6jxsk', + fromAddress: 'celestia1yn3t8qujmtxjsnx7angjua3jhvkjxy5n5xgs0n', + }, + }, + ], + gasBudget: { + amount: [{ denom: 'utia', amount: '20000' }], + gasLimit: 200000, + }, +}; + +export const TEST_SEND_MANY_STAKE_TX = { + hash: '41FDE39A6294A4AB3985CE073D6F91E53E5E48A10121A4AF1C816A1CCEAAFEF7', + signature: '+FTvI+MeURNUDE3Vd7AjjWxglBQPet4Hmwqn+kLwvCtpCnwrFkGIKh/ei+wZCrGGO8aRRy0HhZDB4QamOLYKzg==', + pubKey: 'Aybzx++eXuqQDnbXAxMHEBIShkNdUN3YhD9M586N/SfA', + privateKey: 'c3J/YwqMZ8WhKSmmmHi7tNZuvoYyqdZMzAVIBK19IBE=', + signedTxBase64: + 'CsMCCp4BCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJ3Ci9jZWxlc3RpYTF5bjN0OHF1am10eGpzbng3YW5nanVhM2podmtqeHk1bjV4Z3MwbhI2Y2VsZXN0aWF2YWxvcGVyMXU2NTV0Z3VsM3N1N3MwdTdreHloNm1kd2N5NXFuNnh3bDMyczBkGgwKBHV0aWESBDcwMDAKnwEKIy9jb3Ntb3Muc3Rha2luZy52MWJldGExLk1zZ0RlbGVnYXRlEngKL2NlbGVzdGlhMXluM3Q4cXVqbXR4anNueDdhbmdqdWEzamh2a2p4eTVuNXhnczBuEjZjZWxlc3RpYXZhbG9wZXIxOXVyZzlhd2p6d3E4ZDQwdndqZHZ2MHl3OWtnZWhzY2YwengzZ3MaDQoEdXRpYRIFMTEwMDASZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAybzx++eXuqQDnbXAxMHEBIShkNdUN3YhD9M586N/SfAEgQKAggBGDASEwoNCgR1dGlhEgUzMDAwMBDgpxIaQPhU7yPjHlETVAxN1XewI41sYJQUD3reB5sKp/pC8LwraQp8KxZBiCof3ovsGQqxhjvGkUctB4WQweEGpji2Cs4=', + chainId: 'mocha-4', + accountNumber: 47998, + sequence: 48, + sendMessages: [ + { + typeUrl: '/cosmos.staking.v1beta1.MsgDelegate', + value: { + amount: { + denom: 'utia', + amount: '7000', + }, + validatorAddress: 'celestiavaloper1u655tgul3su7s0u7kxyh6mdwcy5qn6xwl32s0d', + delegatorAddress: 'celestia1yn3t8qujmtxjsnx7angjua3jhvkjxy5n5xgs0n', + }, + }, + { + typeUrl: '/cosmos.staking.v1beta1.MsgDelegate', + value: { + amount: { + denom: 'utia', + amount: '11000', + }, + validatorAddress: 'celestiavaloper19urg9awjzwq8d40vwjdvv0yw9kgehscf0zx3gs', + delegatorAddress: 'celestia1yn3t8qujmtxjsnx7angjua3jhvkjxy5n5xgs0n', + }, + }, + ], + gasBudget: { + amount: [{ denom: 'utia', amount: '30000' }], + gasLimit: 300000, + }, +}; + +export const TEST_SEND_MANY_UNSTAKE_TX = { + hash: '11A81B3542A12F95CBBD6CA126A0A407C752DBFFA03884B7BE8DEE839A7C2D1D', + signature: 'Nq25TSajPGzmJHGXvbzzQcCFZ452cC8bew+07nxebGQ5VorJzerlY08rmxFhsVcKRGJhZet/f8s9C0OPyQQ3tA==', + pubKey: 'Aybzx++eXuqQDnbXAxMHEBIShkNdUN3YhD9M586N/SfA', + privateKey: 'c3J/YwqMZ8WhKSmmmHi7tNZuvoYyqdZMzAVIBK19IBE=', + signedTxBase64: + 'CsYCCqABCiUvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dVbmRlbGVnYXRlEncKL2NlbGVzdGlhMXluM3Q4cXVqbXR4anNueDdhbmdqdWEzamh2a2p4eTVuNXhnczBuEjZjZWxlc3RpYXZhbG9wZXIxdTY1NXRndWwzc3U3czB1N2t4eWg2bWR3Y3k1cW42eHdsMzJzMGQaDAoEdXRpYRIENTAwMAqgAQolL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnVW5kZWxlZ2F0ZRJ3Ci9jZWxlc3RpYTF5bjN0OHF1am10eGpzbng3YW5nanVhM2podmtqeHk1bjV4Z3MwbhI2Y2VsZXN0aWF2YWxvcGVyMTl1cmc5YXdqendxOGQ0MHZ3amR2djB5dzlrZ2Voc2NmMHp4M2dzGgwKBHV0aWESBDkwMDASZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAybzx++eXuqQDnbXAxMHEBIShkNdUN3YhD9M586N/SfAEgQKAggBGDISEwoNCgR1dGlhEgUzNTAwMBCwrhUaQDatuU0mozxs5iRxl72880HAhWeOdnAvG3sPtO58XmxkOVaKyc3q5WNPK5sRYbFXCkRiYWXrf3/LPQtDj8kEN7Q=', + chainId: 'mocha-4', + accountNumber: 47998, + sequence: 50, + sendMessages: [ + { + typeUrl: '/cosmos.staking.v1beta1.MsgUndelegate', + value: { + amount: { + denom: 'utia', + amount: '5000', + }, + validatorAddress: 'celestiavaloper1u655tgul3su7s0u7kxyh6mdwcy5qn6xwl32s0d', + delegatorAddress: 'celestia1yn3t8qujmtxjsnx7angjua3jhvkjxy5n5xgs0n', + }, + }, + { + typeUrl: '/cosmos.staking.v1beta1.MsgUndelegate', + value: { + amount: { + denom: 'utia', + amount: '9000', + }, + validatorAddress: 'celestiavaloper19urg9awjzwq8d40vwjdvv0yw9kgehscf0zx3gs', + delegatorAddress: 'celestia1yn3t8qujmtxjsnx7angjua3jhvkjxy5n5xgs0n', + }, + }, + ], + gasBudget: { + amount: [{ denom: 'utia', amount: '35000' }], + gasLimit: 350000, + }, +}; + export const TEST_DELEGATE_TX = { hash: '205B08B8148C27BA290945FA1A1110FE8F6A60F023D5D765E9C5EF61FD1FFDC6', signature: '7t7e7cuzamTzcy/8/3phcZqGvmialvRTAg4Zg4bAS5Eqki+rhHZ3K6wmfsDbWDbhMdiL0bQyt9maX8nEKMY1DA==', diff --git a/modules/sdk-coin-tia/test/unit/tia.ts b/modules/sdk-coin-tia/test/unit/tia.ts index 57550e8885..e41e4d521c 100644 --- a/modules/sdk-coin-tia/test/unit/tia.ts +++ b/modules/sdk-coin-tia/test/unit/tia.ts @@ -1,7 +1,7 @@ import { CosmosTransaction, SendMessage } from '@bitgo/abstract-cosmos'; import { BitGoAPI } from '@bitgo/sdk-api'; import { EcdsaRangeProof, EcdsaTypes } from '@bitgo/sdk-lib-mpc'; -import { TestBitGo, TestBitGoAPI, mockSerializedChallengeWithProofs } from '@bitgo/sdk-test'; +import { mockSerializedChallengeWithProofs, TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test'; import { coins } from '@bitgo/statics'; import BigNumber from 'bignumber.js'; import { beforeEach } from 'mocha'; @@ -9,13 +9,16 @@ import sinon from 'sinon'; import { Tia, Ttia } from '../../src'; import utils from '../../src/lib/utils'; import { + address, + mockAccountDetailsResponse, TEST_DELEGATE_TX, + TEST_SEND_MANY_STAKE_TX, + TEST_SEND_MANY_TX, + TEST_SEND_MANY_UNSTAKE_TX, TEST_SEND_TX, TEST_TX_WITH_MEMO, TEST_UNDELEGATE_TX, TEST_WITHDRAW_REWARDS_TX, - address, - mockAccountDetailsResponse, wrwUser, } from '../resources/tia'; import should = require('should'); @@ -124,6 +127,28 @@ describe('TIA', function () { isTransactionVerified.should.equal(true); }); + it('should succeed to verify sendMany transaction', async function () { + const txPrebuild = { + txHex: TEST_SEND_MANY_TX.signedTxBase64, + txInfo: {}, + }; + const txParams = { + recipients: [ + { + address: 'celestia15vkevhc849fxxc5cpv360rg3t56h0k0rygkkny', + amount: '100000', + }, + { + address: 'celestia1pe6lud48qetlh4860uvrk2r94aqn064jn6jxsk', + amount: '100000', + }, + ], + }; + const verification = {}; + const isTransactionVerified = await basecoin.verifyTransaction({ txParams, txPrebuild, verification }); + isTransactionVerified.should.equal(true); + }); + it('should succeed to verify delegate transaction', async function () { const txPrebuild = { txHex: TEST_DELEGATE_TX.signedTxBase64, @@ -212,6 +237,81 @@ describe('TIA', function () { }); }); + it('should explain sendMany transfer transaction', async function () { + const explainedTransaction = await basecoin.explainTransaction({ + txHex: TEST_SEND_MANY_TX.signedTxBase64, + }); + explainedTransaction.should.deepEqual({ + displayOrder: ['id', 'outputs', 'outputAmount', 'changeOutputs', 'changeAmount', 'fee', 'type'], + id: TEST_SEND_MANY_TX.hash, + outputs: [ + { + address: 'celestia15vkevhc849fxxc5cpv360rg3t56h0k0rygkkny', + amount: '100000', + }, + { + address: 'celestia1pe6lud48qetlh4860uvrk2r94aqn064jn6jxsk', + amount: '100000', + }, + ], + outputAmount: '200000', + changeOutputs: [], + changeAmount: '0', + fee: { fee: TEST_SEND_MANY_TX.gasBudget.amount[0].amount }, + type: 0, + }); + }); + + it('should explain sendMany stake transaction', async function () { + const explainedTransaction = await basecoin.explainTransaction({ + txHex: TEST_SEND_MANY_STAKE_TX.signedTxBase64, + }); + explainedTransaction.should.deepEqual({ + displayOrder: ['id', 'outputs', 'outputAmount', 'changeOutputs', 'changeAmount', 'fee', 'type'], + id: TEST_SEND_MANY_STAKE_TX.hash, + outputs: [ + { + address: 'celestiavaloper1u655tgul3su7s0u7kxyh6mdwcy5qn6xwl32s0d', + amount: '7000', + }, + { + address: 'celestiavaloper19urg9awjzwq8d40vwjdvv0yw9kgehscf0zx3gs', + amount: '11000', + }, + ], + outputAmount: '18000', + changeOutputs: [], + changeAmount: '0', + fee: { fee: TEST_SEND_MANY_STAKE_TX.gasBudget.amount[0].amount }, + type: 13, + }); + }); + + it('should explain sendMany unstake transaction', async function () { + const explainedTransaction = await basecoin.explainTransaction({ + txHex: TEST_SEND_MANY_UNSTAKE_TX.signedTxBase64, + }); + explainedTransaction.should.deepEqual({ + displayOrder: ['id', 'outputs', 'outputAmount', 'changeOutputs', 'changeAmount', 'fee', 'type'], + id: TEST_SEND_MANY_UNSTAKE_TX.hash, + outputs: [ + { + address: 'celestiavaloper1u655tgul3su7s0u7kxyh6mdwcy5qn6xwl32s0d', + amount: '5000', + }, + { + address: 'celestiavaloper19urg9awjzwq8d40vwjdvv0yw9kgehscf0zx3gs', + amount: '9000', + }, + ], + outputAmount: '14000', + changeOutputs: [], + changeAmount: '0', + fee: { fee: TEST_SEND_MANY_UNSTAKE_TX.gasBudget.amount[0].amount }, + type: 17, + }); + }); + it('should explain a delegate transaction', async function () { const explainedTransaction = await basecoin.explainTransaction({ txHex: TEST_DELEGATE_TX.signedTxBase64, @@ -267,7 +367,7 @@ describe('TIA', function () { amount: 'UNAVAILABLE', }, ], - outputAmount: 'UNAVAILABLE', + outputAmount: undefined, changeOutputs: [], changeAmount: '0', fee: { fee: TEST_WITHDRAW_REWARDS_TX.gasBudget.amount[0].amount }, diff --git a/modules/sdk-coin-tia/test/unit/transactionBuilder/StakingActivateBuilder.ts b/modules/sdk-coin-tia/test/unit/transactionBuilder/StakingActivateBuilder.ts index e22e5c1992..e67fade59d 100644 --- a/modules/sdk-coin-tia/test/unit/transactionBuilder/StakingActivateBuilder.ts +++ b/modules/sdk-coin-tia/test/unit/transactionBuilder/StakingActivateBuilder.ts @@ -118,4 +118,51 @@ describe('Tia Delegate txn Builder', () => { }, ]); }); + + it('should build a sendMany stake tx', async function () { + const testSendManyTx = testData.TEST_SEND_MANY_STAKE_TX; + const txBuilder = factory.getStakingActivateBuilder(); + txBuilder.sequence(testSendManyTx.sequence); + txBuilder.gasBudget(testSendManyTx.gasBudget); + txBuilder.messages(testSendManyTx.sendMessages.map((msg) => msg.value)); + txBuilder.publicKey(toHex(fromBase64(testSendManyTx.pubKey))); + txBuilder.chainId(testSendManyTx.chainId); + txBuilder.accountNumber(testSendManyTx.accountNumber); + txBuilder.memo(''); + txBuilder.addSignature( + { pub: toHex(fromBase64(testSendManyTx.pubKey)) }, + Buffer.from(testSendManyTx.signature, 'base64') + ); + + const tx = await txBuilder.build(); + const json = await (await txBuilder.build()).toJson(); + should.equal(tx.type, TransactionType.StakingActivate); + should.deepEqual(json.gasBudget, testSendManyTx.gasBudget); + should.deepEqual(json.sendMessages, testSendManyTx.sendMessages); + should.deepEqual(json.publicKey, toHex(fromBase64(testSendManyTx.pubKey))); + should.deepEqual(json.sequence, testSendManyTx.sequence); + should.deepEqual( + tx.inputs, + testSendManyTx.sendMessages.map((msg) => { + return { + address: msg.value.delegatorAddress, + value: msg.value.amount.amount, + coin: 'ttia', + }; + }) + ); + should.deepEqual( + tx.outputs, + testSendManyTx.sendMessages.map((msg) => { + return { + address: msg.value.validatorAddress, + value: msg.value.amount.amount, + coin: 'ttia', + }; + }) + ); + should.equal(tx.id, testSendManyTx.hash); + const rawTx = tx.toBroadcastFormat(); + should.equal(rawTx, testSendManyTx.signedTxBase64); + }); }); diff --git a/modules/sdk-coin-tia/test/unit/transactionBuilder/StakingDeactivateBuilder.ts b/modules/sdk-coin-tia/test/unit/transactionBuilder/StakingDeactivateBuilder.ts index 3b58118a26..f79f4f70fa 100644 --- a/modules/sdk-coin-tia/test/unit/transactionBuilder/StakingDeactivateBuilder.ts +++ b/modules/sdk-coin-tia/test/unit/transactionBuilder/StakingDeactivateBuilder.ts @@ -116,4 +116,51 @@ describe('Tia Undelegate txn Builder', () => { }, ]); }); + + it('should build a sendMany unstake tx', async function () { + const testSendManyTx = testData.TEST_SEND_MANY_UNSTAKE_TX; + const txBuilder = factory.getStakingDeactivateBuilder(); + txBuilder.sequence(testSendManyTx.sequence); + txBuilder.gasBudget(testSendManyTx.gasBudget); + txBuilder.messages(testSendManyTx.sendMessages.map((msg) => msg.value)); + txBuilder.publicKey(toHex(fromBase64(testSendManyTx.pubKey))); + txBuilder.chainId(testSendManyTx.chainId); + txBuilder.accountNumber(testSendManyTx.accountNumber); + txBuilder.memo(''); + txBuilder.addSignature( + { pub: toHex(fromBase64(testSendManyTx.pubKey)) }, + Buffer.from(testSendManyTx.signature, 'base64') + ); + + const tx = await txBuilder.build(); + const json = await (await txBuilder.build()).toJson(); + should.equal(tx.type, TransactionType.StakingDeactivate); + should.deepEqual(json.gasBudget, testSendManyTx.gasBudget); + should.deepEqual(json.sendMessages, testSendManyTx.sendMessages); + should.deepEqual(json.publicKey, toHex(fromBase64(testSendManyTx.pubKey))); + should.deepEqual(json.sequence, testSendManyTx.sequence); + should.deepEqual( + tx.inputs, + testSendManyTx.sendMessages.map((msg) => { + return { + address: msg.value.delegatorAddress, + value: msg.value.amount.amount, + coin: 'ttia', + }; + }) + ); + should.deepEqual( + tx.outputs, + testSendManyTx.sendMessages.map((msg) => { + return { + address: msg.value.validatorAddress, + value: msg.value.amount.amount, + coin: 'ttia', + }; + }) + ); + should.equal(tx.id, testSendManyTx.hash); + const rawTx = tx.toBroadcastFormat(); + should.equal(rawTx, testSendManyTx.signedTxBase64); + }); }); diff --git a/modules/sdk-coin-tia/test/unit/transactionBuilder/transferBuilder.ts b/modules/sdk-coin-tia/test/unit/transactionBuilder/transferBuilder.ts index c80c6fe271..2adf8650d6 100644 --- a/modules/sdk-coin-tia/test/unit/transactionBuilder/transferBuilder.ts +++ b/modules/sdk-coin-tia/test/unit/transactionBuilder/transferBuilder.ts @@ -157,4 +157,50 @@ describe('Tia Transfer Builder', () => { }, ]); }); + + it('should build a sendMany Transfer tx', async function () { + const testSendManyTx = testData.TEST_SEND_MANY_TX; + const txBuilder = factory.getTransferBuilder(); + txBuilder.sequence(testSendManyTx.sequence); + txBuilder.gasBudget(testSendManyTx.gasBudget); + txBuilder.messages(testSendManyTx.sendMessages.map((msg) => msg.value)); + txBuilder.publicKey(toHex(fromBase64(testSendManyTx.pubKey))); + txBuilder.chainId(testSendManyTx.chainId); + txBuilder.accountNumber(testSendManyTx.accountNumber); + txBuilder.addSignature( + { pub: toHex(fromBase64(testSendManyTx.pubKey)) }, + Buffer.from(testSendManyTx.signature, 'base64') + ); + + const tx = await txBuilder.build(); + const json = await (await txBuilder.build()).toJson(); + should.equal(tx.type, TransactionType.Send); + should.deepEqual(json.gasBudget, testSendManyTx.gasBudget); + should.deepEqual(json.sendMessages, testSendManyTx.sendMessages); + should.deepEqual(json.publicKey, toHex(fromBase64(testSendManyTx.pubKey))); + should.deepEqual(json.sequence, testSendManyTx.sequence); + should.deepEqual( + tx.inputs, + testSendManyTx.sendMessages.map((msg) => { + return { + address: msg.value.fromAddress, + value: msg.value.amount[0].amount, + coin: 'ttia', + }; + }) + ); + should.deepEqual( + tx.outputs, + testSendManyTx.sendMessages.map((msg) => { + return { + address: msg.value.toAddress, + value: msg.value.amount[0].amount, + coin: 'ttia', + }; + }) + ); + should.equal(tx.id, testSendManyTx.hash); + const rawTx = tx.toBroadcastFormat(); + should.equal(rawTx, testSendManyTx.signedTxBase64); + }); }); diff --git a/modules/sdk-coin-zeta/test/unit/zeta.ts b/modules/sdk-coin-zeta/test/unit/zeta.ts index deb5c8dbcb..2ca268663d 100644 --- a/modules/sdk-coin-zeta/test/unit/zeta.ts +++ b/modules/sdk-coin-zeta/test/unit/zeta.ts @@ -1,25 +1,25 @@ +import { CosmosTransaction, SendMessage } from '@bitgo/abstract-cosmos'; import { BitGoAPI } from '@bitgo/sdk-api'; +import { EcdsaRangeProof, EcdsaTypes } from '@bitgo/sdk-lib-mpc'; import { mockSerializedChallengeWithProofs, TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test'; +import { coins } from '@bitgo/statics'; import BigNumber from 'bignumber.js'; +import { beforeEach } from 'mocha'; import sinon from 'sinon'; import { Tzeta, Zeta } from '../../src'; +import { GAS_AMOUNT } from '../../src/lib/constants'; import utils from '../../src/lib/utils'; import { + address, + mockAccountDetailsResponse, TEST_DELEGATE_TX, TEST_SEND_TX, TEST_TX_WITH_MEMO, TEST_UNDELEGATE_TX, TEST_WITHDRAW_REWARDS_TX, - address, wrwUser, - mockAccountDetailsResponse, } from '../resources/zeta'; import should = require('should'); -import { coins } from '@bitgo/statics'; -import { beforeEach } from 'mocha'; -import { EcdsaRangeProof, EcdsaTypes } from '@bitgo/sdk-lib-mpc'; -import { CosmosTransaction, SendMessage } from '@bitgo/abstract-cosmos'; -import { GAS_AMOUNT } from '../../src/lib/constants'; import nock = require('nock'); describe('Zeta', function () { @@ -268,7 +268,7 @@ describe('Zeta', function () { amount: 'UNAVAILABLE', }, ], - outputAmount: 'UNAVAILABLE', + outputAmount: undefined, changeOutputs: [], changeAmount: '0', fee: { fee: TEST_WITHDRAW_REWARDS_TX.gasBudget.amount[0].amount },