Skip to content

Commit

Permalink
feat(sdk-coin-apt): test cases for legacy coin
Browse files Browse the repository at this point in the history
TICKET: COIN-2914
  • Loading branch information
baltiyal committed Jan 28, 2025
1 parent 94a41ae commit 248276e
Show file tree
Hide file tree
Showing 9 changed files with 7,164 additions and 4,864 deletions.
7 changes: 5 additions & 2 deletions modules/sdk-coin-apt/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@ export const UNAVAILABLE_TEXT = 'UNAVAILABLE';
export const DEFAULT_GAS_UNIT_PRICE = 100;
export const SECONDS_PER_WEEK = 7 * 24 * 60 * 60; // Days * Hours * Minutes * Seconds

export const APTOS_ACCOUNT_MODULE = 'aptos_account';
export const FUNGIBLE_ASSET_MODULE = 'primary_fungible_store';
export const APTOS_COIN = '0x1::aptos_account::transfer_coins';
export const FUNGIBLE_ASSET = '0x1::primary_fungible_store::transfer';
export const NON_FUNGIBLE_ASSET = '0x1::object::transfer';

export const NON_FUNGIBLE_ASSET_TRANSFER_SUPPLY_PER_TRANSACTION = '1';
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Transaction } from './transaction';
import {
AccountAddress,
Aptos,
AptosConfig,
EntryFunctionABI,
MoveAbility,
Network,
objectStructTag,
TransactionPayload,
TransactionPayloadEntryFunction,
TypeTagAddress,
TypeTagGeneric,
TypeTagStruct,
} from '@aptos-labs/ts-sdk';
import { InvalidTransactionError, TransactionRecipient, TransactionType } from '@bitgo/sdk-core';
import { BaseCoin as CoinConfig, NetworkType } from '@bitgo/statics';
import { NON_FUNGIBLE_ASSET_TRANSFER_SUPPLY_PER_TRANSACTION } from '../constants';

