-
Notifications
You must be signed in to change notification settings - Fork 307
Expand file tree
/
Copy pathtransferBuilder.ts
More file actions
152 lines (138 loc) · 4.87 KB
/
Copy pathtransferBuilder.ts
File metadata and controls
152 lines (138 loc) · 4.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import BigNumber from 'bignumber.js';
import { Interface, Schema } from '@bitgo/abstract-substrate';
import { Transaction } from './transaction';
import { TxMethod } from './iface';
import { PolyxBaseBuilder } from './baseBuilder';
import { DecodedSignedTx, DecodedSigningPayload, defineMethod, UnsignedTransaction } from '@substrate/txwrapper-core';
import { BaseCoin as CoinConfig } from '@bitgo/statics';
import { BaseAddress, InvalidTransactionError, TransactionType } from '@bitgo/sdk-core';
import utils from './utils';
export class TransferBuilder extends PolyxBaseBuilder<TxMethod, Transaction> {
protected _amount: string;
protected _to: string;
protected _memo: string;
constructor(_coinConfig: Readonly<CoinConfig>) {
super(_coinConfig);
this.material(utils.getMaterial(_coinConfig.network.type));
}
protected get transactionType(): TransactionType {
return TransactionType.Send;
}
/**
* Construct an unsigned `transferWithMemo` transaction using the provided details.
*
* @returns {UnsignedTransaction} The constructed unsigned transferWithMemo transaction.
*/
protected buildTransaction(): UnsignedTransaction {
const baseTxInfo = this.createBaseTxInfo();
return this.TransferWithMemo(
{
dest: { id: this._to },
value: this._amount,
memo: this._memo,
},
baseTxInfo
);
}
/**
*
* The amount for transfer transaction.
*
* @param {string} amount
* @returns {TransferBuilder} This transfer builder.
*/
amount(amount: string): this {
this.validateValue(new BigNumber(amount));
this._amount = amount;
return this;
}
/**
*
* The destination address for transfer transaction.
*
* @param {string} dest
* @returns {TransferBuilder} This transfer builder.
*/
to({ address }: BaseAddress): this {
this.validateAddress({ address });
this._to = address;
return this;
}
/**
* The memo to attach to the transfer transaction.
* Encodes the memo as UTF-8 bytes right-padded with zero bytes to 32 bytes,
* matching the Polymesh Memo type ([u8; 32]).
*
* @param {string} memo The memo string to include.
* @returns {TransferBuilder} This transfer builder.
*/
memo(memo: string): this {
// fromImplementation passes the decoded on-chain hex (0x + 64 hex chars) — pass through unchanged
if (/^0x[0-9a-fA-F]{64}$/.test(memo)) {
this._memo = memo;
return this;
}
const memoBytes = Buffer.from(memo, 'utf8');
if (memoBytes.length > 32) {
throw new Error('Memo must be 32 bytes or fewer when UTF-8 encoded');
}
const paddedBuffer = Buffer.alloc(32, 0);
memoBytes.copy(paddedBuffer, 0);
this._memo = '0x' + paddedBuffer.toString('hex');
return this;
}
/** @inheritdoc */
validateDecodedTransaction(decodedTxn: DecodedSigningPayload | DecodedSignedTx, rawTransaction?: string): void {
if (decodedTxn.method?.name === Interface.MethodNames.TransferWithMemo) {
const txMethod = decodedTxn.method.args as Interface.TransferWithMemoArgs;
const amount = `${txMethod.value}`;
const to = txMethod.dest.id;
const memo = txMethod.memo;
const validationResult = Schema.TransferWithMemoTransactionSchema.validate({ amount, to, memo });
if (validationResult.error) {
throw new InvalidTransactionError(`Invalid transaction: ${validationResult.error.message}`);
}
}
}
/** @inheritdoc */
protected fromImplementation(rawTransaction: string): Transaction {
const tx = super.fromImplementation(rawTransaction);
if (!this._method || !this._method.args) {
throw new InvalidTransactionError('Transaction method or args are undefined');
}
if (this._method?.name === Interface.MethodNames.TransferWithMemo) {
const txMethod = this._method.args as Interface.TransferWithMemoArgs;
this.amount(txMethod.value);
this.to({
address: utils.decodeSubstrateAddress(txMethod.dest.id, utils.getAddressFormat(this._coinConfig.name)),
});
this.memo(txMethod.memo);
} else {
throw new InvalidTransactionError(`Invalid Transaction Type: ${this._method.name}. Expected transferWithMemo`);
}
return tx;
}
/**
* Construct a transaction to transfer funds with an attached memo.
*
* @param {Interface.TransferWithMemoArgs} args Arguments to be passed to the transferWithMemo method
* @param {Interface.CreateBaseTxInfo} info Base txn info required to construct the transfer transaction
* @returns {UnsignedTransaction} An unsigned transferWithMemo transaction
*/
private TransferWithMemo(
args: Interface.TransferWithMemoArgs,
info: Interface.CreateBaseTxInfo
): UnsignedTransaction {
return defineMethod(
{
method: {
args,
name: 'transferWithMemo',
pallet: 'balances',
},
...info.baseTxInfo,
},
info.options
);
}
}