Skip to content

Commit f8295bb

Browse files
authored
Merge pull request #7290 from BitGo/COIN-6101
feat: added createAccount intent for canton wallet init
2 parents 3902e57 + f0f9964 commit f8295bb

File tree

7 files changed

+155
-1
lines changed

7 files changed

+155
-1
lines changed

modules/bitgo/test/v2/unit/wallet.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,6 +1247,87 @@ describe('V2 Wallet:', function () {
12471247
});
12481248
});
12491249

1250+
describe('Canton tests: ', () => {
1251+
let cantonWallet: Wallet;
1252+
const cantonBitgo = TestBitGo.decorate(BitGo, { env: 'mock' });
1253+
cantonBitgo.initializeTestVars();
1254+
const walletData = {
1255+
id: '598f606cd8fc24710d2ebadb1d9459bb',
1256+
coinSpecific: {
1257+
baseAddress: '12205::12205b4e3537a95126d90604592344d8ad3c3ddccda4f79901954280ee19c576714d',
1258+
pendingChainInitialization: true,
1259+
lastChainIndex: { 0: 0 },
1260+
},
1261+
coin: 'tcanton',
1262+
keys: [
1263+
'598f606cd8fc24710d2ebad89dce86c2',
1264+
'598f606cc8e43aef09fcb785221d9dd2',
1265+
'5935d59cf660764331bafcade1855fd7',
1266+
],
1267+
multisigType: 'tss',
1268+
};
1269+
1270+
before(async function () {
1271+
cantonWallet = new Wallet(bitgo, bitgo.coin('tcanton'), walletData);
1272+
nock(bgUrl).get(`/api/v2/${cantonWallet.coin()}/key/${cantonWallet.keyIds()[0]}`).times(3).reply(200, {
1273+
id: '598f606cd8fc24710d2ebad89dce86c2',
1274+
pub: '5f8WmC2uW9SAk7LMX2r4G1Bx8MMwx8sdgpotyHGodiZo',
1275+
source: 'user',
1276+
encryptedPrv:
1277+
'{"iv":"hNK3rg82P1T94MaueXFAbA==","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"cV4wU4EzPjs=","ct":"9VZX99Ztsb6p75Cxl2lrcXBplmssIAQ9k7ZA81vdDYG4N5dZ36BQNWVfDoelj9O31XyJ+Xri0XKIWUzl0KKLfUERplmtNoOCn5ifJcZwCrOxpHZQe3AJ700o8Wmsrk5H"}',
1278+
coinSpecific: {},
1279+
});
1280+
1281+
nock(bgUrl).get(`/api/v2/${cantonWallet.coin()}/key/${cantonWallet.keyIds()[1]}`).times(2).reply(200, {
1282+
id: '598f606cc8e43aef09fcb785221d9dd2',
1283+
pub: 'G1s43JTzNZzqhUn4aNpwgcc6wb9FUsZQD5JjffG6isyd',
1284+
encryptedPrv:
1285+
'{"iv":"UFrt/QlIUR1XeQafPBaAlw==","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"7VPBYaJXPm8=","ct":"ajFKv2y8yaIBXQ39sAbBWcnbiEEzbjS4AoQtp5cXYqjeDRxt3aCxemPm22pnkJaCijFjJrMHbkmsNhNYzHg5aHFukN+nEAVssyNwHbzlhSnm8/BVN50yAdAAtWreh8cp"}',
1286+
source: 'backup',
1287+
coinSpecific: {},
1288+
});
1289+
1290+
nock(bgUrl).get(`/api/v2/${cantonWallet.coin()}/key/${cantonWallet.keyIds()[2]}`).times(2).reply(200, {
1291+
id: '5935d59cf660764331bafcade1855fd7',
1292+
pub: 'GH1LV1e9FdqGe8U2c8PMEcma3fDeh1ktcGVBrD3AuFqx',
1293+
encryptedPrv:
1294+
'{"iv":"iIuWOHIOErEDdiJn6g46mg==","v":1,"iter":10000,"ks":256,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Rzh7RRJksj0=","ct":"rcNICUfp9FakT53l+adB6XKzS1vNTc0Qq9jAtqnxA+ScssiS4Q0l3sgG/0gDy5DaZKtXryKBDUvGsi7b/fYaFCUpAoZn/VZTOhOUN/mo7ZHb4OhOXL29YPPkiryAq9Cr"}',
1295+
source: 'bitgo',
1296+
coinSpecific: {},
1297+
});
1298+
});
1299+
1300+
after(async function () {
1301+
nock.cleanAll();
1302+
});
1303+
1304+
it('Should build wallet initialization transactions correctly', async function () {
1305+
const txRequestNock = nock(bgUrl)
1306+
.post(`/api/v2/wallet/${cantonWallet.id()}/txrequests`)
1307+
.reply((url, body) => {
1308+
const bodyParams = body as any;
1309+
bodyParams.intent.intentType.should.equal('createAccount');
1310+
bodyParams.intent.recipients.length.should.equal(0);
1311+
return [
1312+
200,
1313+
{
1314+
apiVersion: 'full',
1315+
transactions: [
1316+
{
1317+
unsignedTx: {
1318+
serializedTxHex: 'fake transaction',
1319+
feeInfo: 'fake fee info',
1320+
},
1321+
},
1322+
],
1323+
},
1324+
];
1325+
});
1326+
await cantonWallet.sendWalletInitialization();
1327+
txRequestNock.isDone().should.equal(true);
1328+
});
1329+
});
1330+
12501331
describe('Solana tests: ', () => {
12511332
let solWallet: Wallet;
12521333
const passphrase = '#Bondiola1234';

modules/sdk-coin-canton/src/canton.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ export class Canton extends BaseCoin {
6767
return multisigTypes.tss;
6868
}
6969

70+
/** inherited doc */
71+
requiresWalletInitializationTransaction(): boolean {
72+
return true;
73+
}
74+
7075
getMPCAlgorithm(): MPCAlgorithm {
7176
return 'eddsa';
7277
}

modules/sdk-coin-canton/src/lib/walletInitialization/walletInitTransaction.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export class WalletInitTransaction extends BaseTransaction {
7979
if (!this._preparedParty) {
8080
throw new InvalidTransactionError('Empty transaction data');
8181
}
82-
return Buffer.from(this._preparedParty.multiHash);
82+
return Buffer.from(this._preparedParty.multiHash, 'base64');
8383
}
8484

8585
fromRawTransaction(rawTx: string): void {

modules/sdk-core/src/bitgo/baseCoin/baseCoin.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,14 @@ export abstract class BaseCoin implements IBaseCoin {
381381
return false;
382382
}
383383

384+
/**
385+
* Check whether a coin requires wallet initialization
386+
* @returns {boolean}
387+
*/
388+
requiresWalletInitializationTransaction(): boolean {
389+
return false;
390+
}
391+
384392
/**
385393
* Check whether a coin supports signing of Typed data
386394
* @returns {boolean}

modules/sdk-core/src/bitgo/baseCoin/iBaseCoin.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,4 +604,5 @@ export interface IBaseCoin {
604604
* @param {string} params.multiSigType - The type of multisig (e.g. 'onchain' or 'tss')
605605
*/
606606
assertIsValidKey({ publicKey, encryptedPrv, walletPassphrase, multiSigType }: AuditKeyParams): void;
607+
requiresWalletInitializationTransaction(): boolean;
607608
}

modules/sdk-core/src/bitgo/wallet/iWallet.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,11 @@ export type SendNFTResult = {
893893
pendingApproval: PendingApprovalData;
894894
};
895895

896+
export type WalletInitResult = {
897+
success: PrebuildTransactionResult[];
898+
failure: Error[];
899+
};
900+
896901
export interface IWallet {
897902
bitgo: BitGoBase;
898903
baseCoin: IBaseCoin;
@@ -985,6 +990,7 @@ export interface IWallet {
985990
buildTokenEnablements(params?: BuildTokenEnablementOptions): Promise<PrebuildTransactionResult[]>;
986991
sendTokenEnablement(params?: PrebuildAndSignTransactionOptions): Promise<any>;
987992
sendTokenEnablements(params?: BuildTokenEnablementOptions): Promise<any>;
993+
sendWalletInitialization(params?: PrebuildTransactionOptions): Promise<WalletInitResult>;
988994
signMessage(params: WalletSignMessageOptions): Promise<SignedMessage>;
989995
buildSignMessageRequest(params: WalletSignMessageOptions): Promise<TxRequest>;
990996
signTypedData(params: WalletSignTypedDataOptions): Promise<SignedMessage>;

modules/sdk-core/src/bitgo/wallet/wallet.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ import {
123123
WalletSignTypedDataOptions,
124124
WalletType,
125125
BuildTokenApprovalResponse,
126+
WalletInitResult,
126127
} from './iWallet';
127128

128129
const debug = require('debug')('bitgo:v2:wallet');
@@ -3319,6 +3320,48 @@ export class Wallet implements IWallet {
33193320
};
33203321
}
33213322

3323+
/**
3324+
* The chain canton, requires the wallet to be initialized by sending out a transaction
3325+
* to be onboarded onto the validator.
3326+
* Builds, Signs and sends a transaction that initializes the canton wallet
3327+
* @param params
3328+
*/
3329+
public async sendWalletInitialization(params: PrebuildAndSignTransactionOptions = {}): Promise<WalletInitResult> {
3330+
if (!this.baseCoin.requiresWalletInitializationTransaction()) {
3331+
throw new Error(`Wallet initialization is not required for ${this.baseCoin.getFullName()}`);
3332+
}
3333+
if (this._wallet.multisigType !== 'tss') {
3334+
throw new Error('Wallet initialization transaction is only supported for TSS wallets');
3335+
}
3336+
if (params.reqId) {
3337+
this.bitgo.setRequestTracer(params.reqId);
3338+
}
3339+
const buildParams: PrebuildTransactionOptions = _.pick(params, this.prebuildWhitelistedParams());
3340+
if (!buildParams.type) {
3341+
buildParams.type = 'createAccount';
3342+
}
3343+
const prebuildTx = await this.prebuildTransaction(buildParams);
3344+
const unsignedBuildWithOptions: PrebuildAndSignTransactionOptions = {
3345+
...params,
3346+
prebuildTx,
3347+
};
3348+
if (typeof params.prebuildTx === 'string' || params.prebuildTx?.buildParams?.type !== 'createAccount') {
3349+
throw new Error('Invalid build of wallet init');
3350+
}
3351+
const successfulTxs: PrebuildTransactionResult[] = [];
3352+
const failedTxs = new Array<Error>();
3353+
try {
3354+
const sendTx = await this.sendManyTxRequests(unsignedBuildWithOptions);
3355+
successfulTxs.push(sendTx);
3356+
} catch (e) {
3357+
failedTxs.push(e);
3358+
}
3359+
return {
3360+
success: successfulTxs,
3361+
failure: failedTxs,
3362+
};
3363+
}
3364+
33223365
/* MARK: TSS Helpers */
33233366

33243367
/**
@@ -3439,6 +3482,16 @@ export class Wallet implements IWallet {
34393482
params.preview
34403483
);
34413484
break;
3485+
case 'createAccount':
3486+
txRequest = await this.tssUtils!.prebuildTxWithIntent(
3487+
{
3488+
reqId,
3489+
intentType: 'createAccount',
3490+
},
3491+
apiVersion,
3492+
params.preview
3493+
);
3494+
break;
34423495
case 'customTx':
34433496
txRequest = await this.tssUtils!.prebuildTxWithIntent(
34443497
{

0 commit comments

Comments
 (0)