export class NonFungibleAssetTransaction extends Transaction {
constructor(coinConfig: Readonly<CoinConfig>) {
super(coinConfig);
this._type = TransactionType.SendNFT;
this._assetId = AccountAddress.ZERO.toString();
}

protected parseTransactionPayload(payload: TransactionPayload): void {
if (!(payload instanceof TransactionPayloadEntryFunction) || payload.entryFunction.args.length !== 2) {
throw new InvalidTransactionError('Invalid transaction payload');
}
const entryFunction = payload.entryFunction;
if (!this._recipient) {
this._recipient = {} as TransactionRecipient;
}
this._assetId = entryFunction.args[0].toString();
this._recipient.address = entryFunction.args[1].toString();
this._recipient.amount = NON_FUNGIBLE_ASSET_TRANSFER_SUPPLY_PER_TRANSACTION;
}

protected async buildRawTransaction(): Promise<void> {
const network: Network = this._coinConfig.network.type === NetworkType.MAINNET ? Network.MAINNET : Network.TESTNET;
const aptos = new Aptos(new AptosConfig({ network }));
const senderAddress = AccountAddress.fromString(this._sender);
const recipientAddress = AccountAddress.fromString(this._recipient.address);
const nonFungibleAssetAccountAddress = AccountAddress.fromString(this._assetId);

const transferDigitalAssetAbi: EntryFunctionABI = {
typeParameters: [{ constraints: [MoveAbility.KEY] }],
parameters: [new TypeTagStruct(objectStructTag(new TypeTagGeneric(0))), new TypeTagAddress()],
};

const simpleTxn = await aptos.transaction.build.simple({
sender: senderAddress,
data: {
function: '0x1::object::transfer',
typeArguments: ['0x4::token::Token'],
functionArguments: [nonFungibleAssetAccountAddress, recipientAddress],
abi: transferDigitalAssetAbi,
},
options: {
maxGasAmount: this.maxGasAmount,
gasUnitPrice: this.gasUnitPrice,
expireTimestamp: this.expirationTime,
accountSequenceNumber: this.sequenceNumber,
},
});
this._rawTransaction = simpleTxn.rawTransaction;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { TransactionBuilder } from './transactionBuilder';
import { BaseCoin as CoinConfig } from '@bitgo/statics';
import { FungibleAssetTransaction } from '../transaction/fungibleAssetTransaction';
import { TransactionType } from '@bitgo/sdk-core';
import { Transaction } from '../transaction/transaction';
import utils from '../utils';
import { TransactionPayload, TransactionPayloadEntryFunction } from '@aptos-labs/ts-sdk';
import { NonFungibleAssetTransaction } from '../transaction/nonFungibleAssetTransaction';

export class NonFungibleAssetTransactionBuilder extends TransactionBuilder {
constructor(_coinConfig: Readonly<CoinConfig>) {
super(_coinConfig);
this._transaction = new NonFungibleAssetTransaction(_coinConfig);
}

protected get transactionType(): TransactionType {
return TransactionType.SendNFT;
}

assetId(assetId: string): TransactionBuilder {
this.validateAddress({ address: assetId });
this.transaction.assetId = assetId;
return this;
}

/** @inheritdoc */
initBuilder(tx: FungibleAssetTransaction): void {
this._transaction = tx;
}

/** @inheritdoc */
validateTransaction(transaction?: FungibleAssetTransaction): void {
if (!transaction) {
throw new Error('transaction not defined');
}
this.validateAddress({ address: transaction.sender });
this.validateAddress({ address: transaction.recipient.address });
this.validateAddress({ address: transaction.assetId });
}

protected isValidTransactionPayload(payload: TransactionPayload) {
try {
if (!(payload instanceof TransactionPayloadEntryFunction) || payload.entryFunction.args.length !== 2) {
console.error('invalid transaction payload');
return false;
}
const entryFunction = payload.entryFunction;
const nonFungibleAssetAddress = entryFunction.args[0].toString();
const recipientAddress = entryFunction.args[1].toString();
return utils.isValidAddress(recipientAddress) && utils.isValidAddress(nonFungibleAssetAddress);
} catch (e) {
console.error('invalid transaction payload', e);
return false;
}
}

/** @inheritdoc */
protected fromImplementation(rawTransaction: string): Transaction {
this.transaction.fromRawTransaction(rawTransaction);
this.transaction.transactionType = this.transactionType;
return this.transaction;
}

/** @inheritdoc */
protected async buildImplementation(): Promise<Transaction> {
this.transaction.transactionType = this.transactionType;
await this.transaction.build();
return this.transaction;
}
}
11 changes: 11 additions & 0 deletions modules/sdk-coin-apt/src/lib/transactionBuilderFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { TransferTransaction } from './transaction/transferTransaction';
import { BaseCoin as CoinConfig } from '@bitgo/statics';
import { FungibleAssetTransaction } from './transaction/fungibleAssetTransaction';
import { FungibleAssetTransactionBuilder } from './transactionBuilder/fungibleAssetTransactionBuilder';
import { NonFungibleAssetTransaction } from './transaction/nonFungibleAssetTransaction';
import { NonFungibleAssetTransactionBuilder } from './transactionBuilder/nonFungibleAssetTransactionBuilder';

export class TransactionBuilderFactory extends BaseTransactionBuilderFactory {
constructor(_coinConfig: Readonly<CoinConfig>) {
Expand All @@ -28,6 +30,10 @@ export class TransactionBuilderFactory extends BaseTransactionBuilderFactory {
const fungibleTransferTokenTx = new FungibleAssetTransaction(this._coinConfig);
fungibleTransferTokenTx.fromDeserializedSignedTransaction(signedTxn);
return this.getFungibleAssetTransactionBuilder(fungibleTransferTokenTx);
case TransactionType.SendNFT:
const nonFungibleTransferTokenTx = new NonFungibleAssetTransaction(this._coinConfig);
nonFungibleTransferTokenTx.fromDeserializedSignedTransaction(signedTxn);
return this.getNonFungibleAssetTransactionBuilder(nonFungibleTransferTokenTx);
default:
throw new InvalidTransactionError('Invalid transaction');
}
Expand All @@ -51,6 +57,11 @@ export class TransactionBuilderFactory extends BaseTransactionBuilderFactory {
return this.initializeBuilder(tx, new FungibleAssetTransactionBuilder(this._coinConfig));
}

/** @inheritdoc */
getNonFungibleAssetTransactionBuilder(tx?: Transaction): NonFungibleAssetTransactionBuilder {
return this.initializeBuilder(tx, new NonFungibleAssetTransactionBuilder(this._coinConfig));
}

/** @inheritdoc */
getWalletInitializationBuilder(): void {
throw new Error('Method not implemented.');
Expand Down
16 changes: 11 additions & 5 deletions modules/sdk-coin-apt/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ import {
APT_BLOCK_ID_LENGTH,
APT_SIGNATURE_LENGTH,
APT_TRANSACTION_ID_LENGTH,
APTOS_ACCOUNT_MODULE,
FUNGIBLE_ASSET_MODULE,
APTOS_COIN,
FUNGIBLE_ASSET,
NON_FUNGIBLE_ASSET,
} from './constants';
import BigNumber from 'bignumber.js';

Expand Down Expand Up @@ -72,12 +73,17 @@ export class Utils implements BaseUtils {
throw new Error('Invalid Payload: Expected TransactionPayloadEntryFunction');
}
const entryFunction = payload.entryFunction;
const moduleAddress = entryFunction.module_name.address.toString();
const moduleIdentifier = entryFunction.module_name.name.identifier;
switch (moduleIdentifier) {
case APTOS_ACCOUNT_MODULE:
const functionIdentifier = entryFunction.function_name.identifier;
const uniqueIdentifier = `${moduleAddress}::${moduleIdentifier}::${functionIdentifier}`;
switch (uniqueIdentifier) {
case APTOS_COIN:
return TransactionType.Send;
case FUNGIBLE_ASSET_MODULE:
case FUNGIBLE_ASSET:
return TransactionType.SendToken;
case NON_FUNGIBLE_ASSET:
return TransactionType.SendNFT;
default:
throw new InvalidTransactionError(`Invalid transaction: unable to fetch transaction type ${moduleIdentifier}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ describe('Apt Token Transfer Builder', () => {
it('should build and send a signed tx', async function () {
const txBuilder = factory.from(testData.FUNGIBLE_TOKEN_TRANSFER);
const tx = (await txBuilder.build()) as FungibleAssetTransaction;
console.log('----- tx: ', tx);
should.equal(tx.type, TransactionType.SendToken);
tx.inputs.length.should.equal(1);
tx.inputs[0].should.deepEqual({
Expand Down
Loading

0 comments on commit 248276e

Please sign in to comment.