Skip to content

Commit 8f8eb14

Browse files
feat: unsigned sweep for songbird
Ticket: WIN-4884 TICKET: WIN-4844
1 parent 7ffbfce commit 8f8eb14

File tree

4 files changed

+118
-2
lines changed

4 files changed

+118
-2
lines changed

modules/sdk-coin-sgb/src/lib/transactionBuilder.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { TransferBuilder } from './transferBuilder';
66

77
export class TransactionBuilder extends AbstractTransactionBuilder {
88
protected _transfer: TransferBuilder;
9+
private _signatures: any;
910

1011
constructor(_coinConfig: Readonly<CoinConfig>) {
1112
super(_coinConfig);
@@ -24,6 +25,11 @@ export class TransactionBuilder extends AbstractTransactionBuilder {
2425
return this._transfer;
2526
}
2627

28+
addSignature(publicKey, signature) {
29+
this._signatures = [];
30+
this._signatures.push({ publicKey, signature });
31+
}
32+
2733
protected getContractData(addresses: string[]): string {
2834
throw new Error('Method not implemented.');
2935
}

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@
88

99
import { BaseCoin, BitGoBase, common, MPCAlgorithm, MultisigType, multisigTypes } from '@bitgo/sdk-core';
1010
import { BaseCoin as StaticsBaseCoin, coins } from '@bitgo/statics';
11-
import { AbstractEthLikeNewCoins, recoveryBlockchainExplorerQuery } from '@bitgo/abstract-eth';
11+
import {
12+
AbstractEthLikeNewCoins,
13+
recoveryBlockchainExplorerQuery,
14+
UnsignedSweepTxMPCv2,
15+
RecoverOptions,
16+
OfflineVaultTxInfo,
17+
} from '@bitgo/abstract-eth';
1218
import { TransactionBuilder } from './lib';
1319

1420
export class Sgb extends AbstractEthLikeNewCoins {
@@ -39,6 +45,10 @@ export class Sgb extends AbstractEthLikeNewCoins {
3945
return 'ecdsa';
4046
}
4147

48+
protected async buildUnsignedSweepTxnTSS(params: RecoverOptions): Promise<OfflineVaultTxInfo | UnsignedSweepTxMPCv2> {
49+
return this.buildUnsignedSweepTxnMPCv2(params);
50+
}
51+
4252
async recoveryBlockchainExplorerQuery(query: Record<string, string>): Promise<Record<string, unknown>> {
4353
const apiToken = common.Environments[this.bitgo.getEnv()].sgbExplorerApiToken;
4454
const explorerUrl = common.Environments[this.bitgo.getEnv()].sgbExplorerBaseUrl;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const getTxListRequestUnsignedSweep: Record<string, string> = {
2+
module: 'account',
3+
action: 'txlist',
4+
address: '0x1469e6e519ff8bf398b76b4be0b50701b999f14c',
5+
};
6+
7+
const getTxListResponseUnsignedSweep: Record<string, unknown> = {
8+
status: '1',
9+
result: [
10+
{
11+
hash: '0xede855d43d70ea1bb75db63d4f75113dae0845f0d4bdb0b2d8bda55249c70812',
12+
nonce: '23',
13+
from: '0x1469e6e519ff8bf398b76b4be0b50701b999f14c',
14+
},
15+
],
16+
message: 'OK',
17+
};
18+
19+
const getBalanceRequestUnsignedSweep: Record<string, string> = {
20+
module: 'account',
21+
action: 'balance',
22+
address: '0x1469e6e519ff8bf398b76b4be0b50701b999f14c',
23+
};
24+
25+
const getBalanceResponseUnsignedSweep: Record<string, unknown> = {
26+
status: '1',
27+
result: '100000000000000000',
28+
message: 'OK',
29+
};
30+
31+
export const mockDataUnsignedSweep = {
32+
userKey:
33+
'038412b0e79372ca618978f2bc9fc944c504e828050a55a19fdfeca93cff5ec6562ae94f204a3f99e87334f812be8a54927ff24572bc666c5436887d2e42c0997d',
34+
backupKey:
35+
'038412b0e79372ca618978f2bc9fc944c504e828050a55a19fdfeca93cff5ec6562ae94f204a3f99e87334f812be8a54927ff24572bc666c5436887d2e42c0997d',
36+
derivationPath: 'm/0',
37+
derivationSeed: '',
38+
walletBaseAddress: '0x1469e6e519ff8bf398b76b4be0b50701b999f14c',
39+
recoveryDestination: '0x07efb1aa5e41b70b21facd3d287548ebf632a165',
40+
getTxListRequest: getTxListRequestUnsignedSweep,
41+
getTxListResponse: getTxListResponseUnsignedSweep,
42+
getBalanceRequest: getBalanceRequestUnsignedSweep,
43+
getBalanceResponse: getBalanceResponseUnsignedSweep,
44+
};

modules/sdk-coin-sgb/test/unit/sgb.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
import 'should';
1+
import * as should from 'should';
22

33
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
44
import { BitGoAPI } from '@bitgo/sdk-api';
55

66
import { Sgb, Tsgb } from '../../src/index';
7+
import { UnsignedSweepTxMPCv2 } from '@bitgo/abstract-eth';
8+
import { mockDataUnsignedSweep } from '../resources';
9+
import nock from 'nock';
10+
import { common } from '@bitgo/sdk-core';
711

812
const bitgo: TestBitGoAPI = TestBitGo.decorate(BitGoAPI, { env: 'test' });
913

@@ -40,3 +44,55 @@ describe('sgb', function () {
4044
});
4145
});
4246
});
47+
48+
describe('Build Unsigned Sweep for Self-Custody Cold Wallets - (MPCv2)', function () {
49+
const bitgo = TestBitGo.decorate(BitGoAPI, { env: 'test' });
50+
const explorerUrl = common.Environments[bitgo.getEnv()].sgbExplorerBaseUrl as string;
51+
52+
it('should generate an unsigned sweep without derivation path', async () => {
53+
nock(explorerUrl)
54+
.get('/api')
55+
.twice()
56+
.query(mockDataUnsignedSweep.getTxListRequest)
57+
.reply(200, mockDataUnsignedSweep.getTxListResponse);
58+
nock(explorerUrl)
59+
.get('/api')
60+
.query(mockDataUnsignedSweep.getBalanceRequest)
61+
.reply(200, mockDataUnsignedSweep.getBalanceResponse);
62+
63+
const baseCoin: any = bitgo.coin('tsgb');
64+
const transaction = (await baseCoin.recover({
65+
userKey: mockDataUnsignedSweep.userKey,
66+
backupKey: mockDataUnsignedSweep.backupKey,
67+
walletContractAddress: mockDataUnsignedSweep.walletBaseAddress,
68+
recoveryDestination: mockDataUnsignedSweep.recoveryDestination,
69+
isTss: true,
70+
eip1559: { maxFeePerGas: 20000000000, maxPriorityFeePerGas: 10000000000 },
71+
gasLimit: 500000,
72+
replayProtectionOptions: {
73+
chain: 16,
74+
hardfork: 'london',
75+
},
76+
})) as UnsignedSweepTxMPCv2;
77+
should.exist(transaction);
78+
transaction.should.have.property('txRequests');
79+
transaction.txRequests.length.should.equal(1);
80+
const txRequest = transaction.txRequests[0];
81+
txRequest.should.have.property('walletCoin');
82+
txRequest.walletCoin.should.equal('tsgb');
83+
txRequest.should.have.property('transactions');
84+
txRequest.transactions.length.should.equal(1);
85+
const tx = txRequest.transactions[0];
86+
tx.should.have.property('nonce');
87+
tx.should.have.property('unsignedTx');
88+
tx.unsignedTx.should.have.property('serializedTxHex');
89+
tx.unsignedTx.should.have.property('signableHex');
90+
tx.unsignedTx.should.have.property('derivationPath');
91+
tx.unsignedTx.should.have.property('feeInfo');
92+
tx.unsignedTx.feeInfo?.should.have.property('fee');
93+
tx.unsignedTx.feeInfo?.should.have.property('feeString');
94+
tx.unsignedTx.should.have.property('parsedTx');
95+
tx.unsignedTx.parsedTx?.should.have.property('spendAmount');
96+
tx.unsignedTx.parsedTx?.should.have.property('outputs');
97+
});
98+
});

0 commit comments

Comments
 (0)