Skip to content

Commit 5eefcdd

Browse files
authored
Merge pull request #123 from BitGo/WP-5491/consolidateunspents-issue
feat(awm): fix consolidate unspent response
2 parents cb4355b + da4c125 commit 5eefcdd

File tree

2 files changed

+127
-1
lines changed

2 files changed

+127
-1
lines changed

src/__tests__/api/master/consolidateUnspents.test.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,121 @@ describe('POST /api/:coin/wallet/:walletId/consolidateunspents', () => {
173173
sinon.assert.calledOnce(consolidateUnspentsStub);
174174
});
175175

176+
it('should handle array result from consolidateUnspents and return first element', async () => {
177+
const walletGetNock = nock(bitgoApiUrl)
178+
.get(`/api/v2/${coin}/wallet/${walletId}`)
179+
.matchHeader('authorization', `Bearer ${accessToken}`)
180+
.reply(200, mockWalletData);
181+
182+
const keychainGetNock = nock(bitgoApiUrl)
183+
.get(`/api/v2/${coin}/key/user-key-id`)
184+
.matchHeader('authorization', `Bearer ${accessToken}`)
185+
.reply(200, mockUserKeychain);
186+
187+
const mockArrayResult = [
188+
{
189+
transfer: {
190+
entries: [
191+
{ address: 'tb1qu...', value: -4000 },
192+
{ address: 'tb1qle...', value: -4000 },
193+
{ address: 'tb1qtw...', value: 2714, isChange: true },
194+
],
195+
id: 'first-transfer-id',
196+
coin: 'tbtc',
197+
wallet: '685abbf19ca95b79f88e0b41d9337109',
198+
txid: 'first-tx-id',
199+
status: 'signed',
200+
},
201+
txid: 'first-tx-id',
202+
tx: '01000000000102first...',
203+
status: 'signed',
204+
},
205+
];
206+
207+
const consolidateUnspentsStub = sinon
208+
.stub(Wallet.prototype, 'consolidateUnspents')
209+
.resolves(mockArrayResult);
210+
211+
const requestPayload = {
212+
pubkey: mockUserKeychain.pub,
213+
source: 'user' as const,
214+
feeRate: 1000,
215+
bulk: true,
216+
};
217+
218+
const response = await agent
219+
.post(`/api/${coin}/wallet/${walletId}/consolidateunspents`)
220+
.set('Authorization', `Bearer ${accessToken}`)
221+
.send(requestPayload);
222+
223+
response.status.should.equal(200);
224+
// Should return only the first element from the array
225+
response.body.should.have.property('transfer');
226+
response.body.should.have.property('txid', 'first-tx-id');
227+
response.body.should.have.property('tx', '01000000000102first...');
228+
response.body.should.have.property('status', 'signed');
229+
response.body.transfer.should.have.property('id', 'first-transfer-id');
230+
response.body.transfer.should.have.property('txid', 'first-tx-id');
231+
response.body.transfer.should.have.property('status', 'signed');
232+
response.body.transfer.should.have.property('entries').which.is.Array();
233+
234+
walletGetNock.done();
235+
keychainGetNock.done();
236+
sinon.assert.calledOnce(consolidateUnspentsStub);
237+
});
238+
239+
it('should fail when consolidateUnspents returns array with more than one element', async () => {
240+
const walletGetNock = nock(bitgoApiUrl)
241+
.get(`/api/v2/${coin}/wallet/${walletId}`)
242+
.matchHeader('authorization', `Bearer ${accessToken}`)
243+
.reply(200, mockWalletData);
244+
245+
const keychainGetNock = nock(bitgoApiUrl)
246+
.get(`/api/v2/${coin}/key/user-key-id`)
247+
.matchHeader('authorization', `Bearer ${accessToken}`)
248+
.reply(200, mockUserKeychain);
249+
250+
const mockArrayResult = [
251+
{
252+
txid: 'first-tx-id',
253+
tx: '01000000000102first...',
254+
status: 'signed',
255+
},
256+
{
257+
txid: 'second-tx-id',
258+
tx: '01000000000102second...',
259+
status: 'signed',
260+
},
261+
];
262+
263+
const consolidateUnspentsStub = sinon
264+
.stub(Wallet.prototype, 'consolidateUnspents')
265+
.resolves(mockArrayResult);
266+
267+
const requestPayload = {
268+
pubkey: mockUserKeychain.pub,
269+
source: 'user' as const,
270+
feeRate: 1000,
271+
};
272+
273+
const response = await agent
274+
.post(`/api/${coin}/wallet/${walletId}/consolidateunspents`)
275+
.set('Authorization', `Bearer ${accessToken}`)
276+
.send(requestPayload);
277+
278+
response.status.should.equal(500);
279+
response.body.should.have.property('error', 'Internal Server Error');
280+
response.body.should.have.property('name', 'Error');
281+
response.body.should.have.property(
282+
'details',
283+
'Expected single consolidation result, but received 2 results',
284+
);
285+
286+
walletGetNock.done();
287+
keychainGetNock.done();
288+
sinon.assert.calledOnce(consolidateUnspentsStub);
289+
});
290+
176291
it('should succeed in consolidating unspents with all optional parameters', async () => {
177292
const walletGetNock = nock(bitgoApiUrl)
178293
.get(`/api/v2/${coin}/wallet/${walletId}`)

src/masterBitgoExpress/handlers/handleConsolidateUnspents.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,18 @@ export async function handleConsolidateUnspents(
3838
};
3939

4040
// Send consolidate unspents
41-
const result = await wallet.consolidateUnspents(consolidationParams);
41+
let result = await wallet.consolidateUnspents(consolidationParams);
42+
43+
if (Array.isArray(result)) {
44+
if (result.length === 1) {
45+
result = result[0];
46+
} else if (result.length > 1) {
47+
throw new Error(
48+
`Expected single consolidation result, but received ${result.length} results`,
49+
);
50+
}
51+
}
52+
4253
return result;
4354
} catch (error) {
4455
const err = error as Error;

0 commit comments

Comments
 (0)