Skip to content

Commit 3b76e15

Browse files
committed
refactor(sdk-coin-apt): delegation pool transaction subclass
Ticket: SC-3600 Ticket: SC-3601 First, we isolate `recipients` concept to transfer-based transactions. For delegation pool operations, the transaction sender is not always correlated with inputs and outputs (e.g. withdraw). Then we create delegation pool-based transactions with `validatorAddress` and `amount`.
1 parent 3c43f46 commit 3b76e15

13 files changed

+244
-125
lines changed

modules/sdk-coin-apt/src/lib/iface.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export interface TxData {
1717
id: string;
1818
sender: string;
1919
/** @deprecated - use `recipients`. */
20-
recipient: TransactionRecipient;
20+
recipient?: TransactionRecipient;
2121
recipients: TransactionRecipient[];
2222
sequenceNumber: number;
2323
maxGasAmount: number;
@@ -28,6 +28,14 @@ export interface TxData {
2828
assetId: string;
2929
}
3030

31+
/**
32+
* The transaction data returned from the toJson() function of a delegation pool transaction
33+
*/
34+
export interface DelegationPoolTxData extends TxData {
35+
validatorAddress: string | null;
36+
amount: string | null;
37+
}
38+
3139
export interface RecipientsValidationResult {
3240
recipients: {
3341
deserializedAddresses: string[];
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import {
2+
AccountAddress,
3+
EntryFunctionABI,
4+
InputGenerateTransactionPayloadData,
5+
MoveFunctionId,
6+
TransactionPayload,
7+
TransactionPayloadEntryFunction,
8+
TypeTagAddress,
9+
TypeTagU64,
10+
} from '@aptos-labs/ts-sdk';
11+
import { AbstractDelegationPoolTransaction } from './abstractDelegationPoolTransaction';
12+
import { BaseCoin } from '@bitgo/statics';
13+
import { InvalidTransactionError } from '@bitgo/sdk-core';
14+
import utils from '../utils';
15+
import { APTOS_COIN } from '../constants';
16+
17+
/**
18+
* This is a convenience class for delegation pool functions with arguments [address, amount].
19+
*
20+
* Assume this class can be deleted at any time (concrete implementations remain the same).
21+
* Therefore, do not store objects as this type.
22+
* Good: `export abstract class DelegationPoolWithdrawTransaction extends AbstractDelegationPoolAmountBasedTransaction`
23+
* Good: `const transaction: AbstractDelegationPoolTransaction = DelegationPoolWithdrawTransaction()`
24+
* Bad: `const transaction: AbstractDelegationPoolAmountBasedTransaction = DelegationPoolWithdrawTransaction()`
25+
*/
26+
export abstract class AbstractDelegationPoolAmountBasedTransaction extends AbstractDelegationPoolTransaction {
27+
constructor(coinConfig: Readonly<BaseCoin>) {
28+
super(coinConfig);
29+
this._assetId = APTOS_COIN;
30+
}
31+
32+
abstract moveFunctionId(): MoveFunctionId;
33+
34+
protected override parseTransactionPayload(payload: TransactionPayload): void {
35+
if (!this.isValidPayload(payload)) {
36+
throw new InvalidTransactionError('Invalid transaction payload');
37+
}
38+
const { entryFunction } = payload;
39+
const addressArg = entryFunction.args[0];
40+
const amountArg = entryFunction.args[1];
41+
const [{ address, amount }] = utils.parseRecipients(addressArg, amountArg);
42+
this.validatorAddress = address;
43+
this.amount = amount.toString();
44+
}
45+
46+
protected override getTransactionPayloadData(): InputGenerateTransactionPayloadData {
47+
const { validatorAddress, amount } = this;
48+
if (validatorAddress === undefined) throw new Error('validatorAddress is undefined');
49+
if (amount === undefined) throw new Error('amount is undefined');
50+
return {
51+
function: this.moveFunctionId(),
52+
typeArguments: [],
53+
functionArguments: [AccountAddress.fromString(validatorAddress), amount],
54+
abi: this.abi,
55+
};
56+
}
57+
58+
private isValidPayload(payload: TransactionPayload): payload is TransactionPayloadEntryFunction {
59+
return (
60+
payload instanceof TransactionPayloadEntryFunction &&
61+
payload.entryFunction.args.length === 2 &&
62+
payload.entryFunction.type_args.length === 0
63+
);
64+
}
65+
66+
private abi: EntryFunctionABI = {
67+
typeParameters: [],
68+
parameters: [new TypeTagAddress(), new TypeTagU64()],
69+
};
70+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { DelegationPoolTxData } from '../iface';
2+
import { Transaction } from './transaction';
3+
import { BaseCoin } from '@bitgo/statics';
4+
5+
/**
6+
* This is for transactions where one delegator participates in a delegation pool.
7+
*/
8+
export abstract class AbstractDelegationPoolTransaction extends Transaction {
9+
public validatorAddress?: string = undefined;
10+
public amount?: string = undefined;
11+
12+
constructor(coinConfig: Readonly<BaseCoin>) {
13+
super(coinConfig);
14+
}
15+
16+
override toJson(): DelegationPoolTxData {
17+
return {
18+
...super.toJson(),
19+
validatorAddress: this.validatorAddress ?? null,
20+
amount: this.amount ?? null,
21+
};
22+
}
23+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import BigNumber from 'bignumber.js';
2+
import { InputsAndOutputs, Transaction } from './transaction';
3+
import { TxData } from '../iface';
4+
5+
/**
6+
* This is for transactions where one sender sends coins to recipients.
7+
*/
8+
export abstract class AbstractTransferTransaction extends Transaction {
9+
override inputsAndOutputs(): InputsAndOutputs {
10+
const totalAmount = this._recipients.reduce(
11+
(accumulator, current) => accumulator.plus(current.amount),
12+
new BigNumber('0')
13+
);
14+
const inputs = [
15+
{
16+
address: this.sender,
17+
value: totalAmount.toString(),
18+
coin: this._coinConfig.name,
19+
},
20+
];
21+
const outputs = this._recipients.map((recipient) => {
22+
return {
23+
address: recipient.address,
24+
value: recipient.amount as string,
25+
coin: this._coinConfig.name,
26+
};
27+
});
28+
return { inputs, outputs, externalOutputs: this._recipients };
29+
}
30+
31+
override toJson(): TxData {
32+
return {
33+
...super.toJson(),
34+
recipient: this.recipient,
35+
};
36+
}
37+
}

modules/sdk-coin-apt/src/lib/transaction/customTransaction.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
1-
import { Transaction } from './transaction';
2-
import { BaseCoin as CoinConfig } from '@bitgo/statics';
3-
import { InvalidTransactionError, TransactionType } from '@bitgo/sdk-core';
41
import {
52
AccountAddress,
63
EntryFunctionABI,
74
EntryFunctionArgumentTypes,
8-
SimpleEntryFunctionArgumentTypes,
95
InputGenerateTransactionPayloadData,
6+
SimpleEntryFunctionArgumentTypes,
107
TransactionPayload,
118
TransactionPayloadEntryFunction,
129
TypeTagAddress,
1310
TypeTagBool,
14-
TypeTagU8,
11+
TypeTagU128,
1512
TypeTagU16,
13+
TypeTagU256,
1614
TypeTagU32,
1715
TypeTagU64,
18-
TypeTagU128,
19-
TypeTagU256,
16+
TypeTagU8,
2017
} from '@aptos-labs/ts-sdk';
18+
import { InvalidTransactionError, TransactionType } from '@bitgo/sdk-core';
19+
import { BaseCoin as CoinConfig } from '@bitgo/statics';
2120
import { CustomTransactionParams } from '../iface';
22-
import { validateModuleName, validateFunctionName } from '../utils/validation';
2321
import utils from '../utils';
22+
import { validateFunctionName, validateModuleName } from '../utils/validation';
23+
import { AbstractTransferTransaction } from './abstractTransferTransaction';
2424

2525
/**
2626
* Transaction class for custom Aptos transactions.
2727
*/
28-
export class CustomTransaction extends Transaction {
28+
export class CustomTransaction extends AbstractTransferTransaction {
2929
private _moduleName: string;
3030
private _functionName: string;
3131
private _typeArguments: string[] = [];
Lines changed: 33 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,47 @@
1-
import { Transaction } from './transaction';
2-
import { InvalidTransactionError, TransactionType } from '@bitgo/sdk-core';
3-
import {
4-
AccountAddress,
5-
EntryFunctionABI,
6-
InputGenerateTransactionPayloadData,
7-
TransactionPayload,
8-
TransactionPayloadEntryFunction,
9-
TypeTagAddress,
10-
TypeTagU64,
11-
} from '@aptos-labs/ts-sdk';
1+
import { MoveFunctionId } from '@aptos-labs/ts-sdk';
2+
import { TransactionType } from '@bitgo/sdk-core';
123

134
import { BaseCoin as CoinConfig } from '@bitgo/statics';
14-
import { APTOS_COIN, DELEGATION_POOL_ADD_STAKE_FUNCTION } from '../constants';
15-
import utils from '../utils';
5+
import { DELEGATION_POOL_ADD_STAKE_FUNCTION } from '../constants';
6+
import { AbstractDelegationPoolAmountBasedTransaction } from './abstractDelegationPoolAmountBasedTransaction';
7+
import { InputsAndOutputs } from './transaction';
168

17-
export class DelegationPoolAddStakeTransaction extends Transaction {
9+
export class DelegationPoolAddStakeTransaction extends AbstractDelegationPoolAmountBasedTransaction {
1810
constructor(coinConfig: Readonly<CoinConfig>) {
1911
super(coinConfig);
2012
this._type = TransactionType.StakingDelegate;
21-
this._assetId = APTOS_COIN;
2213
}
2314

24-
protected parseTransactionPayload(payload: TransactionPayload): void {
25-
if (!this.isValidPayload(payload)) {
26-
throw new InvalidTransactionError('Invalid transaction payload');
27-
}
28-
const { entryFunction } = payload;
29-
const addressArg = entryFunction.args[0];
30-
const amountArg = entryFunction.args[1];
31-
this.recipients = utils.parseRecipients(addressArg, amountArg);
15+
override moveFunctionId(): MoveFunctionId {
16+
return DELEGATION_POOL_ADD_STAKE_FUNCTION;
3217
}
3318

34-
protected getTransactionPayloadData(): InputGenerateTransactionPayloadData {
19+
override inputsAndOutputs(): InputsAndOutputs {
20+
const { sender, validatorAddress, amount } = this;
21+
if (sender === undefined) throw new Error('sender is undefined');
22+
if (validatorAddress === undefined) throw new Error('validatorAddress is undefined');
23+
if (amount === undefined) throw new Error('amount is undefined');
3524
return {
36-
function: DELEGATION_POOL_ADD_STAKE_FUNCTION,
37-
typeArguments: [],
38-
functionArguments: [AccountAddress.fromString(this.recipients[0].address), this.recipients[0].amount],
39-
abi: this.abi,
25+
inputs: [
26+
{
27+
address: sender,
28+
value: amount,
29+
coin: this._coinConfig.name,
30+
},
31+
],
32+
outputs: [
33+
{
34+
address: validatorAddress,
35+
value: amount,
36+
coin: this._coinConfig.name,
37+
},
38+
],
39+
externalOutputs: [
40+
{
41+
address: validatorAddress,
42+
amount: amount,
43+
},
44+
],
4045
};
4146
}
42-
43-
private isValidPayload(payload: TransactionPayload): payload is TransactionPayloadEntryFunction {
44-
return (
45-
payload instanceof TransactionPayloadEntryFunction &&
46-
payload.entryFunction.args.length === 2 &&
47-
payload.entryFunction.type_args.length === 0
48-
);
49-
}
50-
51-
private abi: EntryFunctionABI = {
52-
typeParameters: [],
53-
parameters: [new TypeTagAddress(), new TypeTagU64()],
54-
};
5547
}

modules/sdk-coin-apt/src/lib/transaction/digitalAssetTransfer.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { Transaction } from './transaction';
21
import {
32
AccountAddress,
43
EntryFunctionABI,
@@ -14,12 +13,13 @@ import {
1413
import { InvalidTransactionError, TransactionRecipient, TransactionType } from '@bitgo/sdk-core';
1514
import { BaseCoin as CoinConfig } from '@bitgo/statics';
1615
import {
17-
DIGITAL_ASSET_TYPE_ARGUMENT,
18-
DIGITAL_ASSET_TRANSFER_FUNCTION,
1916
DIGITAL_ASSET_TRANSFER_AMOUNT,
17+
DIGITAL_ASSET_TRANSFER_FUNCTION,
18+
DIGITAL_ASSET_TYPE_ARGUMENT,
2019
} from '../constants';
20+
import { AbstractTransferTransaction } from './abstractTransferTransaction';
2121

22-
export class DigitalAssetTransfer extends Transaction {
22+
export class DigitalAssetTransfer extends AbstractTransferTransaction {
2323
constructor(coinConfig: Readonly<CoinConfig>) {
2424
super(coinConfig);
2525
this._type = TransactionType.SendNFT;

modules/sdk-coin-apt/src/lib/transaction/fungibleAssetTransfer.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { Transaction } from './transaction';
21
import {
32
AccountAddress,
43
EntryFunctionABI,
@@ -13,15 +12,16 @@ import {
1312
import { InvalidTransactionError, TransactionType } from '@bitgo/sdk-core';
1413
import { BaseCoin as CoinConfig } from '@bitgo/statics';
1514
import {
16-
FUNGIBLE_ASSET_TYPE_ARGUMENT,
17-
FUNGIBLE_ASSET_TRANSFER_FUNCTION,
18-
FUNGIBLE_ASSET_BATCH_TRANSFER_FUNCTION,
19-
FUNBIGLE_ASSET_TYPE_TAG,
2015
BATCH_FUNGIBLE_ASSET_TYPE_TAG,
16+
FUNBIGLE_ASSET_TYPE_TAG,
17+
FUNGIBLE_ASSET_BATCH_TRANSFER_FUNCTION,
18+
FUNGIBLE_ASSET_TRANSFER_FUNCTION,
19+
FUNGIBLE_ASSET_TYPE_ARGUMENT,
2120
} from '../constants';
2221
import utils from '../utils';
22+
import { AbstractTransferTransaction } from './abstractTransferTransaction';
2323

24-
export class FungibleAssetTransfer extends Transaction {
24+
export class FungibleAssetTransfer extends AbstractTransferTransaction {
2525
constructor(coinConfig: Readonly<CoinConfig>) {
2626
super(coinConfig);
2727
this._type = TransactionType.SendToken;

0 commit comments

Comments
 (0)