From 278726309816876c357180f04ca04d08fd4ee82d Mon Sep 17 00:00:00 2001 From: Alok Baltiyal Date: Sat, 25 Jan 2025 22:04:59 +0530 Subject: [PATCH] feat(sdk-coin-apt): legacy coin and aptos coin transfer changes TICKET: COIN-2894 --- modules/sdk-coin-apt/src/lib/constants.ts | 6 +- .../transaction/fungibleAssetTransaction.ts | 8 +- .../lib/transaction/transferTransaction.ts | 15 +- .../fungibleAssetTransactionBuilder.ts | 10 +- .../transactionBuilder/transactionBuilder.ts | 6 +- .../lib/transactionBuilder/transferBuilder.ts | 21 +- modules/sdk-coin-apt/src/lib/utils.ts | 2 +- modules/sdk-coin-apt/test/resources/apt.ts | 5 + .../transactionBuilder/transferBuilder.ts | 239 ++++++++++++------ 9 files changed, 220 insertions(+), 92 deletions(-) diff --git a/modules/sdk-coin-apt/src/lib/constants.ts b/modules/sdk-coin-apt/src/lib/constants.ts index 425b90a2da..c58a4b5af1 100644 --- a/modules/sdk-coin-apt/src/lib/constants.ts +++ b/modules/sdk-coin-apt/src/lib/constants.ts @@ -9,6 +9,8 @@ export const SECONDS_PER_WEEK = 7 * 24 * 60 * 60; // Days * Hours * Minutes * Se export const APTOS_ACCOUNT_MODULE = 'aptos_account'; export const FUNGIBLE_ASSET_MODULE = 'primary_fungible_store'; -export const FUNGIBLE_ASSET_FUNCTION = '0x1::primary_fungible_store::transfer'; +export const FUNGIBLE_ASSET_TRANSFER_FUNCTION = '0x1::primary_fungible_store::transfer'; +export const COIN_TRANSFER_FUNCTION = '0x1::aptos_account::transfer_coins'; -export const FUNGIBLE_ASSET = '0x1::fungible_asset::Metadata'; +export const FUNGIBLE_ASSET_TYPE_ARGUMENT = '0x1::fungible_asset::Metadata'; +export const APTOS_COIN = '0x1::aptos_coin::AptosCoin'; diff --git a/modules/sdk-coin-apt/src/lib/transaction/fungibleAssetTransaction.ts b/modules/sdk-coin-apt/src/lib/transaction/fungibleAssetTransaction.ts index 310a0e0b7c..adffc7e99c 100644 --- a/modules/sdk-coin-apt/src/lib/transaction/fungibleAssetTransaction.ts +++ b/modules/sdk-coin-apt/src/lib/transaction/fungibleAssetTransaction.ts @@ -13,7 +13,7 @@ import { } from '@aptos-labs/ts-sdk'; import { InvalidTransactionError, TransactionRecipient, TransactionType } from '@bitgo/sdk-core'; import { BaseCoin as CoinConfig, NetworkType } from '@bitgo/statics'; -import { FUNGIBLE_ASSET, FUNGIBLE_ASSET_FUNCTION } from '../constants'; +import { FUNGIBLE_ASSET_TYPE_ARGUMENT, FUNGIBLE_ASSET_TRANSFER_FUNCTION } from '../constants'; export class FungibleAssetTransaction extends Transaction { constructor(coinConfig: Readonly) { @@ -26,7 +26,7 @@ export class FungibleAssetTransaction extends Transaction { !(payload instanceof TransactionPayloadEntryFunction) || payload.entryFunction.args.length !== 3 || payload.entryFunction.type_args.length !== 1 || - FUNGIBLE_ASSET !== payload.entryFunction.type_args[0].toString() + FUNGIBLE_ASSET_TYPE_ARGUMENT !== payload.entryFunction.type_args[0].toString() ) { throw new InvalidTransactionError('Invalid transaction payload'); } @@ -55,8 +55,8 @@ export class FungibleAssetTransaction extends Transaction { const simpleTxn = await aptos.transaction.build.simple({ sender: senderAddress, data: { - function: FUNGIBLE_ASSET_FUNCTION, - typeArguments: [FUNGIBLE_ASSET], + function: FUNGIBLE_ASSET_TRANSFER_FUNCTION, + typeArguments: [FUNGIBLE_ASSET_TYPE_ARGUMENT], functionArguments: [fungibleTokenAddress, recipientAddress, this.recipient.amount], abi: faTransferAbi, }, diff --git a/modules/sdk-coin-apt/src/lib/transaction/transferTransaction.ts b/modules/sdk-coin-apt/src/lib/transaction/transferTransaction.ts index 62a0fc4664..64b7ad551c 100644 --- a/modules/sdk-coin-apt/src/lib/transaction/transferTransaction.ts +++ b/modules/sdk-coin-apt/src/lib/transaction/transferTransaction.ts @@ -10,21 +10,29 @@ import { } from '@aptos-labs/ts-sdk'; import { BaseCoin as CoinConfig, NetworkType } from '@bitgo/statics'; +import { APTOS_COIN, COIN_TRANSFER_FUNCTION } from '../constants'; export class TransferTransaction extends Transaction { constructor(coinConfig: Readonly) { super(coinConfig); this._type = TransactionType.Send; + this._assetId = APTOS_COIN; } protected parseTransactionPayload(payload: TransactionPayload): void { - if (!(payload instanceof TransactionPayloadEntryFunction)) { + if ( + !(payload instanceof TransactionPayloadEntryFunction) || + payload.entryFunction.args.length !== 2 || + payload.entryFunction.type_args.length !== 1 || + payload.entryFunction.type_args[0].toString().length === 0 + ) { throw new InvalidTransactionError('Invalid transaction payload'); } const entryFunction = payload.entryFunction; if (!this._recipient) { this._recipient = {} as TransactionRecipient; } + this._assetId = entryFunction.type_args[0].toString(); this._recipient.address = entryFunction.args[0].toString(); const amountBuffer = Buffer.from(entryFunction.args[1].bcsToBytes()); this._recipient.amount = amountBuffer.readBigUint64LE().toString(); @@ -35,12 +43,11 @@ export class TransferTransaction extends Transaction { const aptos = new Aptos(new AptosConfig({ network })); const senderAddress = AccountAddress.fromString(this._sender); const recipientAddress = AccountAddress.fromString(this._recipient.address); - const simpleTxn = await aptos.transaction.build.simple({ sender: senderAddress, data: { - function: '0x1::aptos_account::transfer_coins', - typeArguments: ['0x1::aptos_coin::AptosCoin'], + function: COIN_TRANSFER_FUNCTION, + typeArguments: [this.assetId], functionArguments: [recipientAddress, this.recipient.amount], }, options: { diff --git a/modules/sdk-coin-apt/src/lib/transactionBuilder/fungibleAssetTransactionBuilder.ts b/modules/sdk-coin-apt/src/lib/transactionBuilder/fungibleAssetTransactionBuilder.ts index cfc694411b..70a58876df 100644 --- a/modules/sdk-coin-apt/src/lib/transactionBuilder/fungibleAssetTransactionBuilder.ts +++ b/modules/sdk-coin-apt/src/lib/transactionBuilder/fungibleAssetTransactionBuilder.ts @@ -5,7 +5,7 @@ import { TransactionType } from '@bitgo/sdk-core'; import BigNumber from 'bignumber.js'; import utils from '../utils'; import { TransactionPayload, TransactionPayloadEntryFunction } from '@aptos-labs/ts-sdk'; -import { FUNGIBLE_ASSET } from '../constants'; +import { FUNGIBLE_ASSET_TYPE_ARGUMENT } from '../constants'; export class FungibleAssetTransactionBuilder extends TransactionBuilder { constructor(_coinConfig: Readonly) { @@ -16,6 +16,12 @@ export class FungibleAssetTransactionBuilder extends TransactionBuilder { protected get transactionType(): TransactionType { return TransactionType.SendToken; } + //TODO: Ticket: COIN-2941 : Check Statics based asset validation and if possible, implement it + assetId(assetId: string): TransactionBuilder { + this.validateAddress({ address: assetId }); + this.transaction.assetId = assetId; + return this; + } /** @inheritdoc */ validateTransaction(transaction?: FungibleAssetTransaction): void { @@ -32,7 +38,7 @@ export class FungibleAssetTransactionBuilder extends TransactionBuilder { !(payload instanceof TransactionPayloadEntryFunction) || payload.entryFunction.args.length !== 3 || payload.entryFunction.type_args.length !== 1 || - FUNGIBLE_ASSET !== payload.entryFunction.type_args[0].toString() + FUNGIBLE_ASSET_TYPE_ARGUMENT !== payload.entryFunction.type_args[0].toString() ) { console.error('invalid transaction payload'); return false; diff --git a/modules/sdk-coin-apt/src/lib/transactionBuilder/transactionBuilder.ts b/modules/sdk-coin-apt/src/lib/transactionBuilder/transactionBuilder.ts index 15f082c43d..f756f2dfe1 100644 --- a/modules/sdk-coin-apt/src/lib/transactionBuilder/transactionBuilder.ts +++ b/modules/sdk-coin-apt/src/lib/transactionBuilder/transactionBuilder.ts @@ -84,11 +84,7 @@ export abstract class TransactionBuilder extends BaseTransactionBuilder { return this; } - assetId(assetId: string): TransactionBuilder { - this.validateAddress({ address: assetId }); - this.transaction.assetId = assetId; - return this; - } + abstract assetId(assetId: string): TransactionBuilder; /** @inheritdoc */ protected signImplementation(key: BaseKey): Transaction { diff --git a/modules/sdk-coin-apt/src/lib/transactionBuilder/transferBuilder.ts b/modules/sdk-coin-apt/src/lib/transactionBuilder/transferBuilder.ts index c35b769ede..aa20a3a22d 100644 --- a/modules/sdk-coin-apt/src/lib/transactionBuilder/transferBuilder.ts +++ b/modules/sdk-coin-apt/src/lib/transactionBuilder/transferBuilder.ts @@ -16,13 +16,32 @@ export class TransferBuilder extends TransactionBuilder { return TransactionType.Send; } + validatePartsOfAssetId(assetId: string): void { + const parts = assetId.split('::'); + if (parts.length !== 3 || !utils.isValidAddress(parts[0])) { + throw new Error('Invalid asset ID'); + } + } + + assetId(assetId: string): TransactionBuilder { + this.validatePartsOfAssetId(assetId); + this.transaction.assetId = assetId; + return this; + } + protected isValidTransactionPayload(payload: TransactionPayload) { try { - if (!(payload instanceof TransactionPayloadEntryFunction)) { + if ( + !(payload instanceof TransactionPayloadEntryFunction) || + payload.entryFunction.args.length !== 2 || + payload.entryFunction.type_args.length !== 1 || + payload.entryFunction.type_args[0].toString().length === 0 + ) { console.error('invalid transaction payload'); return false; } const entryFunction = payload.entryFunction; + this.validatePartsOfAssetId(entryFunction.type_args[0].toString()); const recipientAddress = entryFunction.args[0].toString(); const amountBuffer = Buffer.from(entryFunction.args[1].bcsToBytes()); const recipientAmount = new BigNumber(amountBuffer.readBigUint64LE().toString()); diff --git a/modules/sdk-coin-apt/src/lib/utils.ts b/modules/sdk-coin-apt/src/lib/utils.ts index dab44d37fe..6a4a111347 100644 --- a/modules/sdk-coin-apt/src/lib/utils.ts +++ b/modules/sdk-coin-apt/src/lib/utils.ts @@ -72,7 +72,7 @@ export class Utils implements BaseUtils { throw new Error('Invalid Payload: Expected TransactionPayloadEntryFunction'); } const entryFunction = payload.entryFunction; - const moduleIdentifier = entryFunction.module_name.name.identifier.trim(); + const moduleIdentifier = entryFunction.module_name.name.identifier; switch (moduleIdentifier) { case APTOS_ACCOUNT_MODULE: return TransactionType.Send; diff --git a/modules/sdk-coin-apt/test/resources/apt.ts b/modules/sdk-coin-apt/test/resources/apt.ts index 97a29ddb68..ca2f9ebff1 100644 --- a/modules/sdk-coin-apt/test/resources/apt.ts +++ b/modules/sdk-coin-apt/test/resources/apt.ts @@ -64,6 +64,9 @@ export const invalidRecipients: Recipient[] = [ export const TRANSFER = '0x1aed808916ab9b1b30b07abb53561afd46847285ce28651221d406173a37244992000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e73010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220f7405c28a02cf5bab4ea4498240bb3579db45951794eb1c843bef0534c093ad908e803000000000000400d0300000000006400000000000000979390670000000002030020f73836f42257240e43d439552471fc9dbcc3f1af5bd0b4ed83f44b5f6614644240caeb90efd4b7ecd922c97bb3163e6a9de1fbb2ee0fc0d56af484f4af9b0015c5831341550af29b3686713b6657c821d894635fe13c7933f06ee043728f040b090000dbc87a1c816d9bcd06b683c37e80c7162e4d48da7812198b830e4d5d8e0629f200205223396c531f13e031a9f0cb26d459d799a52e51be9a1cb9e871afb4c31f91ff4013e7e8a1325ee5f656c93baa3d0206a1d9bd6da5abdc6f5d9b8bbbb0926ddac68f3e57a915dd217d2d43e776a6cc01af72f895ea712acc836d30349f29a3c606'; +export const TRANSACTION_USING_TRANSFER_COINS = + '0x1aed808916ab9b1b30b07abb53561afd46847285ce28651221d406173a37244992000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e73010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220f7405c28a02cf5bab4ea4498240bb3579db45951794eb1c843bef0534c093ad908e803000000000000400d0300000000006400000000000000979390670000000002030020f73836f42257240e43d439552471fc9dbcc3f1af5bd0b4ed83f44b5f6614644240caeb90efd4b7ecd922c97bb3163e6a9de1fbb2ee0fc0d56af484f4af9b0015c5831341550af29b3686713b6657c821d894635fe13c7933f06ee043728f040b090000dbc87a1c816d9bcd06b683c37e80c7162e4d48da7812198b830e4d5d8e0629f200205223396c531f13e031a9f0cb26d459d799a52e51be9a1cb9e871afb4c31f91ff4013e7e8a1325ee5f656c93baa3d0206a1d9bd6da5abdc6f5d9b8bbbb0926ddac68f3e57a915dd217d2d43e776a6cc01af72f895ea712acc836d30349f29a3c606'; + export const INVALID_TRANSFER = 'AAAAAAAAAAAAA6e7361637469bc4a58e500b9e64cb6547ee9b403000000000000002064ba1fb2f2fbd2938a350015d601f4db89cd7e8e2370d0dd9ae3ac4f635c1581111b8a49f67370bc4a58e500b9e64cb6462e39b802000000000000002064ba1fb2f2fbd2938a350015d601f4db89cd7e8e2370d0dd9ae3ac47aa1ff81f01c4173a804406a365e69dfb297d4eaaf002546ebd016400000000000000cba4a48bb0f8b586c167e5dcefaa1c5e96ab3f0836d6ca08f2081732944d1e5b6b406a4a462e39b8030000000000000020b9490ede63215262c434e03f606d9799f3ba704523ceda184b386d47aa1ff81f01000000000000006400000000000000'; @@ -73,3 +76,5 @@ export const fungibleTokenAddress = { export const FUNGIBLE_TOKEN_TRANSFER = '0x1aed808916ab9b1b30b07abb53561afd46847285ce28651221d406173a372449a700000000000000020000000000000000000000000000000000000000000000000000000000000001167072696d6172795f66756e6769626c655f73746f7265087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010e66756e6769626c655f6173736574084d65746164617461000320d5d0d561493ea2b9410f67da804653ae44e793c2423707d4f11edb2e3819205020f7405c28a02cf5bab4ea4498240bb3579db45951794eb1c843bef0534c093ad9080100000000000000400d0300000000006400000000000000e42696670000000002030020f73836f42257240e43d439552471fc9dbcc3f1af5bd0b4ed83f44b5f661464424029665cd4c94658a0d83907bbed7e761794b25bccc03fc87e6dd63a543accdddfd7a6f1e7a15e8681547ca437ff99b58c92f816e35a0f333d7f1fd1330c21ad0a0000dbc87a1c816d9bcd06b683c37e80c7162e4d48da7812198b830e4d5d8e0629f200205223396c531f13e031a9f0cb26d459d799a52e51be9a1cb9e871afb4c31f91ff40de7b0bb86ca346031942e9cf21ff9604c7c08c2c662e38a0afe3dd640512c0441396c0697cd8bbbcf39694d6f88e35f6bed9fb34bd209b0479b5e8bd0cf3eb0b'; + +export const LEGACY_COIN = '0x4fb379c10c763a13e724064ecfb7d946690bea519ba982c81b518d1c11dd23fe::fa_test::Coinz'; diff --git a/modules/sdk-coin-apt/test/unit/transactionBuilder/transferBuilder.ts b/modules/sdk-coin-apt/test/unit/transactionBuilder/transferBuilder.ts index 2aaa04a322..cb12758d38 100644 --- a/modules/sdk-coin-apt/test/unit/transactionBuilder/transferBuilder.ts +++ b/modules/sdk-coin-apt/test/unit/transactionBuilder/transferBuilder.ts @@ -7,8 +7,165 @@ import should from 'should'; describe('Apt Transfer Transaction', () => { const factory = new TransactionBuilderFactory(coins.get('tapt')); - describe('Succeed', () => { - it('should build a transfer tx', async function () { + describe('Aptos Coin Transfer Transaction', () => { + describe('Succeed', () => { + it('should build a transfer tx', async function () { + const transaction = new TransferTransaction(coins.get('tapt')); + const txBuilder = factory.getTransferBuilder(transaction); + txBuilder.sender(testData.sender2.address); + txBuilder.recipient(testData.recipients[0]); + txBuilder.gasData({ + maxGasAmount: 200000, + gasUnitPrice: 100, + }); + txBuilder.sequenceNumber(14); + txBuilder.expirationTime(1736246155); + txBuilder.addFeePayerAddress(testData.feePayer.address); + const tx = (await txBuilder.build()) as TransferTransaction; + should.equal(tx.sender, testData.sender2.address); + should.equal(tx.recipient.address, testData.recipients[0].address); + should.equal(tx.recipient.amount, testData.recipients[0].amount); + should.equal(tx.maxGasAmount, 200000); + should.equal(tx.gasUnitPrice, 100); + should.equal(tx.sequenceNumber, 14); + should.equal(tx.expirationTime, 1736246155); + should.equal(tx.type, TransactionType.Send); + tx.inputs.length.should.equal(1); + tx.inputs[0].should.deepEqual({ + address: testData.sender2.address, + value: testData.recipients[0].amount, + coin: 'tapt', + }); + tx.outputs.length.should.equal(1); + tx.outputs[0].should.deepEqual({ + address: testData.recipients[0].address, + value: testData.recipients[0].amount, + coin: 'tapt', + }); + const rawTx = tx.toBroadcastFormat(); + should.equal(txBuilder.isValidRawTransaction(rawTx), true); + rawTx.should.equal( + '0x1aed808916ab9b1b30b07abb53561afd46847285ce28651221d406173a3724490e000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e73010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220f7405c28a02cf5bab4ea4498240bb3579db45951794eb1c843bef0534c093ad908e803000000000000400d03000000000064000000000000008b037d670000000002030020000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dbc87a1c816d9bcd06b683c37e80c7162e4d48da7812198b830e4d5d8e0629f2002000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + ); + }); + + it('should build and send a signed tx', async function () { + const txBuilder = factory.from(testData.TRANSACTION_USING_TRANSFER_COINS); + const tx = (await txBuilder.build()) as TransferTransaction; + should.equal(tx.type, TransactionType.Send); + tx.inputs.length.should.equal(1); + tx.inputs[0].should.deepEqual({ + address: testData.sender2.address, + value: testData.recipients[0].amount, + coin: 'tapt', + }); + tx.outputs.length.should.equal(1); + tx.outputs[0].should.deepEqual({ + address: testData.recipients[0].address, + value: testData.recipients[0].amount, + coin: 'tapt', + }); + should.equal(tx.id, '0x80a52dd5d4f712a80b77ad7b4a12a8e61b76243a546099b0ab9acfef4e9a4e31'); + should.equal(tx.maxGasAmount, 200000); + should.equal(tx.gasUnitPrice, 100); + should.equal(tx.sequenceNumber, 146); + should.equal(tx.expirationTime, 1737528215); + should.equal(tx.type, TransactionType.Send); + const rawTx = tx.toBroadcastFormat(); + should.equal(txBuilder.isValidRawTransaction(rawTx), true); + should.equal(rawTx, testData.TRANSACTION_USING_TRANSFER_COINS); + }); + + it('should succeed to validate a valid signablePayload', async function () { + const transaction = new TransferTransaction(coins.get('tapt')); + const txBuilder = factory.getTransferBuilder(transaction); + txBuilder.sender(testData.sender2.address); + txBuilder.recipient(testData.recipients[0]); + txBuilder.gasData({ + maxGasAmount: 200000, + gasUnitPrice: 100, + }); + txBuilder.sequenceNumber(14); + txBuilder.expirationTime(1736246155); + txBuilder.addFeePayerAddress(testData.feePayer.address); + const tx = (await txBuilder.build()) as TransferTransaction; + const signablePayload = tx.signablePayload; + should.equal( + signablePayload.toString('hex'), + '5efa3c4f02f83a0f4b2d69fc95c607cc02825cc4e7be536ef0992df050d9e67c011aed808916ab9b1b30b07abb53561afd46847285ce28651221d406173a3724490e000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e73010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220f7405c28a02cf5bab4ea4498240bb3579db45951794eb1c843bef0534c093ad908e803000000000000400d03000000000064000000000000008b037d67000000000200dbc87a1c816d9bcd06b683c37e80c7162e4d48da7812198b830e4d5d8e0629f2' + ); + }); + + it('should build a unsigned tx and validate its toJson', async function () { + const transaction = new TransferTransaction(coins.get('tapt')); + const txBuilder = factory.getTransferBuilder(transaction); + txBuilder.sender(testData.sender2.address); + txBuilder.recipient(testData.recipients[0]); + txBuilder.gasData({ + maxGasAmount: 200000, + gasUnitPrice: 100, + }); + txBuilder.sequenceNumber(14); + txBuilder.expirationTime(1736246155); + txBuilder.addFeePayerAddress(testData.feePayer.address); + const tx = (await txBuilder.build()) as TransferTransaction; + const toJson = tx.toJson(); + should.equal(toJson.sender, testData.sender2.address); + should.deepEqual(toJson.recipient, { + address: testData.recipients[0].address, + amount: testData.recipients[0].amount, + }); + should.equal(toJson.sequenceNumber, 14); + should.equal(toJson.maxGasAmount, 200000); + should.equal(toJson.gasUnitPrice, 100); + should.equal(toJson.expirationTime, 1736246155); + should.equal(toJson.feePayer, testData.feePayer.address); + }); + + it('should build a signed tx and validate its toJson', async function () { + const txBuilder = factory.from(testData.TRANSACTION_USING_TRANSFER_COINS); + const tx = (await txBuilder.build()) as TransferTransaction; + const toJson = tx.toJson(); + should.equal(toJson.id, '0x80a52dd5d4f712a80b77ad7b4a12a8e61b76243a546099b0ab9acfef4e9a4e31'); + should.equal(toJson.sender, '0x1aed808916ab9b1b30b07abb53561afd46847285ce28651221d406173a372449'); + should.deepEqual(toJson.recipient, { + address: '0xf7405c28a02cf5bab4ea4498240bb3579db45951794eb1c843bef0534c093ad9', + amount: '1000', + }); + should.equal(toJson.sequenceNumber, 146); + should.equal(toJson.maxGasAmount, 200000); + should.equal(toJson.gasUnitPrice, 100); + should.equal(toJson.expirationTime, 1737528215); + }); + }); + + describe('Fail', () => { + it('should fail for invalid sender', async function () { + const transaction = new TransferTransaction(coins.get('tapt')); + const builder = factory.getTransferBuilder(transaction); + should(() => builder.sender('randomString')).throwError('Invalid address randomString'); + }); + + it('should fail for invalid recipient', async function () { + const builder = factory.getTransferBuilder(); + should(() => builder.recipient(testData.invalidRecipients[0])).throwError('Invalid address randomString'); + should(() => builder.recipient(testData.invalidRecipients[1])).throwError('Value cannot be less than zero'); + should(() => builder.recipient(testData.invalidRecipients[2])).throwError('Invalid amount format'); + }); + it('should fail for invalid gas amount', async function () { + const builder = factory.getTransferBuilder(); + should(() => builder.gasData({ maxGasAmount: -1, gasUnitPrice: 100 })).throwError( + 'Value cannot be less than zero' + ); + should(() => builder.gasData({ maxGasAmount: 200000, gasUnitPrice: -1 })).throwError( + 'Value cannot be less than zero' + ); + }); + }); + }); + + describe('Legacy Coin Transfer Transaction', () => { + it('should build a coinz (legacy coin) transfer tx', async function () { const transaction = new TransferTransaction(coins.get('tapt')); const txBuilder = factory.getTransferBuilder(transaction); txBuilder.sender(testData.sender2.address); @@ -19,6 +176,7 @@ describe('Apt Transfer Transaction', () => { }); txBuilder.sequenceNumber(14); txBuilder.expirationTime(1736246155); + txBuilder.assetId(testData.LEGACY_COIN); txBuilder.addFeePayerAddress(testData.feePayer.address); const tx = (await txBuilder.build()) as TransferTransaction; should.equal(tx.sender, testData.sender2.address); @@ -44,38 +202,11 @@ describe('Apt Transfer Transaction', () => { const rawTx = tx.toBroadcastFormat(); should.equal(txBuilder.isValidRawTransaction(rawTx), true); rawTx.should.equal( - '0x1aed808916ab9b1b30b07abb53561afd46847285ce28651221d406173a3724490e000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e73010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220f7405c28a02cf5bab4ea4498240bb3579db45951794eb1c843bef0534c093ad908e803000000000000400d03000000000064000000000000008b037d670000000002030020000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dbc87a1c816d9bcd06b683c37e80c7162e4d48da7812198b830e4d5d8e0629f2002000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + '0x1aed808916ab9b1b30b07abb53561afd46847285ce28651221d406173a3724490e000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e7301074fb379c10c763a13e724064ecfb7d946690bea519ba982c81b518d1c11dd23fe0766615f7465737405436f696e7a000220f7405c28a02cf5bab4ea4498240bb3579db45951794eb1c843bef0534c093ad908e803000000000000400d03000000000064000000000000008b037d670000000002030020000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dbc87a1c816d9bcd06b683c37e80c7162e4d48da7812198b830e4d5d8e0629f2002000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' ); }); - it('should build and send a signed tx', async function () { - const txBuilder = factory.from(testData.TRANSFER); - const tx = (await txBuilder.build()) as TransferTransaction; - should.equal(tx.type, TransactionType.Send); - tx.inputs.length.should.equal(1); - tx.inputs[0].should.deepEqual({ - address: testData.sender2.address, - value: testData.recipients[0].amount, - coin: 'tapt', - }); - tx.outputs.length.should.equal(1); - tx.outputs[0].should.deepEqual({ - address: testData.recipients[0].address, - value: testData.recipients[0].amount, - coin: 'tapt', - }); - should.equal(tx.id, '0x80a52dd5d4f712a80b77ad7b4a12a8e61b76243a546099b0ab9acfef4e9a4e31'); - should.equal(tx.maxGasAmount, 200000); - should.equal(tx.gasUnitPrice, 100); - should.equal(tx.sequenceNumber, 146); - should.equal(tx.expirationTime, 1737528215); - should.equal(tx.type, TransactionType.Send); - const rawTx = tx.toBroadcastFormat(); - should.equal(txBuilder.isValidRawTransaction(rawTx), true); - should.equal(rawTx, testData.TRANSFER); - }); - - it('should succeed to validate a valid signablePayload', async function () { + it('should succeed to validate a valid signablePayload for coinz (legacy coin)', async function () { const transaction = new TransferTransaction(coins.get('tapt')); const txBuilder = factory.getTransferBuilder(transaction); txBuilder.sender(testData.sender2.address); @@ -86,16 +217,17 @@ describe('Apt Transfer Transaction', () => { }); txBuilder.sequenceNumber(14); txBuilder.expirationTime(1736246155); + txBuilder.assetId(testData.LEGACY_COIN); txBuilder.addFeePayerAddress(testData.feePayer.address); const tx = (await txBuilder.build()) as TransferTransaction; const signablePayload = tx.signablePayload; should.equal( signablePayload.toString('hex'), - '5efa3c4f02f83a0f4b2d69fc95c607cc02825cc4e7be536ef0992df050d9e67c011aed808916ab9b1b30b07abb53561afd46847285ce28651221d406173a3724490e000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e73010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220f7405c28a02cf5bab4ea4498240bb3579db45951794eb1c843bef0534c093ad908e803000000000000400d03000000000064000000000000008b037d67000000000200dbc87a1c816d9bcd06b683c37e80c7162e4d48da7812198b830e4d5d8e0629f2' + '5efa3c4f02f83a0f4b2d69fc95c607cc02825cc4e7be536ef0992df050d9e67c011aed808916ab9b1b30b07abb53561afd46847285ce28651221d406173a3724490e000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e7472616e736665725f636f696e7301074fb379c10c763a13e724064ecfb7d946690bea519ba982c81b518d1c11dd23fe0766615f7465737405436f696e7a000220f7405c28a02cf5bab4ea4498240bb3579db45951794eb1c843bef0534c093ad908e803000000000000400d03000000000064000000000000008b037d67000000000200dbc87a1c816d9bcd06b683c37e80c7162e4d48da7812198b830e4d5d8e0629f2' ); }); - it('should build a unsigned tx and validate its toJson', async function () { + it('should build a coinz (legacy coin) unsigned tx and validate its toJson', async function () { const transaction = new TransferTransaction(coins.get('tapt')); const txBuilder = factory.getTransferBuilder(transaction); txBuilder.sender(testData.sender2.address); @@ -106,6 +238,7 @@ describe('Apt Transfer Transaction', () => { }); txBuilder.sequenceNumber(14); txBuilder.expirationTime(1736246155); + txBuilder.assetId(testData.LEGACY_COIN); txBuilder.addFeePayerAddress(testData.feePayer.address); const tx = (await txBuilder.build()) as TransferTransaction; const toJson = tx.toJson(); @@ -120,45 +253,5 @@ describe('Apt Transfer Transaction', () => { should.equal(toJson.expirationTime, 1736246155); should.equal(toJson.feePayer, testData.feePayer.address); }); - - it('should build a signed tx and validate its toJson', async function () { - const txBuilder = factory.from(testData.TRANSFER); - const tx = (await txBuilder.build()) as TransferTransaction; - const toJson = tx.toJson(); - should.equal(toJson.id, '0x80a52dd5d4f712a80b77ad7b4a12a8e61b76243a546099b0ab9acfef4e9a4e31'); - should.equal(toJson.sender, '0x1aed808916ab9b1b30b07abb53561afd46847285ce28651221d406173a372449'); - should.deepEqual(toJson.recipient, { - address: '0xf7405c28a02cf5bab4ea4498240bb3579db45951794eb1c843bef0534c093ad9', - amount: '1000', - }); - should.equal(toJson.sequenceNumber, 146); - should.equal(toJson.maxGasAmount, 200000); - should.equal(toJson.gasUnitPrice, 100); - should.equal(toJson.expirationTime, 1737528215); - }); - }); - - describe('Fail', () => { - it('should fail for invalid sender', async function () { - const transaction = new TransferTransaction(coins.get('tapt')); - const builder = factory.getTransferBuilder(transaction); - should(() => builder.sender('randomString')).throwError('Invalid address randomString'); - }); - - it('should fail for invalid recipient', async function () { - const builder = factory.getTransferBuilder(); - should(() => builder.recipient(testData.invalidRecipients[0])).throwError('Invalid address randomString'); - should(() => builder.recipient(testData.invalidRecipients[1])).throwError('Value cannot be less than zero'); - should(() => builder.recipient(testData.invalidRecipients[2])).throwError('Invalid amount format'); - }); - it('should fail for invalid gas amount', async function () { - const builder = factory.getTransferBuilder(); - should(() => builder.gasData({ maxGasAmount: -1, gasUnitPrice: 100 })).throwError( - 'Value cannot be less than zero' - ); - should(() => builder.gasData({ maxGasAmount: 200000, gasUnitPrice: -1 })).throwError( - 'Value cannot be less than zero' - ); - }); }); });