Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions modules/sdk-coin-apt/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export const COIN_TRANSFER_FUNCTION = '0x1::aptos_account::transfer_coins';
export const COIN_BATCH_TRANSFER_FUNCTION = '0x1::aptos_account::batch_transfer_coins';
export const DIGITAL_ASSET_TRANSFER_FUNCTION = '0x1::object::transfer';
export const DELEGATION_POOL_ADD_STAKE_FUNCTION = '0x1::delegation_pool::add_stake';
export const DELEGATION_POOL_UNLOCK_FUNCTION = '0x1::delegation_pool::unlock';
export const DELEGATION_POOL_WITHDRAW_FUNCTION = '0x1::delegation_pool::withdraw';

export const APTOS_COIN = '0x1::aptos_coin::AptosCoin';
export const FUNGIBLE_ASSET_TYPE_ARGUMENT = '0x1::fungible_asset::Metadata';
Expand Down
10 changes: 9 additions & 1 deletion modules/sdk-coin-apt/src/lib/iface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface TxData {
id: string;
sender: string;
/** @deprecated - use `recipients`. */
recipient: TransactionRecipient;
recipient?: TransactionRecipient;
recipients: TransactionRecipient[];
sequenceNumber: number;
maxGasAmount: number;
Expand All @@ -28,6 +28,14 @@ export interface TxData {
assetId: string;
}

/**
* The transaction data returned from the toJson() function of a delegation pool transaction
*/
export interface DelegationPoolTxData extends TxData {
validatorAddress: string | null;
amount: string | null;
}

export interface RecipientsValidationResult {
recipients: {
deserializedAddresses: string[];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {
AccountAddress,
EntryFunctionABI,
InputGenerateTransactionPayloadData,
MoveFunctionId,
TransactionPayload,
TransactionPayloadEntryFunction,
TypeTagAddress,
TypeTagU64,
} from '@aptos-labs/ts-sdk';
import { AbstractDelegationPoolTransaction } from './abstractDelegationPoolTransaction';
import { BaseCoin } from '@bitgo/statics';
import { InvalidTransactionError } from '@bitgo/sdk-core';
import utils from '../utils';
import { APTOS_COIN } from '../constants';

/**
* This is a convenience class for delegation pool functions with arguments [address, amount].
*
* Assume this class can be deleted at any time (concrete implementations remain the same).
* Therefore, do not store objects as this type.
* Good: `export abstract class DelegationPoolWithdrawTransaction extends AbstractDelegationPoolAmountBasedTransaction`
* Good: `const transaction: AbstractDelegationPoolTransaction = DelegationPoolWithdrawTransaction()`
* Bad: `const transaction: AbstractDelegationPoolAmountBasedTransaction = DelegationPoolWithdrawTransaction()`
*/
export abstract class AbstractDelegationPoolAmountBasedTransaction extends AbstractDelegationPoolTransaction {
constructor(coinConfig: Readonly<BaseCoin>) {
super(coinConfig);
this._assetId = APTOS_COIN;
}

abstract moveFunctionId(): MoveFunctionId;

protected override parseTransactionPayload(payload: TransactionPayload): void {
if (!this.isValidPayload(payload)) {
throw new InvalidTransactionError('Invalid transaction payload');
}
const { entryFunction } = payload;
const addressArg = entryFunction.args[0];
const amountArg = entryFunction.args[1];
const [{ address, amount }] = utils.parseRecipients(addressArg, amountArg);
this.validatorAddress = address;
this.amount = amount.toString();
}

protected override getTransactionPayloadData(): InputGenerateTransactionPayloadData {
const { validatorAddress, amount } = this;
if (validatorAddress === undefined) throw new Error('validatorAddress is undefined');
if (amount === undefined) throw new Error('amount is undefined');
return {
function: this.moveFunctionId(),
typeArguments: [],
functionArguments: [AccountAddress.fromString(validatorAddress), amount],
abi: this.abi,
};
}

private isValidPayload(payload: TransactionPayload): payload is TransactionPayloadEntryFunction {
return (
payload instanceof TransactionPayloadEntryFunction &&
payload.entryFunction.args.length === 2 &&
payload.entryFunction.type_args.length === 0
);
}

private abi: EntryFunctionABI = {
typeParameters: [],
parameters: [new TypeTagAddress(), new TypeTagU64()],
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { DelegationPoolTxData } from '../iface';
import { Transaction } from './transaction';
import { BaseCoin } from '@bitgo/statics';

/**
* This is for transactions where one delegator participates in a delegation pool.
*/
export abstract class AbstractDelegationPoolTransaction extends Transaction {
public validatorAddress?: string = undefined;
public amount?: string = undefined;

constructor(coinConfig: Readonly<BaseCoin>) {
super(coinConfig);
}

override toJson(): DelegationPoolTxData {
return {
...super.toJson(),
validatorAddress: this.validatorAddress ?? null,
amount: this.amount ?? null,
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import BigNumber from 'bignumber.js';
import { InputsAndOutputs, Transaction } from './transaction';
import { TxData } from '../iface';

/**
* This is for transactions where one sender sends coins to recipients.
*/
export abstract class AbstractTransferTransaction extends Transaction {
override inputsAndOutputs(): InputsAndOutputs {
const totalAmount = this._recipients.reduce(
(accumulator, current) => accumulator.plus(current.amount),
new BigNumber('0')
);
const inputs = [
{
address: this.sender,
value: totalAmount.toString(),
coin: this._coinConfig.name,
},
];
const outputs = this._recipients.map((recipient) => {
return {
address: recipient.address,
value: recipient.amount as string,
coin: this._coinConfig.name,
};
});
return { inputs, outputs, externalOutputs: this._recipients };
}

override toJson(): TxData {
return {
...super.toJson(),
recipient: this.recipient,
};
}
}
18 changes: 9 additions & 9 deletions modules/sdk-coin-apt/src/lib/transaction/customTransaction.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
import { Transaction } from './transaction';
import { BaseCoin as CoinConfig } from '@bitgo/statics';
import { InvalidTransactionError, TransactionType } from '@bitgo/sdk-core';
import {
AccountAddress,
EntryFunctionABI,
EntryFunctionArgumentTypes,
SimpleEntryFunctionArgumentTypes,
InputGenerateTransactionPayloadData,
SimpleEntryFunctionArgumentTypes,
TransactionPayload,
TransactionPayloadEntryFunction,
TypeTagAddress,
TypeTagBool,
TypeTagU8,
TypeTagU128,
TypeTagU16,
TypeTagU256,
TypeTagU32,
TypeTagU64,
TypeTagU128,
TypeTagU256,
TypeTagU8,
} from '@aptos-labs/ts-sdk';
import { InvalidTransactionError, TransactionType } from '@bitgo/sdk-core';
import { BaseCoin as CoinConfig } from '@bitgo/statics';
import { CustomTransactionParams } from '../iface';
import { validateModuleName, validateFunctionName } from '../utils/validation';
import utils from '../utils';
import { validateFunctionName, validateModuleName } from '../utils/validation';
import { AbstractTransferTransaction } from './abstractTransferTransaction';

/**
* Transaction class for custom Aptos transactions.
*/
export class CustomTransaction extends Transaction {
export class CustomTransaction extends AbstractTransferTransaction {
private _moduleName: string;
private _functionName: string;
private _typeArguments: string[] = [];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,55 +1,47 @@
import { Transaction } from './transaction';
import { InvalidTransactionError, TransactionType } from '@bitgo/sdk-core';
import {
AccountAddress,
EntryFunctionABI,
InputGenerateTransactionPayloadData,
TransactionPayload,
TransactionPayloadEntryFunction,
TypeTagAddress,
TypeTagU64,
} from '@aptos-labs/ts-sdk';
import { MoveFunctionId } from '@aptos-labs/ts-sdk';
import { TransactionType } from '@bitgo/sdk-core';

import { BaseCoin as CoinConfig } from '@bitgo/statics';
import { APTOS_COIN, DELEGATION_POOL_ADD_STAKE_FUNCTION } from '../constants';
import utils from '../utils';
import { DELEGATION_POOL_ADD_STAKE_FUNCTION } from '../constants';
import { AbstractDelegationPoolAmountBasedTransaction } from './abstractDelegationPoolAmountBasedTransaction';
import { InputsAndOutputs } from './transaction';

export class DelegationPoolAddStakeTransaction extends Transaction {
export class DelegationPoolAddStakeTransaction extends AbstractDelegationPoolAmountBasedTransaction {
constructor(coinConfig: Readonly<CoinConfig>) {
super(coinConfig);
this._type = TransactionType.StakingDelegate;
this._assetId = APTOS_COIN;
}

protected parseTransactionPayload(payload: TransactionPayload): void {
if (!this.isValidPayload(payload)) {
throw new InvalidTransactionError('Invalid transaction payload');
}
const { entryFunction } = payload;
const addressArg = entryFunction.args[0];
const amountArg = entryFunction.args[1];
this.recipients = utils.parseRecipients(addressArg, amountArg);
override moveFunctionId(): MoveFunctionId {
return DELEGATION_POOL_ADD_STAKE_FUNCTION;
}

protected getTransactionPayloadData(): InputGenerateTransactionPayloadData {
override inputsAndOutputs(): InputsAndOutputs {
const { sender, validatorAddress, amount } = this;
if (sender === undefined) throw new Error('sender is undefined');
if (validatorAddress === undefined) throw new Error('validatorAddress is undefined');
if (amount === undefined) throw new Error('amount is undefined');
return {
function: DELEGATION_POOL_ADD_STAKE_FUNCTION,
typeArguments: [],
functionArguments: [AccountAddress.fromString(this.recipients[0].address), this.recipients[0].amount],
abi: this.abi,
inputs: [
{
address: sender,
value: amount,
coin: this._coinConfig.name,
},
],
outputs: [
{
address: validatorAddress,
value: amount,
coin: this._coinConfig.name,
},
],
externalOutputs: [
{
address: validatorAddress,
amount: amount,
},
],
};
}

private isValidPayload(payload: TransactionPayload): payload is TransactionPayloadEntryFunction {
return (
payload instanceof TransactionPayloadEntryFunction &&
payload.entryFunction.args.length === 2 &&
payload.entryFunction.type_args.length === 0
);
}

private abi: EntryFunctionABI = {
typeParameters: [],
parameters: [new TypeTagAddress(), new TypeTagU64()],
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { MoveFunctionId } from '@aptos-labs/ts-sdk';
import { TransactionType } from '@bitgo/sdk-core';

import { BaseCoin as CoinConfig } from '@bitgo/statics';
import { DELEGATION_POOL_UNLOCK_FUNCTION } from '../constants';
import { AbstractDelegationPoolAmountBasedTransaction } from './abstractDelegationPoolAmountBasedTransaction';
import { InputsAndOutputs } from './transaction';

export class DelegationPoolUnlockTransaction extends AbstractDelegationPoolAmountBasedTransaction {
constructor(coinConfig: Readonly<CoinConfig>) {
super(coinConfig);
this._type = TransactionType.StakingUnlock;
}

override moveFunctionId(): MoveFunctionId {
return DELEGATION_POOL_UNLOCK_FUNCTION;
}

override inputsAndOutputs(): InputsAndOutputs {
return {
inputs: [],
outputs: [],
externalOutputs: [],
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { MoveFunctionId } from '@aptos-labs/ts-sdk';
import { TransactionType } from '@bitgo/sdk-core';

import { BaseCoin as CoinConfig } from '@bitgo/statics';
import { DELEGATION_POOL_WITHDRAW_FUNCTION } from '../constants';
import { AbstractDelegationPoolAmountBasedTransaction } from './abstractDelegationPoolAmountBasedTransaction';
import { InputsAndOutputs } from './transaction';

export class DelegationPoolWithdrawTransaction extends AbstractDelegationPoolAmountBasedTransaction {
constructor(coinConfig: Readonly<CoinConfig>) {
super(coinConfig);
this._type = TransactionType.StakingWithdraw;
}

override moveFunctionId(): MoveFunctionId {
return DELEGATION_POOL_WITHDRAW_FUNCTION;
}

override inputsAndOutputs(): InputsAndOutputs {
const { sender, validatorAddress, amount } = this;
if (sender === undefined) throw new Error('sender is undefined');
if (validatorAddress === undefined) throw new Error('validatorAddress is undefined');
if (amount === undefined) throw new Error('amount is undefined');
return {
inputs: [
{
address: validatorAddress,
value: amount,
coin: this._coinConfig.name,
},
],
outputs: [
{
address: sender,
value: amount,
coin: this._coinConfig.name,
},
],
externalOutputs: [],
};
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Transaction } from './transaction';
import {
AccountAddress,
EntryFunctionABI,
Expand All @@ -14,12 +13,13 @@ import {
import { InvalidTransactionError, TransactionRecipient, TransactionType } from '@bitgo/sdk-core';
import { BaseCoin as CoinConfig } from '@bitgo/statics';
import {
DIGITAL_ASSET_TYPE_ARGUMENT,
DIGITAL_ASSET_TRANSFER_FUNCTION,
DIGITAL_ASSET_TRANSFER_AMOUNT,
DIGITAL_ASSET_TRANSFER_FUNCTION,
DIGITAL_ASSET_TYPE_ARGUMENT,
} from '../constants';
import { AbstractTransferTransaction } from './abstractTransferTransaction';

export class DigitalAssetTransfer extends Transaction {
export class DigitalAssetTransfer extends AbstractTransferTransaction {
constructor(coinConfig: Readonly<CoinConfig>) {
super(coinConfig);
this._type = TransactionType.SendNFT;
Expand Down
Loading