Skip to content

Commit dcd3ded

Browse files
committed
docs(express): sendMany v2
Ticket: WCI-57
1 parent 19164bc commit dcd3ded

File tree

1 file changed

+93
-53
lines changed

1 file changed

+93
-53
lines changed

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

Lines changed: 93 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import { BitgoExpressError } from '../../schemas/error';
88
* Request parameters for sending to multiple recipients (v2)
99
*/
1010
export const SendManyRequestParams = {
11-
/** The coin identifier (e.g., 'btc', 'tbtc', 'eth', 'teth') */
11+
/** A cryptocurrency or token ticker symbol. */
1212
coin: t.string,
13-
/** The ID of the wallet */
13+
/** The wallet ID. */
1414
id: t.string,
1515
} as const;
1616

@@ -112,82 +112,122 @@ export const TokenEnablement = t.intersection([
112112
* for building, signing, and sending transactions to multiple recipients.
113113
*/
114114
export const SendManyRequestBody = {
115-
/** Array of recipients with addresses and amounts */
115+
/** A list of recipient addresses and amounts. Must be present but empty for CPFP transactions. */
116116
recipients: optional(t.array(Recipient)),
117117

118-
/** The wallet passphrase to decrypt the user key */
118+
/** Passphrase to decrypt the user key on the wallet. Required if External Signer is not used to sign the transactions. */
119119
walletPassphrase: optional(t.string),
120120

121121
/** The extended private key (alternative to walletPassphrase) */
122122
xprv: optional(t.string),
123123

124-
/** The private key (prv) in string form */
124+
/** The un-encrypted user private key in string form. If the key is a JSON object it must be stringified. Required if `walletPassphrase` is not available or encrypted private key is not stored by BitGo. */
125125
prv: optional(t.string),
126126

127-
/** Estimate fees to aim for first confirmation within this number of blocks */
127+
/**
128+
* (BTC only) The number of blocks required to confirm a transaction. You can use `numBlocks` to estimate the fee
129+
* rate by targeting confirmation within a given number of blocks. If both `feeRate` and `numBlocks` are absent,
130+
* the transaction defaults to 2 blocks for confirmation.
131+
*
132+
* Note: The `maxFeeRate` limits the fee rate generated by `numBlocks`.
133+
*/
128134
numBlocks: optional(t.number),
129135

130-
/** The desired fee rate for the transaction in base units per kilobyte (e.g., satoshis/kB) */
136+
/**
137+
* Custom fee rate (in base units) per kilobyte (or virtual kilobyte). For example, satoshis per kvByte.
138+
*
139+
* If the `feeRate` is less than the minimum required network fee, then the minimum fee applies. For example,
140+
* 1000 sat/kvByte, a flat 1000 microAlgos, or a flat 10 drops of xrp. For XRP, the actual fee is usually
141+
* 4.5 times the open ledger fee.
142+
*
143+
* Note: The `feeRate` overrides the `maxFeeRate` and `minFeeRate`.
144+
*/
131145
feeRate: optional(t.number),
132146

133-
/** Fee multiplier (multiplies the estimated fee by this factor) */
147+
/**
148+
* (UTXO only) Custom multiplier to the `feeRate`. The resulting fee rate is limited by the `maxFeeRate`.
149+
* For replace-by-fee (RBF) transactions (that include `rbfTxIds`), the `feeMultiplier` must be greater than 1,
150+
* since it's an absolute fee multiplier to the transaction being replaced.
151+
*
152+
* Note: The `maxFeeRate` limits the fee rate generated by `feeMultiplier`.
153+
*/
134154
feeMultiplier: optional(t.number),
135155

136-
/** The maximum limit for a fee rate in base units per kilobyte */
156+
/**
157+
* (BTC only) The maximum fee rate (in base units) per kilobyte (or virtual kilobyte). For example, satoshis per kvByte.
158+
* The `maxFeeRate` limits the fee rate generated by both `feeMultiplier` and `numBlocks`.
159+
*
160+
* Note: The `feeRate` overrides the `maxFeeRate`.
161+
*/
137162
maxFeeRate: optional(t.number),
138163

139-
/** Minimum number of confirmations needed for an unspent to be included (defaults to 1) */
164+
/** The unspent selection for the transaction will only consider unspents with at least this many confirmations to be used as inputs. Does not apply to change outputs unless used in combination with `enforceMinConfirmsForChange`. */
140165
minConfirms: optional(t.number),
141166

142-
/** If true, minConfirms also applies to change outputs */
167+
/** Defaults to false. When set to true, will enforce minConfirms for change outputs. */
143168
enforceMinConfirmsForChange: optional(t.boolean),
144169

145-
/** Target number of unspents to maintain in the wallet */
170+
/**
171+
* Defaults to 1000. Specifies the minimum count of good-sized unspents to maintain in the wallet.
172+
* Change splitting ceases when the wallet has `targetWalletUnspents` good-sized unspents.
173+
*
174+
* Note: Wallets that continuously send a high count of transactions will automatically split large change amounts
175+
* into multiple good-sized change outputs while they have fewer than `targetWalletUnspents` good-sized unspents
176+
* in their unspent pool. Breaking up large unspents helps to reduce the amount of unconfirmed funds in flight in
177+
* future transactions, and helps to avoid long chains of unconfirmed transactions. This is especially useful for
178+
* newly funded wallets or recently refilled send-only wallets.
179+
*/
146180
targetWalletUnspents: optional(t.number),
147181

148182
/** Message to attach to the transaction */
149183
message: optional(t.string),
150184

151-
/** Minimum value of unspents to use (in base units) */
185+
/** Ignore unspents smaller than this amount of base units (e.g. satoshis). For doge, only string is allowed. */
152186
minValue: optional(t.union([t.number, t.string])),
153187

154-
/** Maximum value of unspents to use (in base units) */
188+
/** Ignore unspents larger than this amount of base units (e.g. satoshis). For doge, only string is allowed. */
155189
maxValue: optional(t.union([t.number, t.string])),
156190

157-
/** Custom sequence ID for the transaction */
191+
/**
192+
* A `sequenceId` is a unique and arbitrary wallet identifier applied to transfers and transactions at creation.
193+
* It is optional but highly recommended. With a `sequenceId` you can easily reference transfers and transactions—
194+
* for example, to safely retry sending. Because the system only confirms one send request per `sequenceId`
195+
* (and fails all subsequent attempts), you can retry sending without the risk of double spending.
196+
* The `sequenceId` is only visible to users on the wallet and is not shared publicly.
197+
*/
158198
sequenceId: optional(t.string),
159199

160-
/** Absolute max ledger the transaction should be accepted in (for XRP) */
200+
/** (XRP only) Absolute max ledger the transaction should be accepted in, whereafter it will be rejected. */
161201
lastLedgerSequence: optional(t.number),
162202

163-
/** Relative ledger height (in relation to the current ledger) that the transaction should be accepted in */
203+
/** (XRP only) Relative ledger height (in relation to the current ledger) that the transaction should be accepted in, whereafter it will be rejected. */
164204
ledgerSequenceDelta: optional(t.number),
165205

166-
/** Custom gas price to be used for sending the transaction (for account-based coins) */
206+
/** Custom gas price to be used for sending the transaction. Only for ETH and ERC20 tokens. */
167207
gasPrice: optional(t.number),
168208

169-
/** Set to true to disable automatic change splitting for purposes of unspent management */
209+
/** Defaults to false. Set `true` to disable automatic change splitting. Also see: `targetWalletUnspents` */
170210
noSplitChange: optional(t.boolean),
171211

172-
/** Array of specific unspent IDs to use in the transaction */
212+
/** Used to explicitly specify the unspents to be used in the input set in the transaction. Each unspent should be in the form `prevTxId:nOutput`. */
173213
unspents: optional(t.array(t.string)),
174214

175-
/** Comment to attach to the transaction */
215+
/** Optional metadata (only persisted in BitGo) to be applied to the transaction. Use this to add transaction-specific information such as the transaction's purpose or another identifier that you want to reference later. The value is shown in the UI in the transfer listing page. (length ≤ 256) */
176216
comment: optional(t.string),
177217

178-
/** One-time password for 2FA */
218+
/** Two factor auth code to enable sending the transaction. Not necessary if using a long lived access token within the spending limit. */
179219
otp: optional(t.string),
180220

181-
/** Specifies the destination of the change output */
221+
/** Specifies a custom destination address for the transaction's change output(s) (length ≤ 500) */
182222
changeAddress: optional(t.string),
183223

184224
/** If true, allows using an external change address */
185225
allowExternalChangeAddress: optional(t.boolean),
186226

187-
/** Send this transaction using coin-specific instant sending method (if available) */
227+
/** (DASH only) Specifies whether or not to use Dash's "InstantSend" feature when sending a transaction. */
188228
instant: optional(t.boolean),
189229

190-
/** Memo to use in transaction (supported by Stellar, XRP, etc.) */
230+
/** Extra transaction information for CSPR, EOS, HBAR, RUNE, STX, TON, XLM, and XRP. Required for XLM transactions. Note: For XRP this is the destination tag (DT). For CSPR this is the transfer ID. */
191231
memo: optional(MemoParams),
192232

193233
/** Transfer ID for tracking purposes */
@@ -196,28 +236,40 @@ export const SendManyRequestBody = {
196236
/** EIP-1559 fee parameters for Ethereum transactions */
197237
eip1559: optional(EIP1559Params),
198238

199-
/** Gas limit for the transaction (for account-based coins) */
239+
/** Custom gas limit to be used for sending the transaction. Only for ETH and ERC20 tokens. */
200240
gasLimit: optional(t.number),
201241

202-
/** Token name for token transfers */
242+
/** Token name, defined in the BitGoJS Statics package. */
203243
tokenName: optional(t.string),
204244

205-
/** Type of transaction (e.g., 'trustline' for Stellar) */
245+
/**
246+
* Required for transactions from MPC wallets. "acceleration" speeds up transactions with a certain nonce by
247+
* adjusting the gas setting. "accountSet" is for XRP AccountSet transactions. "consolidate" combines multiple
248+
* UTXO inputs into fewer outputs. "enabletoken" is for SOL. "fanout" splits UTXO inputs into many smaller outputs
249+
* (UTXO coins only). "stakingLock" and "stakingUnlock" are for Stacks delegations. "transfer" is for native-asset
250+
* transfers. "trustline" is for Stellar trustline transactions.
251+
* Possible types include: [acceleration, accountSet, consolidate, enabletoken, fanout, stakingLock, stakingUnlock,
252+
* transfer, transfertoken, trustline].
253+
*
254+
* For AVAX, possible types include: `addValidator`, `export`, and `import`.
255+
* For XRP, possible types include: `payment` and `accountSet`. The default is `payment`.
256+
* For STX, type is required.
257+
*/
206258
type: optional(t.string),
207259

208260
/** Custodian transaction ID (for institutional custody integrations) */
209261
custodianTransactionId: optional(t.string),
210262

211-
/** If true, enables hop transactions for exchanges */
263+
/** (ETH, AVAXC and POLYGON) Set to true if funds to destination need to come from single sig address. */
212264
hop: optional(t.boolean),
213265

214-
/** Address type for the transaction (e.g., 'p2sh', 'p2wsh') */
266+
/** @deprecated Use `changeAddressType` instead. The type of address to create for change. One of `p2sh`, `p2shP2wsh`, `p2wsh`, or `p2tr`. */
215267
addressType: optional(t.string),
216268

217-
/** Change address type (e.g., 'p2sh', 'p2wsh') */
269+
/** The address type for the change address. One of `p2sh`, `p2shP2wsh`, `p2wsh`, `p2tr` or `p2trMusig2`. */
218270
changeAddressType: optional(t.string),
219271

220-
/** Transaction format (legacy or psbt) */
272+
/** [UTXO only] Format of the returned transaction hex serialization. `legacy` for serialized transaction in custom bitcoinjs-lib format. `psbt` for BIP174 serialized transaction. Defaults to `legacy`. */
221273
txFormat: optional(t.union([t.literal('legacy'), t.literal('psbt'), t.literal('psbt-lite')])),
222274

223275
/** If set to false, sweep all funds including required minimums (e.g., DOT requires 1 DOT minimum) */
@@ -229,7 +281,7 @@ export const SendManyRequestBody = {
229281
/** NFT ID (for NFT transfers) */
230282
nftId: optional(t.string),
231283

232-
/** Transaction nonce (for account-based coins) */
284+
/** (DOT only) A nonce ID is a number used to protect private communications by preventing replay attacks. This is an advanced option where users can manually input a new nonce value in order to correct or fill in a missing nonce ID value. */
233285
nonce: optional(t.string),
234286

235287
/** If true, only preview the transaction without sending */
@@ -238,7 +290,7 @@ export const SendManyRequestBody = {
238290
/** Receive address (for specific coins like ADA) */
239291
receiveAddress: optional(t.string),
240292

241-
/** Messages to be signed with specific addresses */
293+
/** [UTXO only] An array of messages that you sign with the wallet keys using the BIP322 format. If passed, the `recipients` array must be empty. */
242294
messages: optional(
243295
t.array(
244296
t.type({
@@ -260,13 +312,13 @@ export const SendManyRequestBody = {
260312
/** Non-participation flag (for governance/staking protocols like Algorand) */
261313
nonParticipation: optional(t.boolean),
262314

263-
/** Valid from block height */
315+
/** Optional block this transaction is valid from. */
264316
validFromBlock: optional(t.number),
265317

266-
/** Valid to block height */
318+
/** Optional block this transaction is valid until. */
267319
validToBlock: optional(t.number),
268320

269-
/** Reservation parameters for unspent management */
321+
/** Optional parameter for UTXO coins to automatically reserve the unspents that are used in the build. Useful for Cold wallets. If using, must set `expireTime`. */
270322
reservation: optional(
271323
t.partial({
272324
expireTime: t.string,
@@ -729,26 +781,14 @@ export const SendManyResponse = t.intersection([
729781
]);
730782

731783
/**
732-
* Send to multiple recipients (v2)
784+
* Send coins or tokens to one or more recipients. You can use this endpoint to schedule outgoing transactions in bulk, lowering your aggregate amount of blockchain fees.
733785
*
734-
* This endpoint sends funds to multiple recipients by:
735-
* 1. Building a transaction with the specified recipients and parameters
736-
* 2. Signing the transaction with the user's key (decrypted with walletPassphrase or xprv)
737-
* 3. Requesting a signature from BitGo's key
738-
* 4. Sending the fully-signed transaction to the blockchain network
786+
* Works with both multisignature and MPC wallets. Also supports external-signer mode.
739787
*
740-
* The v2 API supports:
741-
* - Multiple recipients in a single transaction
742-
* - Full control over transaction fees (feeRate, maxFeeRate, numBlocks)
743-
* - UTXO selection (minValue, maxValue, unspents array)
744-
* - Instant transactions (if supported by the coin)
745-
* - TSS wallets with txRequest flow
746-
* - Account-based and UTXO-based coins
747-
* - Token transfers
748-
* - Advanced features like memo fields, hop transactions, EIP-1559 fees
788+
* Works with most BitGo-supported assets, but currently unavailable for: ALGO, ARBETH, AVAXC, CELO, CELO:CUSD, CSPR, DOT, EOS, HTETH:BGERCH, NEAR, OPETH, STX, TON, TRX, TRX:USDC, XLM, XRP, XTZ
749789
*
750790
* @operationId express.v2.wallet.sendmany
751-
* @tag express
791+
* @tag Express
752792
*/
753793
export const PostSendMany = httpRoute({
754794
path: '/api/v2/{coin}/wallet/{id}/sendmany',

0 commit comments

Comments
 (0)