Skip to content

Commit 1cb288e

Browse files
authored
fix(express): fanoutUnspentsV1 type codec
2 parents 753739a + aa72bb2 commit 1cb288e

File tree

2 files changed

+115
-1
lines changed

2 files changed

+115
-1
lines changed

modules/express/src/typedRoutes/api/v1/fanoutUnspents.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,42 @@ export const FanoutUnspentsRequestBody = {
2020
xprv: optional(t.string),
2121
/** Whether to validate addresses (defaults to true) */
2222
validate: optional(t.boolean),
23-
/** Target number of unspents to create (must be at least 2 and less than 300) */
23+
/** Target number of unspents to create (must be at least 2 and less than 300) - REQUIRED */
2424
target: t.number,
2525
/** Minimum number of confirmations needed for an unspent to be included (defaults to 1) */
2626
minConfirms: optional(t.number),
27+
/** Whether to use SegWit change addresses */
28+
segwitChange: optional(t.boolean),
29+
/** Message or note for the transaction */
30+
message: optional(t.string),
31+
/** One-time password for 2FA verification */
32+
otp: optional(t.string),
33+
/** Exact fee amount in satoshis (use either fee, feeRate, or numBlocks, not multiple) */
34+
fee: optional(t.number),
35+
/** Fee rate in satoshis per kilobyte (use either fee, feeRate, or numBlocks, not multiple) */
36+
feeRate: optional(t.number),
37+
/** Whether this is an instant transaction */
38+
instant: optional(t.boolean),
39+
/** Custom sequence ID for the transaction */
40+
sequenceId: optional(t.string),
41+
/** Target number of blocks for fee estimation (use either fee, feeRate, or numBlocks, not multiple) */
42+
numBlocks: optional(t.number),
43+
/** Whether minConfirms also applies to change outputs */
44+
enforceMinConfirmsForChange: optional(t.boolean),
45+
/** Target number of unspents to maintain in the wallet */
46+
targetWalletUnspents: optional(t.number),
47+
/** Minimum value of unspents to use (in base units) */
48+
minValue: optional(t.number),
49+
/** Maximum value of unspents to use (in base units) */
50+
maxValue: optional(t.number),
51+
/** Disable automatic change splitting for unspent management */
52+
noSplitChange: optional(t.boolean),
53+
/** Comment for the transaction */
54+
comment: optional(t.string),
55+
/** Dynamic fee confirmation target (number of blocks) */
56+
dynamicFeeConfirmTarget: optional(t.number),
57+
/** WIF private key for paying fees from a single-key address */
58+
feeSingleKeyWIF: optional(t.string),
2759
};
2860

2961
/**

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

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,13 +710,79 @@ describe('FanoutUnspents codec tests', function () {
710710
assert.strictEqual(decoded.minConfirms, validBody.minConfirms);
711711
});
712712

713+
it('should validate body with fee', function () {
714+
const validBody = {
715+
target: 10,
716+
fee: 10000,
717+
};
718+
719+
const decoded = assertDecode(t.type(FanoutUnspentsRequestBody), validBody);
720+
assert.strictEqual(decoded.fee, validBody.fee);
721+
});
722+
723+
it('should validate body with feeRate', function () {
724+
const validBody = {
725+
target: 10,
726+
feeRate: 20000,
727+
};
728+
729+
const decoded = assertDecode(t.type(FanoutUnspentsRequestBody), validBody);
730+
assert.strictEqual(decoded.feeRate, validBody.feeRate);
731+
});
732+
733+
it('should validate body with message', function () {
734+
const validBody = {
735+
target: 10,
736+
message: 'Test message',
737+
};
738+
739+
const decoded = assertDecode(t.type(FanoutUnspentsRequestBody), validBody);
740+
assert.strictEqual(decoded.message, validBody.message);
741+
});
742+
743+
it('should validate body with otp', function () {
744+
const validBody = {
745+
target: 10,
746+
otp: '123456',
747+
};
748+
749+
const decoded = assertDecode(t.type(FanoutUnspentsRequestBody), validBody);
750+
assert.strictEqual(decoded.otp, validBody.otp);
751+
});
752+
753+
it('should validate body with instant', function () {
754+
const validBody = {
755+
target: 10,
756+
instant: true,
757+
};
758+
759+
const decoded = assertDecode(t.type(FanoutUnspentsRequestBody), validBody);
760+
assert.strictEqual(decoded.instant, validBody.instant);
761+
});
762+
713763
it('should validate body with all fields', function () {
714764
const validBody = {
715765
target: 10,
716766
walletPassphrase: 'mySecurePassphrase',
717767
xprv: 'xprv9s21ZrQH143K3D8TXfvAJgHVfTEeQNW5Ys9wZtnUZkqPzFzSjbEJrWC1vZ4GnXCvR7rQL2UFX3RSuYeU9MrERm1XBvACow7c36vnz5iYyj2',
718768
validate: true,
719769
minConfirms: 2,
770+
segwitChange: true,
771+
message: 'Test transaction',
772+
otp: '123456',
773+
fee: 10000,
774+
feeRate: 20000,
775+
instant: true,
776+
sequenceId: 'seq-12345',
777+
numBlocks: 6,
778+
enforceMinConfirmsForChange: true,
779+
targetWalletUnspents: 50,
780+
minValue: 1000,
781+
maxValue: 100000,
782+
noSplitChange: false,
783+
comment: 'Fanout transaction',
784+
dynamicFeeConfirmTarget: 3,
785+
feeSingleKeyWIF: 'L1aW4aubDFB7yfras2S1mN3bqg9nwySY8nkoLmJebSLD5BWv3ENZ',
720786
};
721787

722788
const decoded = assertDecode(t.type(FanoutUnspentsRequestBody), validBody);
@@ -725,6 +791,22 @@ describe('FanoutUnspents codec tests', function () {
725791
assert.strictEqual(decoded.xprv, validBody.xprv);
726792
assert.strictEqual(decoded.validate, validBody.validate);
727793
assert.strictEqual(decoded.minConfirms, validBody.minConfirms);
794+
assert.strictEqual(decoded.segwitChange, validBody.segwitChange);
795+
assert.strictEqual(decoded.message, validBody.message);
796+
assert.strictEqual(decoded.otp, validBody.otp);
797+
assert.strictEqual(decoded.fee, validBody.fee);
798+
assert.strictEqual(decoded.feeRate, validBody.feeRate);
799+
assert.strictEqual(decoded.instant, validBody.instant);
800+
assert.strictEqual(decoded.sequenceId, validBody.sequenceId);
801+
assert.strictEqual(decoded.numBlocks, validBody.numBlocks);
802+
assert.strictEqual(decoded.enforceMinConfirmsForChange, validBody.enforceMinConfirmsForChange);
803+
assert.strictEqual(decoded.targetWalletUnspents, validBody.targetWalletUnspents);
804+
assert.strictEqual(decoded.minValue, validBody.minValue);
805+
assert.strictEqual(decoded.maxValue, validBody.maxValue);
806+
assert.strictEqual(decoded.noSplitChange, validBody.noSplitChange);
807+
assert.strictEqual(decoded.comment, validBody.comment);
808+
assert.strictEqual(decoded.dynamicFeeConfirmTarget, validBody.dynamicFeeConfirmTarget);
809+
assert.strictEqual(decoded.feeSingleKeyWIF, validBody.feeSingleKeyWIF);
728810
});
729811

730812
it('should reject body with non-number target', function () {

0 commit comments

Comments
 (0)