Open this lesson in your favourite AI. It'll walk you through the why, explain the demo, and quiz you on the try-it list.
When a tx is broadcast but not yet mined, it lives in the mempool. Miners pick the highest fee-per-byte txs first. RBF (Replace-By-Fee) lets a sender bump the fee on a stuck tx. CPFP (Child-Pays-For-Parent) lets a receiver pull a stuck parent into a higher-fee block by spending its output with a high-fee child. Mastering these is essential for any payment app.
Fee bumping with RBF.
Use these three in order. Each builds on the one before.
In one paragraph, explain RBF.
Walk me through CPFP for a stuck unconfirmed deposit.
Design a wallet UX that handles stuck transactions gracefully — when do you prompt for RBF vs CPFP?
// BIP-125 (Replace-By-Fee) signaling: tx is replaceable if any input has sequence < 0xfffffffe.
pub fn is_rbf(tx: &Transaction) -> bool {
tx.inputs.iter().any(|i| i.sequence < 0xfffffffe)
}
// To replace a stuck tx, build a new tx that:
// 1. Spends the same inputs (at least one in common)
// 2. Pays a higher absolute fee AND higher fee rate than original
// 3. Replaces fewer than 100 descendants (default policy)
// 4. The replacement's fee rate >= original's fee rate + min_relay_fee
pub fn build_rbf_replacement(original: &Transaction, new_fee_rate: u64) -> Transaction {
let mut replacement = original.clone();
let total_in: u64 = original.inputs.iter().map(|i| get_utxo_value(&i.previous_outpoint)).sum();
let new_fee = new_fee_rate * tx_vsize(&replacement);
// Adjust change output (last output, typically)
let change_out = replacement.outputs.last_mut().unwrap();
let new_change = total_in - other_outputs_total(&replacement) - new_fee;
require!(new_change >= 546, RbfError::DustChange); // dust limit
change_out.value = new_change;
replacement
}
// CPFP alternative: when sender didn't enable RBF, recipient can spend the unconfirmed
// output in a new high-fee tx. Miners include both because the package fee rate is high.
pub fn build_cpfp_child(parent_tx: &Transaction, vout: u32, recipient_addr: &Address, fee_rate: u64) -> Transaction {
// Spends parent's output and pays high fee; miners pull both via package selection
todo!()
}