From 550612d614340a2a2905b6956539f295eae40c16 Mon Sep 17 00:00:00 2001 From: Lokesh Chandra Date: Wed, 26 Nov 2025 14:30:17 +0530 Subject: [PATCH] docs(express): fanoutUnspentsV2 api definition Ticket: WP-6997 --- modules/express/src/clientRoutes.ts | 4 +- modules/express/src/typedRoutes/api/index.ts | 2 +- .../src/typedRoutes/api/v2/fanoutUnspents.ts | 61 ++++++++++--------- 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/modules/express/src/clientRoutes.ts b/modules/express/src/clientRoutes.ts index 6c1c53becd..2efa4eae75 100755 --- a/modules/express/src/clientRoutes.ts +++ b/modules/express/src/clientRoutes.ts @@ -832,7 +832,7 @@ export async function handleV2ConsolidateAccount( * handle wallet fanout unspents * @param req */ -async function handleV2FanOutUnspents(req: ExpressApiRouteRequest<'express.v2.wallet.fanoutunspents', 'post'>) { +async function handleV2FanOutUnspents(req: ExpressApiRouteRequest<'express.wallet.fanoutunspents', 'post'>) { const bitgo = req.bitgo; const coin = bitgo.coin(req.decoded.coin); const wallet = await coin.wallets().get({ id: req.decoded.id }); @@ -1677,7 +1677,7 @@ export function setupAPIRoutes(app: express.Application, config: Config): void { prepareBitGo(config), typedPromiseWrapper(handleV2ConsolidateUnspents), ]); - router.post('express.v2.wallet.fanoutunspents', [prepareBitGo(config), typedPromiseWrapper(handleV2FanOutUnspents)]); + router.post('express.wallet.fanoutunspents', [prepareBitGo(config), typedPromiseWrapper(handleV2FanOutUnspents)]); router.post('express.v2.wallet.sweep', [prepareBitGo(config), typedPromiseWrapper(handleV2Sweep)]); diff --git a/modules/express/src/typedRoutes/api/index.ts b/modules/express/src/typedRoutes/api/index.ts index 86df2f5196..5e7d68962c 100644 --- a/modules/express/src/typedRoutes/api/index.ts +++ b/modules/express/src/typedRoutes/api/index.ts @@ -171,7 +171,7 @@ export const ExpressWalletFanoutUnspentsApiSpec = apiSpec({ 'express.v1.wallet.fanoutunspents': { put: PutFanoutUnspents, }, - 'express.v2.wallet.fanoutunspents': { + 'express.wallet.fanoutunspents': { post: PostFanoutUnspents, }, }); diff --git a/modules/express/src/typedRoutes/api/v2/fanoutUnspents.ts b/modules/express/src/typedRoutes/api/v2/fanoutUnspents.ts index 6e7517f1ea..b53b52e236 100644 --- a/modules/express/src/typedRoutes/api/v2/fanoutUnspents.ts +++ b/modules/express/src/typedRoutes/api/v2/fanoutUnspents.ts @@ -20,41 +20,51 @@ export const FanoutUnspentsRequestParams = { * creates a larger number of equally-sized outputs for improved transaction parallelization. */ export const FanoutUnspentsRequestBody = { - /** The wallet passphrase to decrypt the user key */ + /** Passphrase to decrypt the user key on the wallet */ walletPassphrase: optional(t.string), - /** The extended private key (alternative to walletPassphrase) */ + /** Private key in string form, if walletPassphrase is not available */ xprv: optional(t.string), - /** The number of new unspents to create */ + /** Number of new unspents to make */ numUnspentsToMake: optional(t.number), - /** Minimum value of unspents to use (in base units) */ + /** Minimum value of unspents to use in base units (e.g. satoshis). For doge, only string is allowed. */ minValue: optional(t.union([t.number, t.string])), - /** Maximum value of unspents to use (in base units) */ + /** Maximum value of unspents to use in base units (e.g. satoshis). For doge, only string is allowed. */ maxValue: optional(t.union([t.number, t.string])), - /** Minimum block height of unspents to use */ + /** Minimum height of unspents on the block chain to use*/ minHeight: optional(t.number), - /** Minimum number of confirmations needed for an unspent to be included (defaults to 1) */ + /** Minimum confirmation threshold for external inputs */ minConfirms: optional(t.number), - /** If true, minConfirms also applies to change outputs */ + /** Flag for enforcing minConfirms for change inputs */ enforceMinConfirmsForChange: optional(t.boolean), - /** Maximum number of inputs to use in the transaction */ + /** Maximum number of unspents to use in the transaction. Mutually exclusive with unspents. */ maxNumInputsToUse: optional(t.number), - /** Array of specific unspent IDs to use */ + /** Unspents to fan out in the transaction. Mutually exclusive with maxNumInputsToUse. */ unspents: optional(t.array(t.string)), - /** The desired fee rate for the transaction in satoshis/kB */ + /** + * Custom fee rate (in base units) per kilobyte (or virtual kilobyte). For example, satoshis per kvByte + * + * If the feeRate is less than the minimum required network fee, then the minimum fee applies. For example, 1000 sat/kvByte, a flat 1000 microAlgos, or a flat 10 drops of xrp. For XRP, the actual fee is usually 4.5 times the open ledger fee. + * + * Note: The feeRate overrides the maxFeeRate and minFeeRate. + */ feeRate: optional(t.number), - /** The maximum limit for a fee rate in satoshis/kB */ + /** + * (BTC only) The maximum fee rate (in base units) per kilobyte (or virtual kilobyte). For example, satoshis per kvByte. The maxFeeRate limits the fee rate generated by both feeMultiplier and numBlocks. + * + * Note: The feeRate overrides the maxFeeRate. + */ maxFeeRate: optional(t.number), - /** The maximum proportion of value you're willing to lose to fees (as a decimal, e.g., 0.1 for 10%) */ + /** Maximum relative portion that can be spent towards fees */ maxFeePercentage: optional(t.number), - /** Estimate fees to aim for first confirmation within this number of blocks */ + /** Block target for fee estimation */ feeTxConfirmTarget: optional(t.number), /** Comment to attach to the transaction */ comment: optional(t.string), /** One-time password for 2FA */ otp: optional(t.string), - /** Target address for the fanout outputs */ + /** Address to use for generated outputs. Must be wallet address. */ targetAddress: optional(t.string), - /** Transaction format type (e.g., 'legacy', 'psbt', 'psbt-lite') - controls output format */ + /** [UTXO only] Format of the returned transaction hex serialization. legacy for serialized transaction in custom bitcoinjs-lib format. psbt for BIP174 serialized transaction */ txFormat: optional(t.union([t.literal('legacy'), t.literal('psbt'), t.literal('psbt-lite')])), /** If true, enables fanout of large number of unspents by creating multiple transactions (200 unspents per tx) */ bulk: optional(t.boolean), @@ -100,22 +110,13 @@ export const FanoutUnspentsResponse = t.union([ ]); /** - * Fan out unspents in a wallet (v2) + * Fan out unspents on a wallet * - * This endpoint fans out unspents in a wallet by creating a transaction that spends from - * one or more inputs to create multiple equal-sized outputs. This is useful for increasing - * the number of UTXOs in a wallet, which can improve transaction parallelization and allow - * for concurrent spending operations. + * Creates transactions that distribute existing unspents into more outputs, enabling parallel + * transaction creation. Supports bulk mode to handle large fanouts across multiple transactions. * - * The v2 API differs from v1 by: - * - Requiring a coin parameter in the path - * - Supporting the full set of SDK parameters for advanced UTXO management - * - Using numUnspentsToMake instead of target (though both refer to output count) - * - Supporting bulk fanout mode that creates multiple transactions - * - Supporting additional parameters like maxNumInputsToUse, unspents array, bulk, fee controls - * - * @operationId express.v2.wallet.fanoutunspents - * @tag express + * @operationId express.wallet.fanoutunspents + * @tag Express */ export const PostFanoutUnspents = httpRoute({ path: '/api/v2/{coin}/wallet/{id}/fanoutunspents',