Skip to content

Commit 688e299

Browse files
authored
Merge pull request #5410 from BitGo/WIN-4299
feat: implement abstract substrate functions
2 parents b27b91e + 20d831b commit 688e299

File tree

11 files changed

+1201
-11
lines changed

11 files changed

+1201
-11
lines changed

modules/abstract-substrate/package.json

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"author": "BitGo SDK Team <[email protected]>",
1717
"license": "MIT",
1818
"engines": {
19-
"node": ">=18 <21"
19+
"node": ">=14 <21"
2020
},
2121
"repository": {
2222
"type": "git",
@@ -39,6 +39,16 @@
3939
},
4040
"dependencies": {
4141
"@bitgo/sdk-core": "^28.20.0",
42-
"@bitgo/statics": "^50.20.0"
42+
"@bitgo/statics": "^50.20.0",
43+
"@polkadot/keyring": "13.2.3",
44+
"@polkadot/types": "14.1.1",
45+
"@polkadot/util": "13.2.3",
46+
"@polkadot/util-crypto": "13.2.3",
47+
"@substrate/txwrapper-core": "7.5.2",
48+
"@substrate/txwrapper-polkadot": "7.5.2",
49+
"bs58": "^4.0.1",
50+
"hi-base32": "^0.5.1",
51+
"lodash": "^4.17.15",
52+
"tweetnacl": "^1.0.3"
4353
}
4454
}

modules/abstract-substrate/src/abstractSubstrateCoin.ts

Lines changed: 99 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as _ from 'lodash';
12
import {
23
BaseCoin,
34
BitGoBase,
@@ -6,14 +7,44 @@ import {
67
ParsedTransaction,
78
ParseTransactionOptions,
89
SignedTransaction,
9-
SignTransactionOptions,
10+
SignTransactionOptions as BaseSignTransactionOptions,
1011
VerifyAddressOptions,
1112
VerifyTransactionOptions,
1213
} from '@bitgo/sdk-core';
1314
import { BaseCoin as StaticsBaseCoin, CoinFamily } from '@bitgo/statics';
15+
import { Interface, KeyPair as SubstrateKeyPair, Utils } from './lib';
16+
17+
const utils = Utils.default;
18+
19+
export const DEFAULT_SCAN_FACTOR = 20; // default number of receive addresses to scan for funds
20+
21+
export interface SignTransactionOptions extends BaseSignTransactionOptions {
22+
txPrebuild: TransactionPrebuild;
23+
prv: string;
24+
}
25+
26+
export interface TransactionPrebuild {
27+
txHex: string;
28+
transaction: Interface.TxData;
29+
}
30+
31+
export interface ExplainTransactionOptions {
32+
txPrebuild: TransactionPrebuild;
33+
publicKey: string;
34+
feeInfo: {
35+
fee: string;
36+
};
37+
}
38+
39+
export interface VerifiedTransactionParameters {
40+
txHex: string;
41+
prv: string;
42+
}
1443

1544
export class SubstrateCoin extends BaseCoin {
1645
protected readonly _staticsCoin: Readonly<StaticsBaseCoin>;
46+
readonly MAX_VALIDITY_DURATION = 2400;
47+
1748
protected constructor(bitgo: BitGoBase, staticsCoin?: Readonly<StaticsBaseCoin>) {
1849
super(bitgo);
1950

@@ -37,7 +68,7 @@ export class SubstrateCoin extends BaseCoin {
3768

3869
/** @inheritDoc **/
3970
getBaseFactor(): string | number {
40-
throw new Error('Method not implemented');
71+
return Math.pow(10, this._staticsCoin.decimalPlaces);
4172
}
4273

4374
/** @inheritDoc **/
@@ -67,12 +98,20 @@ export class SubstrateCoin extends BaseCoin {
6798

6899
/** @inheritDoc **/
69100
generateKeyPair(seed?: Buffer): KeyPair {
70-
throw new Error('Method not implemented');
101+
const keyPair = seed ? utils.keyPairFromSeed(new Uint8Array(seed)) : new SubstrateKeyPair();
102+
const keys = keyPair.getKeys();
103+
if (!keys.prv) {
104+
throw new Error('Missing prv in key generation.');
105+
}
106+
return {
107+
pub: keys.pub,
108+
prv: keys.prv,
109+
};
71110
}
72111

73112
/** @inheritDoc **/
74113
isValidPub(pub: string): boolean {
75-
throw new Error('Method not implemented');
114+
return utils.isValidPublicKey(pub);
76115
}
77116

78117
/** @inheritDoc **/
@@ -86,17 +125,68 @@ export class SubstrateCoin extends BaseCoin {
86125
}
87126

88127
/** @inheritDoc **/
89-
verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
90-
throw new Error('Method not implemented');
128+
async verifyTransaction(params: VerifyTransactionOptions): Promise<boolean> {
129+
const { txParams } = params;
130+
if (Array.isArray(txParams.recipients) && txParams.recipients.length > 1) {
131+
throw new Error(
132+
`${this.getChain()} doesn't support sending to more than 1 destination address within a single transaction. Try again, using only a single recipient.`
133+
);
134+
}
135+
return true;
91136
}
92137

93138
/** @inheritDoc **/
94139
isValidAddress(address: string): boolean {
95-
throw new Error('Method not implemented.');
140+
return utils.isValidAddress(address);
141+
}
142+
143+
verifySignTransactionParams(params: SignTransactionOptions): VerifiedTransactionParameters {
144+
const prv = params.prv;
145+
146+
const txHex = params.txPrebuild.txHex;
147+
148+
if (!txHex) {
149+
throw new Error('missing txPrebuild parameter');
150+
}
151+
152+
if (!_.isString(txHex)) {
153+
throw new Error(`txPrebuild must be an object, got type ${typeof txHex}`);
154+
}
155+
156+
if (!prv) {
157+
throw new Error('missing prv parameter to sign transaction');
158+
}
159+
160+
if (!_.isString(prv)) {
161+
throw new Error(`prv must be a string, got type ${typeof prv}`);
162+
}
163+
164+
if (!_.has(params, 'pubs')) {
165+
throw new Error('missing public key parameter to sign transaction');
166+
}
167+
168+
return { txHex, prv };
96169
}
97170

98171
/** @inheritDoc **/
99-
signTransaction(params: SignTransactionOptions): Promise<SignedTransaction> {
100-
throw new Error('Method not implemented.');
172+
async signTransaction(params: SignTransactionOptions): Promise<SignedTransaction> {
173+
const { txHex, prv } = this.verifySignTransactionParams(params);
174+
const factory = this.getBuilder();
175+
const txBuilder = factory.from(txHex);
176+
const keyPair = new SubstrateKeyPair({ prv: prv });
177+
const { referenceBlock, blockNumber, transactionVersion, sender } = params.txPrebuild.transaction;
178+
179+
txBuilder
180+
.validity({ firstValid: blockNumber, maxDuration: this.MAX_VALIDITY_DURATION })
181+
.referenceBlock(referenceBlock)
182+
.version(transactionVersion)
183+
.sender({ address: sender })
184+
.sign({ key: keyPair.getKeys().prv });
185+
const transaction = await txBuilder.build();
186+
if (!transaction) {
187+
throw new Error('Invalid transaction');
188+
}
189+
const signedTxHex = transaction.toBroadcastFormat();
190+
return { txHex: signedTxHex };
101191
}
102192
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export * from './lib';
12
export { SubstrateCoin } from './abstractSubstrateCoin';

0 commit comments

Comments
 (0)