Skip to content

Commit 6860780

Browse files
committed
feat(sdk-coin-polyx): add token builders
TICKET: WIN-6578
1 parent ea00ff3 commit 6860780

File tree

16 files changed

+780
-42
lines changed

16 files changed

+780
-42
lines changed

modules/abstract-substrate/src/lib/iface.ts

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,70 +17,74 @@ export enum SectionNames {
1717

1818
/**
1919
* Method names for the transaction method. Names change based on the type of transaction e.g 'bond' for the staking transaction
20+
*
21+
* This is implemented as a const object with string literals to allow for extension in derived modules.
2022
*/
21-
export enum MethodNames {
23+
export const MethodNames = {
2224
/**
2325
* Transfer the entire transferable balance from the caller account.
2426
*
2527
* @see https://polkadot.js.org/docs/substrate/extrinsics/#transferalldest-multiaddress-keep_alive-bool
2628
*/
27-
TransferAll = 'transferAll',
29+
TransferAll: 'transferAll' as const,
2830
/**
2931
* Same as the transfer call, but with a check that the transfer will not kill the origin account.
3032
*
3133
* @see https://polkadot.js.org/docs/substrate/extrinsics/#transferkeepalivedest-multiaddress-value-compactu128
3234
*/
33-
TransferKeepAlive = 'transferKeepAlive',
35+
TransferKeepAlive: 'transferKeepAlive' as const,
3436
/**
3537
* Transfer funds with an optional memo attached.
3638
* The memo allows adding context or metadata to the transaction, commonly used for recordkeeping or identification.
3739
*
3840
* @see https://developers.polymesh.network/sdk-docs/enums/Generated/Types/BalancesTx/#transferwithmemo
3941
*/
40-
TransferWithMemo = 'transferWithMemo',
41-
AddStake = 'addStake',
42-
RemoveStake = 'removeStake',
43-
42+
TransferWithMemo: 'transferWithMemo' as const,
43+
AddStake: 'addStake' as const,
44+
RemoveStake: 'removeStake' as const,
4445
/**
4546
* Take the origin account as a stash and lock up value of its balance.
4647
*/
47-
Bond = 'bond',
48+
Bond: 'bond' as const,
4849
/**
4950
* Add some extra amount that have appeared in the stash free_balance into the balance up for staking.
5051
*/
51-
BondExtra = 'bondExtra',
52+
BondExtra: 'bondExtra' as const,
5253
/**
5354
* Declare the desire to nominate targets for the origin controller.
5455
*/
55-
Nominate = 'nominate',
56+
Nominate: 'nominate' as const,
5657
/**
5758
* Declare no desire to either validate or nominate.
5859
*/
59-
Chill = 'chill',
60+
Chill: 'chill' as const,
6061
/**
6162
* Schedule a portion of the stash to be unlocked ready for transfer out after the bond period ends.
6263
*/
63-
Unbond = 'unbond',
64+
Unbond: 'unbond' as const,
6465
/**
6566
* Remove any unlocked chunks from the unlocking queue from our management.
6667
*/
67-
WithdrawUnbonded = 'withdrawUnbonded',
68+
WithdrawUnbonded: 'withdrawUnbonded' as const,
6869
/**
6970
* Send a batch of dispatch calls.
7071
*/
71-
Batch = 'batch',
72+
Batch: 'batch' as const,
7273
/**
7374
* Send a batch of dispatch calls and atomically execute them.
7475
*/
75-
BatchAll = 'batchAll',
76+
BatchAll: 'batchAll' as const,
77+
} as const;
7678

77-
/**
78-
* Registers a Decentralized Identifier (DID) along with Customer Due Diligence (CDD) information.
79-
*
80-
* @see https://developers.polymesh.network/sdk-docs/enums/Generated/Types/IdentityTx/#cddregisterdidwithcdd
81-
*/
82-
RegisterDidWithCDD = 'cddRegisterDidWithCdd',
83-
}
79+
/**
80+
* Type representing the keys of the MethodNames object
81+
*/
82+
export type MethodNamesType = keyof typeof MethodNames;
83+
84+
/**
85+
* Type representing the values of the MethodNames object
86+
*/
87+
export type MethodNamesValues = (typeof MethodNames)[MethodNamesType];
8488

8589
/**
8690
* The transaction data returned from the toJson() function of a transaction
@@ -106,6 +110,7 @@ export interface TxData {
106110
netuid?: string;
107111
numSlashingSpans?: number;
108112
batchCalls?: BatchCallObject[];
113+
memo?: string;
109114
}
110115

111116
/**
@@ -193,7 +198,7 @@ export interface TxMethod {
193198
| UnbondArgs
194199
| WithdrawUnbondedArgs
195200
| BatchArgs;
196-
name: MethodNames;
201+
name: MethodNamesValues;
197202
pallet: string;
198203
}
199204

modules/abstract-substrate/src/lib/nativeTransferBuilder.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,16 @@ export abstract class NativeTransferBuilder extends TransactionBuilder {
121121
/** @inheritdoc */
122122
protected fromImplementation(rawTransaction: string): Transaction {
123123
const tx = super.fromImplementation(rawTransaction);
124-
if (this._method?.name === MethodNames.TransferKeepAlive) {
124+
if (!this._method || !this._method.args) {
125+
throw new InvalidTransactionError('Transaction method or args are undefined');
126+
}
127+
if (this._method.name === MethodNames.TransferKeepAlive) {
125128
const txMethod = this._method.args as TransferArgs;
126129
this.amount(txMethod.value);
127130
this.to({
128131
address: utils.decodeSubstrateAddress(txMethod.dest.id, this.getAddressFormat()),
129132
});
130-
} else if (this._method?.name === MethodNames.TransferAll) {
133+
} else if (this._method.name === MethodNames.TransferAll) {
131134
this._sweepFreeBalance = true;
132135
const txMethod = this._method.args as TransferAllArgs;
133136
this.sweep(txMethod.keepAlive);
@@ -136,7 +139,7 @@ export abstract class NativeTransferBuilder extends TransactionBuilder {
136139
});
137140
} else {
138141
throw new InvalidTransactionError(
139-
`Invalid Transaction Type: ${this._method?.name}. Expected a transferKeepAlive or a proxy transferKeepAlive transaction`
142+
`Invalid Transaction Type: ${this._method.name}. Expected a transferKeepAlive or a proxy transferKeepAlive transaction`
140143
);
141144
}
142145
return tx;

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

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,88 @@ import { DecodedUnsignedTx } from '@substrate/txwrapper-core/lib/types';
44

55
export type AnyJson = string | number | boolean | null | { [key: string]: AnyJson } | Array<AnyJson>;
66

7+
/**
8+
* Extended TxData interface for Polyx transactions
9+
* Adds assetId field to the base TxData interface from abstract-substrate
10+
*/
11+
export interface TxData extends Interface.TxData {
12+
assetId?: string;
13+
fromDID?: string;
14+
toDID?: string;
15+
}
16+
17+
/**
18+
* Settlement type for Polyx transactions
19+
*/
20+
export enum SettlementType {
21+
SettleOnAffirmation = 'SettleOnAffirmation',
22+
}
23+
24+
/**
25+
* Portfolio kind for Polyx transactions
26+
*/
27+
export enum PortfolioKind {
28+
Default = 'Default',
29+
}
30+
31+
/**
32+
* Method names for Polyx transactions.
33+
* Extends the base MethodNames from Interface with additional Polyx-specific methods.
34+
*/
35+
export const MethodNames = {
36+
// Include all values from the base object
37+
...Interface.MethodNames,
38+
39+
/**
40+
* Registers a Decentralized Identifier (DID) along with Customer Due Diligence (CDD) information.
41+
*
42+
* @see https://developers.polymesh.network/sdk-docs/enums/Generated/Types/IdentityTx/#cddregisterdidwithcdd
43+
*/
44+
RegisterDidWithCDD: 'cddRegisterDidWithCdd' as const,
45+
46+
/**
47+
* Pre-approves an asset.
48+
*/
49+
PreApproveAsset: 'preApproveAsset' as const,
50+
51+
AddAndAffirmWithMediators: 'addAndAffirmWithMediators' as const,
52+
} as const;
53+
54+
// Create a type that represents the keys of this object
55+
export type MethodNamesType = keyof typeof MethodNames;
56+
57+
// Create a type that represents the values of this object
58+
export type MethodNamesValues = (typeof MethodNames)[MethodNamesType];
59+
760
export interface RegisterDidWithCDDArgs extends Args {
861
targetAccount: string;
962
secondaryKeys: [];
1063
expiry: null;
1164
}
1265

13-
export interface TxMethod extends Omit<Interface.TxMethod, 'args'> {
66+
export interface PreApproveAssetArgs extends Args {
67+
assetId: string;
68+
}
69+
70+
export interface AddAndAffirmWithMediatorsArgs extends Args {
71+
venueId: null;
72+
settlementType: SettlementType.SettleOnAffirmation;
73+
tradeDate: null;
74+
valueDate: null;
75+
legs: Array<{
76+
fungible: {
77+
sender: { did: string; kind: PortfolioKind.Default };
78+
receiver: { did: string; kind: PortfolioKind.Default };
79+
assetId: string;
80+
amount: string;
81+
};
82+
}>;
83+
portfolios: Array<{ did: string; kind: PortfolioKind.Default }>;
84+
instructionMemo: string;
85+
mediators: [];
86+
}
87+
88+
export interface TxMethod extends Omit<Interface.TxMethod, 'args' | 'name'> {
1489
args:
1590
| Interface.TransferArgs
1691
| Interface.TransferAllArgs
@@ -23,7 +98,10 @@ export interface TxMethod extends Omit<Interface.TxMethod, 'args'> {
2398
| Interface.UnbondArgs
2499
| Interface.WithdrawUnbondedArgs
25100
| Interface.BatchArgs
26-
| RegisterDidWithCDDArgs;
101+
| RegisterDidWithCDDArgs
102+
| PreApproveAssetArgs
103+
| AddAndAffirmWithMediatorsArgs;
104+
name: MethodNamesValues;
27105
}
28106

29107
export interface DecodedTx extends Omit<DecodedUnsignedTx, 'method'> {

modules/sdk-coin-polyx/src/lib/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ export { TransactionBuilderFactory } from './transactionBuilderFactory';
1212
export { PolyxBaseBuilder } from './baseBuilder';
1313
export { TransferBuilder } from './transferBuilder';
1414
export { RegisterDidWithCDDBuilder } from './registerDidWithCDDBuilder';
15+
export { PreApproveAssetBuilder } from './preApproveAssetBuilder';
16+
export { TokenTransferBuilder } from './tokenTransferBuilder';
1517
export { Transaction as PolyxTransaction } from './transaction';
1618
export { BondExtraBuilder } from './bondExtraBuilder';
1719
export { BatchStakingBuilder as BatchBuilder } from './batchStakingBuilder';
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { TransactionType, InvalidTransactionError } from '@bitgo/sdk-core';
2+
import { BaseCoin as CoinConfig } from '@bitgo/statics';
3+
import { DecodedSignedTx, DecodedSigningPayload, defineMethod, UnsignedTransaction } from '@substrate/txwrapper-core';
4+
import { Interface } from '@bitgo/abstract-substrate';
5+
import { PolyxBaseBuilder } from './baseBuilder';
6+
import { TxMethod, PreApproveAssetArgs, MethodNames } from './iface';
7+
import { PreApproveAssetTransactionSchema } from './txnSchema';
8+
import { Transaction } from './transaction';
9+
10+
export class PreApproveAssetBuilder extends PolyxBaseBuilder<TxMethod, Transaction> {
11+
protected _assetId: string;
12+
protected _method: TxMethod;
13+
14+
constructor(_coinConfig: Readonly<CoinConfig>) {
15+
super(_coinConfig);
16+
this._transaction = new Transaction(_coinConfig);
17+
}
18+
19+
protected get transactionType(): TransactionType {
20+
return TransactionType.TrustLine;
21+
}
22+
23+
protected buildTransaction(): UnsignedTransaction {
24+
const baseTxInfo = this.createBaseTxInfo();
25+
return this.preApproveAsset(
26+
{
27+
assetId: this._assetId,
28+
},
29+
baseTxInfo
30+
);
31+
}
32+
33+
/**
34+
* Sets the asset ID for the pre-approval transaction.
35+
*
36+
* @param {string} assetId - The ID of the asset to be pre-approved.
37+
* @returns {this} The current instance of the builder.
38+
*/
39+
assetId(assetId: string): this {
40+
this._assetId = assetId;
41+
return this;
42+
}
43+
44+
/** @inheritdoc */
45+
protected fromImplementation(rawTransaction: string): Transaction {
46+
const tx = super.fromImplementation(rawTransaction);
47+
if (this._method?.name === MethodNames.PreApproveAsset) {
48+
const txMethod = this._method.args as PreApproveAssetArgs;
49+
this.assetId(txMethod.assetId);
50+
} else {
51+
throw new InvalidTransactionError(`Invalid Transaction Type: ${this._method?.name}. Expected preApproveAsset`);
52+
}
53+
return tx;
54+
}
55+
56+
/** @inheritdoc */
57+
validateDecodedTransaction(decodedTxn: DecodedSigningPayload | DecodedSignedTx, rawTransaction?: string): void {
58+
if (decodedTxn.method?.name === MethodNames.PreApproveAsset) {
59+
const txMethod = decodedTxn.method.args as PreApproveAssetArgs;
60+
const assetId = txMethod.assetId;
61+
62+
const validationResult = PreApproveAssetTransactionSchema.validate({ assetId });
63+
if (!validationResult) {
64+
throw new InvalidTransactionError('Invalid transaction: assetId is required');
65+
}
66+
}
67+
}
68+
69+
/**
70+
* Construct a transaction to pre-approve an asset
71+
*
72+
* @param {PreApproveAssetArgs} args Arguments to be passed to the preApproveAsset method
73+
* @param {Interface.CreateBaseTxInfo} info Base txn info required to construct the pre-approve asset txn
74+
* @returns {UnsignedTransaction} an unsigned transaction for asset pre-approval
75+
*/
76+
private preApproveAsset(args: PreApproveAssetArgs, info: Interface.CreateBaseTxInfo): UnsignedTransaction {
77+
console.log(`PreApproveAssetBuilder: preApproveAsset called with args: ${JSON.stringify(args)}`);
78+
return defineMethod(
79+
{
80+
method: {
81+
args,
82+
name: 'preApproveAsset',
83+
pallet: 'asset',
84+
},
85+
...info.baseTxInfo,
86+
},
87+
info.options
88+
);
89+
}
90+
}

modules/sdk-coin-polyx/src/lib/registerDidWithCDDBuilder.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { PolyxBaseBuilder } from './baseBuilder';
33
import { DecodedSignedTx, DecodedSigningPayload, defineMethod, UnsignedTransaction } from '@substrate/txwrapper-core';
44
import { BaseCoin as CoinConfig } from '@bitgo/statics';
55
import { TransactionType, BaseAddress, InvalidTransactionError } from '@bitgo/sdk-core';
6-
import { RegisterDidWithCDDArgs, TxMethod } from './iface';
6+
import { RegisterDidWithCDDArgs, TxMethod, MethodNames } from './iface';
77
import { RegisterDidWithCDDTransactionSchema } from './txnSchema';
88
import { Transaction } from './transaction';
99

@@ -48,18 +48,18 @@ export class RegisterDidWithCDDBuilder extends PolyxBaseBuilder<TxMethod, Transa
4848
/** @inheritdoc */
4949
protected fromImplementation(rawTransaction: string): Transaction {
5050
const tx = super.fromImplementation(rawTransaction);
51-
if (this._method?.name === Interface.MethodNames.RegisterDidWithCDD) {
51+
if (this._method?.name === MethodNames.RegisterDidWithCDD) {
5252
const txMethod = this._method.args as RegisterDidWithCDDArgs;
5353
this.to({ address: utils.decodeSubstrateAddress(txMethod.targetAccount, this.getAddressFormat()) });
5454
} else {
55-
throw new InvalidTransactionError(`Invalid Transaction Type: ${this._method?.name}. Expected transferWithMemo`);
55+
throw new InvalidTransactionError(`Invalid Transaction Type: ${this._method?.name}. Expected RegisterDidWithCDD`);
5656
}
5757
return tx;
5858
}
5959

6060
/** @inheritdoc */
6161
validateDecodedTransaction(decodedTxn: DecodedSigningPayload | DecodedSignedTx, rawTransaction?: string): void {
62-
if (decodedTxn.method?.name === Interface.MethodNames.RegisterDidWithCDD) {
62+
if (decodedTxn.method?.name === MethodNames.RegisterDidWithCDD) {
6363
const txMethod = decodedTxn.method.args as RegisterDidWithCDDArgs;
6464
const targetAccount = txMethod.targetAccount;
6565
const secondaryKeys = txMethod.secondaryKeys;

0 commit comments

Comments
 (0)