Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions modules/express/src/clientRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -854,7 +854,7 @@ async function handleV2Sweep(req: ExpressApiRouteRequest<'express.v2.wallet.swee
* handle CPFP accelerate transaction creation
* @param req
*/
async function handleV2AccelerateTransaction(req: ExpressApiRouteRequest<'express.v2.wallet.accelerateTx', 'post'>) {
async function handleV2AccelerateTransaction(req: ExpressApiRouteRequest<'express.wallet.acceleratetx', 'post'>) {
const bitgo = req.bitgo;
const coin = bitgo.coin(req.decoded.coin);
const wallet = await coin.wallets().get({ id: req.decoded.id });
Expand Down Expand Up @@ -1692,7 +1692,7 @@ export function setupAPIRoutes(app: express.Application, config: Config): void {
router.post('express.v2.wallet.sweep', [prepareBitGo(config), typedPromiseWrapper(handleV2Sweep)]);

// CPFP
router.post('express.v2.wallet.accelerateTx', [
router.post('express.wallet.acceleratetx', [
prepareBitGo(config),
typedPromiseWrapper(handleV2AccelerateTransaction),
]);
Expand Down
2 changes: 1 addition & 1 deletion modules/express/src/typedRoutes/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ export const ExpressV2WalletSweepApiSpec = apiSpec({
});

export const ExpressV2WalletAccelerateTxApiSpec = apiSpec({
'express.v2.wallet.accelerateTx': {
'express.wallet.acceleratetx': {
post: PostWalletAccelerateTx,
},
});
Expand Down
60 changes: 25 additions & 35 deletions modules/express/src/typedRoutes/api/v2/walletAccelerateTx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,25 +53,36 @@ export const MemoParams = t.type({
* 2. Additional fields from PrebuildAndSignTransactionOptions (transaction building and signing fields)
*/
export const AccelerateTxRequestBody = {
/** Transaction IDs to accelerate using CPFP (Child-Pays-For-Parent). Must be array of length 1. */
/**
* Txids of the transactions to bump
*
* **Notes:** Each target unconfirmed transaction must be sending funds to the calling wallet. Accepts only a single txid at this stage.
*/
cpfpTxIds: optional(t.array(t.string)),

/** Transaction IDs to accelerate using RBF (Replace-By-Fee). Must be array of length 1. */
/** The list of transactions to accelerate using Replace-By-Fee (RBF) for UTXO coins (currently accelerating only one tx is supported). */
rbfTxIds: optional(t.array(t.string)),

/** Fee rate for the CPFP transaction (in satoshis/kB). Required for CPFP unless noCpfpFeeRate=true. */
/** Desired effective feerate of the bumped transactions and the CPFP transaction in satoshi per kilobyte */
cpfpFeeRate: optional(t.number),

/** If true, allows skipping cpfpFeeRate requirement for CPFP */
noCpfpFeeRate: optional(t.boolean),

/** Maximum fee willing to pay (in satoshis). Required for CPFP unless noMaxFee=true. */
/**
* Maximum allowed fee for the CPFP transaction in satoshi
*
* **Notes:** A CPFP transaction accelerates the target transactions and all of the unconfirmed transactions the target transactions depends on. As it can be difficult to discern the complete transaction ancestry at times, we recommend limiting the total fee for each CPFP attempt as a safety net to prevent CPFP transactions that exceed your cost expectations.
*/
maxFee: optional(t.number),

/** If true, allows skipping maxFee requirement for CPFP */
noMaxFee: optional(t.boolean),

/** Fee multiplier for RBF (must be > 1). Required when using rbfTxIds. */
/** (UTXO only) Custom multiplier to the feeRate. The resulting fee rate is limited by the maxFeeRate. For replace-by-fee (RBF) transactions (that include rbfTxIds), the feeMultiplier must be greater than 1, since it's an absolute fee multiplier to the transaction being replaced.
*
* **Note:** The maxFeeRate limits the fee rate generated by feeMultiplier.
*/
feeMultiplier: optional(t.number),

/** Recipients array (will be set to empty array by SDK for acceleration) */
Expand All @@ -84,7 +95,7 @@ export const AccelerateTxRequestBody = {
)
),

/** The wallet passphrase to decrypt the user key */
/** (Hot wallet only) Passphrase to decrypt the user key on the wallet to sign the transaction. */
walletPassphrase: optional(t.string),

/** The private key (prv) in string form */
Expand Down Expand Up @@ -380,39 +391,18 @@ export const AccelerateTxRequestBody = {
export const AccelerateTxResponse = t.unknown;

/**
* Accelerate Transaction Confirmation (CPFP/RBF)
* Send a new transaction to accelerate the targeted unconfirmed transaction either by using Child-Pays-For-Parent (CPFP) or Replace-By-Fee (RBF).
*
* This endpoint accelerates a stuck or slow transaction using either:
* - **CPFP (Child-Pays-For-Parent)**: Creates a new transaction that spends the output
* of the stuck transaction with a higher fee, incentivizing miners to confirm both.
* - **RBF (Replace-By-Fee)**: Replaces the original transaction with a new one that
* has a higher fee (only works if original transaction was marked as RBF-enabled).
* **Background:**
* 1. In Bitcoin, a transaction can only be included in a block when all its inputs are confirmed. This requirement can be used to increase the effective fee rate of a stuck low-fee transaction. One of the stuck transaction's outputs is spent in a child transaction with a much higher fee. Miners include the transactions with the highest fees first to maximize their revenue, but the high-fee child transaction can only be included once the parent transaction is confirmed. The miners are therefore incentivized to include both the parent and the child transaction together in a block. A Child-Pays-For-Parent transaction can be created by a recipient of the transaction or by the sender if the target transaction has a change output.
* 2. In Bitcoin, a transaction can be replaced by a new transaction with a higher fee as long as the new transaction spends few or all of the same inputs used by the original transaction that's being replaced. Unlike CPFP, only the sender of the transaction can create a Replace-By-Fee transaction, and only either of the transactions can be confirmed. More often than not, the replacement transaction with the higher fee will be accepted by the miners.
*
* Supported coins: Primarily UTXO-based coins like Bitcoin (btc, tbtc), Litecoin (ltc), etc.
* **Notes:**
* 1. As other coins do not have a blockspace market, this route is only available for Bitcoin at this time.
* 2. Using CPFP, if a target transaction depends on other unconfirmed transactions, this route also adds sufficient fees to elevate the entire transaction ancestry's effective fee rate to the cpfpFeeRate.
*
* **CPFP Requirements:**
* - cpfpTxIds: Array with exactly one transaction ID
* - cpfpFeeRate: Fee rate in satoshis/kB (required unless noCpfpFeeRate=true)
* - maxFee: Maximum fee in satoshis (required unless noMaxFee=true)
* - walletPassphrase, xprv, or prv: For signing the CPFP transaction
*
* **RBF Requirements:**
* - rbfTxIds: Array with exactly one transaction ID
* - feeMultiplier: Multiplier for the fee (must be > 1, e.g., 1.5 for 50% increase)
* - walletPassphrase, xprv, or prv: For signing the replacement transaction
*
* **Important:**
* - Must specify EITHER cpfpTxIds OR rbfTxIds (not both)
* - Original transaction must be unconfirmed
* - For RBF, original transaction must have been created with RBF enabled
*
* **Behavior:**
* 1. Validates acceleration parameters (CPFP or RBF)
* 2. Builds and signs the acceleration transaction
* 3. Submits the transaction to the blockchain
* 4. Returns transaction details
*
* @operationId express.v2.wallet.accelerateTx
* @operationId express.wallet.acceleratetx
* @tag Express
*/
export const PostWalletAccelerateTx = httpRoute({
Expand Down