Skip to content

Commit 454fa81

Browse files
authored
Merge pull request #7570 from BitGo/WP-6950-consolidate-unspents
docs(express): consolidateUnspentsV1 API definition
2 parents b6a51df + 3028158 commit 454fa81

File tree

1 file changed

+52
-28
lines changed

1 file changed

+52
-28
lines changed

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

Lines changed: 52 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,23 @@ export const ConsolidateUnspentsRequestParams = {
1414
* Request body for consolidating unspents in a wallet
1515
*/
1616
export const ConsolidateUnspentsRequestBody = {
17-
/** The wallet passphrase to decrypt the user key */
17+
/** Wallet passphrase to decrypt the user key for signing (required unless xprv is provided) */
1818
walletPassphrase: optional(t.string),
19-
/** The extended private key (alternative to walletPassphrase) */
19+
/** Extended private key as alternative to walletPassphrase (provide only one) */
2020
xprv: optional(t.string),
21-
/** Whether to validate addresses (defaults to true) */
21+
/** Whether to validate addresses before creating transactions (defaults to true) */
2222
validate: optional(t.boolean),
23-
/** Target number of unspents to maintain (defaults to 1) */
23+
/** Target number of unspents to maintain in the wallet (defaults to 1, must be positive integer) */
2424
target: optional(t.number),
25-
/** Minimum size of unspents to consolidate */
25+
/** Minimum size of unspents to consolidate in satoshis (defaults to 0 or auto-calculated from feeRate) */
2626
minSize: optional(t.union([t.number, t.string])),
27-
/** Maximum size of unspents to consolidate */
27+
/** Maximum size of unspents to consolidate in satoshis */
2828
maxSize: optional(t.union([t.number, t.string])),
29-
/** Maximum number of inputs per consolidation transaction (defaults to 200, must be ≥ 2) */
29+
/** Maximum inputs per consolidation transaction (defaults to 200, must be 2-200) */
3030
maxInputCountPerConsolidation: optional(t.number),
31-
/** Maximum number of consolidation iterations (defaults to -1) */
31+
/** Maximum consolidation iterations (defaults to -1 for unlimited, or positive integer) */
3232
maxIterationCount: optional(t.number),
33-
/** Minimum number of confirmations needed for an unspent to be included (defaults to 1) */
33+
/** Minimum confirmations needed for an unspent to be included (defaults to 1) */
3434
minConfirms: optional(t.number),
3535
/** Custom fee rate in satoshis per kilobyte */
3636
feeRate: optional(t.number),
@@ -48,9 +48,9 @@ export const ConsolidateUnspentsRequestBody = {
4848
enforceMinConfirmsForChange: optional(t.boolean),
4949
/** Target number of unspents for the wallet */
5050
targetWalletUnspents: optional(t.number),
51-
/** Minimum value of unspents to include (in satoshis) - accepts number or string */
51+
/** Minimum value of unspents to include in satoshis */
5252
minValue: optional(t.union([t.number, t.string])),
53-
/** Maximum value of unspents to include (in satoshis) - accepts number or string */
53+
/** Maximum value of unspents to include in satoshis */
5454
maxValue: optional(t.union([t.number, t.string])),
5555
/** Comment to attach to the transaction */
5656
comment: optional(t.string),
@@ -59,44 +59,68 @@ export const ConsolidateUnspentsRequestBody = {
5959
/**
6060
* Response for consolidating unspents in a wallet
6161
*
62-
* Returns an array of transaction objects when consolidation occurs,
63-
* or an empty object {} when no consolidation is needed (target already reached).
64-
* The empty object is how Express serializes an undefined return from the V1 SDK.
62+
* Two possible response types:
63+
* 1. Array of transaction objects - when consolidation occurs (one per iteration)
64+
* 2. Empty object {} - when target is already reached (no consolidation needed)
6565
*/
6666
export const ConsolidateUnspentsResponse = t.union([
6767
t.array(
6868
t.type({
69-
/** The status of the transaction ('accepted', 'pendingApproval', or 'otp') */
69+
/** Transaction status: 'accepted' (broadcasted), 'pendingApproval' (needs approval), or 'otp' (needs 2FA) */
7070
status: t.union([t.literal('accepted'), t.literal('pendingApproval'), t.literal('otp')]),
71-
/** The transaction hex */
71+
/** Signed transaction in hex format */
7272
tx: t.string,
73-
/** The transaction hash/ID */
73+
/** Transaction hash/ID */
7474
hash: t.string,
75-
/** Whether the transaction is instant */
75+
/** Whether this is an instant transaction (BitGo Instant) */
7676
instant: t.boolean,
77-
/** The instant ID (if applicable) */
77+
/** Instant transaction ID (only present for instant transactions) */
7878
instantId: optional(t.string),
79-
/** The fee amount in satoshis */
79+
/** Total fee paid in satoshis */
8080
fee: t.number,
81-
/** The fee rate in satoshis per kilobyte */
81+
/** Fee rate in satoshis per kilobyte */
8282
feeRate: t.number,
83-
/** Travel rule information */
83+
/** Travel rule compliance information */
8484
travelInfos: t.unknown,
85-
/** BitGo fee information (if applicable) */
85+
/** BitGo service fee information (if applicable) */
8686
bitgoFee: optional(t.unknown),
87-
/** Travel rule result (if applicable) */
87+
/** Travel rule submission result (if applicable) */
8888
travelResult: optional(t.unknown),
8989
})
9090
),
91-
t.type({}), // Empty object when SDK returns undefined
91+
t.type({}), // Empty object when target already reached (no consolidation needed)
9292
]);
9393

9494
/**
9595
* Consolidate unspents in a wallet
9696
*
97-
* This endpoint consolidates unspents in a wallet by creating a transaction that spends from
98-
* multiple inputs to a single output. This is useful for reducing the number of UTXOs in a wallet,
99-
* which can improve performance and reduce transaction fees.
97+
* Consolidates unspent transaction outputs (UTXOs) by creating transactions that combine
98+
* multiple small inputs into fewer larger outputs. This reduces the UTXO count to improve
99+
* wallet performance and lower future transaction fees.
100+
*
101+
* ## How It Works
102+
* The consolidation process is iterative:
103+
* 1. Fetches unspents matching the filter criteria (minSize, maxSize, minConfirms)
104+
* 2. If unspents ≤ target: consolidation complete (returns empty object)
105+
* 3. Creates a new wallet address to receive consolidated funds
106+
* 4. Builds a transaction with up to maxInputCountPerConsolidation inputs
107+
* 5. Sends all inputs to the new address (minus fees)
108+
* 6. Repeats until target is reached or maxIterationCount is hit
109+
*
110+
* ## Parameters
111+
* - **target**: Desired final unspent count (default: 1)
112+
* - **maxInputCountPerConsolidation**: Max inputs per transaction (default: 200)
113+
* - **maxIterationCount**: Max iterations (default: -1 for unlimited)
114+
* - **minSize**: Auto-calculated from feeRate to avoid consolidating unspents smaller than their fee cost
115+
*
116+
* ## Response
117+
* - **Array of transactions**: When consolidation occurs, returns one transaction per iteration
118+
* - **Empty object {}**: When target is already reached (no consolidation needed)
119+
*
120+
* ## Performance Notes
121+
* - Large consolidations may take time due to multiple iterations
122+
* - Each iteration waits 1 second before the next to allow transaction confirmation
123+
* - Use maxIterationCount to limit execution time for very large wallets
100124
*
101125
* @operationId express.v1.wallet.consolidateunspents
102126
* @tag express

0 commit comments

Comments
 (0)