Skip to content

Commit 5495f9f

Browse files
authored
fix(express): consolidateUnspentsV2 type codec
2 parents 78ff2b3 + e088390 commit 5495f9f

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed

modules/express/src/typedRoutes/api/v2/consolidateunspents.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ export const ConsolidateUnspentsRequestBody = {
5252
otp: optional(t.string),
5353
/** Target address for the consolidation outputs */
5454
targetAddress: optional(t.string),
55+
/** Transaction format type (e.g., 'legacy', 'psbt', 'psbt-lite') - controls output format */
56+
txFormat: optional(t.union([t.literal('legacy'), t.literal('psbt'), t.literal('psbt-lite')])),
5557
/** If true, enables consolidation of large number of unspents by creating multiple transactions (200 unspents per tx) */
5658
bulk: optional(t.boolean),
5759
} as const;

modules/express/test/unit/typedRoutes/consolidateunspentsV2.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,39 @@ describe('ConsolidateUnspents V2 codec tests', function () {
173173
assert.strictEqual(callArgs.limit, 100);
174174
});
175175

176+
it('should successfully consolidate unspents with txFormat parameter', async function () {
177+
const requestBody = {
178+
walletPassphrase: 'test_passphrase',
179+
txFormat: 'psbt' as const,
180+
};
181+
182+
const mockWallet = {
183+
consolidateUnspents: sinon.stub().resolves(mockConsolidateResponse),
184+
};
185+
186+
const walletsGetStub = sinon.stub().resolves(mockWallet);
187+
const mockCoin = {
188+
wallets: sinon.stub().returns({ get: walletsGetStub }),
189+
};
190+
sinon.stub(BitGo.prototype, 'coin').returns(mockCoin as any);
191+
192+
const result = await agent
193+
.post(`/api/v2/${coin}/wallet/${walletId}/consolidateunspents`)
194+
.set('Authorization', 'Bearer test_access_token_12345')
195+
.set('Content-Type', 'application/json')
196+
.send(requestBody);
197+
198+
assert.strictEqual(result.status, 200);
199+
200+
const decodedResponse = assertDecode(ConsolidateUnspentsResponse, result.body);
201+
const singleResponse = assertSingleTxResponse(decodedResponse);
202+
assert.strictEqual(singleResponse.status, mockConsolidateResponse.status);
203+
204+
// Verify txFormat was passed through to SDK
205+
const callArgs = mockWallet.consolidateUnspents.firstCall.args[0];
206+
assert.strictEqual(callArgs.txFormat, 'psbt');
207+
});
208+
176209
it('should return instant transaction response', async function () {
177210
const requestBody = {
178211
walletPassphrase: 'test_passphrase',
@@ -822,6 +855,7 @@ describe('ConsolidateUnspents V2 codec tests', function () {
822855
comment: 'Test consolidation',
823856
otp: '123456',
824857
targetAddress: '2N8hwP1WmJrFF5QWABn38y63uYLhnJYJYTF',
858+
txFormat: 'psbt' as const,
825859
bulk: true,
826860
};
827861

@@ -834,6 +868,7 @@ describe('ConsolidateUnspents V2 codec tests', function () {
834868
assert.strictEqual(decoded.numUnspentsToMake, validBody.numUnspentsToMake);
835869
assert.strictEqual(decoded.limit, validBody.limit);
836870
assert.strictEqual(decoded.minConfirms, validBody.minConfirms);
871+
assert.strictEqual(decoded.txFormat, 'psbt');
837872
assert.strictEqual(decoded.bulk, validBody.bulk);
838873
});
839874

@@ -896,6 +931,56 @@ describe('ConsolidateUnspents V2 codec tests', function () {
896931
const decodedString = assertDecode(t.type(ConsolidateUnspentsRequestBody), validBodyString);
897932
assert.strictEqual(decodedString.maxValue, '1000000');
898933
});
934+
935+
it('should accept valid txFormat values', function () {
936+
const validBodyLegacy = {
937+
txFormat: 'legacy',
938+
};
939+
const validBodyPsbt = {
940+
txFormat: 'psbt',
941+
};
942+
const validBodyPsbtLite = {
943+
txFormat: 'psbt-lite',
944+
};
945+
946+
const decodedLegacy = assertDecode(t.type(ConsolidateUnspentsRequestBody), validBodyLegacy);
947+
assert.strictEqual(decodedLegacy.txFormat, 'legacy');
948+
949+
const decodedPsbt = assertDecode(t.type(ConsolidateUnspentsRequestBody), validBodyPsbt);
950+
assert.strictEqual(decodedPsbt.txFormat, 'psbt');
951+
952+
const decodedPsbtLite = assertDecode(t.type(ConsolidateUnspentsRequestBody), validBodyPsbtLite);
953+
assert.strictEqual(decodedPsbtLite.txFormat, 'psbt-lite');
954+
});
955+
956+
it('should allow txFormat to be undefined', function () {
957+
const validBody = {
958+
walletPassphrase: 'test',
959+
};
960+
961+
const decoded = assertDecode(t.type(ConsolidateUnspentsRequestBody), validBody);
962+
assert.strictEqual(decoded.txFormat, undefined);
963+
});
964+
965+
it('should reject invalid txFormat values', function () {
966+
const invalidBody = {
967+
txFormat: 'invalid-format',
968+
};
969+
970+
assert.throws(() => {
971+
assertDecode(t.type(ConsolidateUnspentsRequestBody), invalidBody);
972+
});
973+
});
974+
975+
it('should reject non-string txFormat', function () {
976+
const invalidBody = {
977+
txFormat: 123, // number instead of string
978+
};
979+
980+
assert.throws(() => {
981+
assertDecode(t.type(ConsolidateUnspentsRequestBody), invalidBody);
982+
});
983+
});
899984
});
900985

901986
describe('ConsolidateUnspentsResponse V2', function () {

0 commit comments

Comments
 (0)