Skip to Content
Welcome to the DuckyDux Docs! 🦆
GuidesUsing Transaction Bundles

Using Transaction Bundles

Transaction bundles are DuckyDux’s core security feature that allows you to group multiple transactions together and execute them atomically through private channels to block builders.

DuckyDux Advantage: All bundles bypass the public mempool and go directly to block builders, providing complete MEV protection for your transactions.

Why Use Bundles?

Atomicity

Bundles ensure “all or nothing” execution, critical for multi-step DeFi operations where partial execution could result in loss of funds.

MEV Protection

Security Critical: Never send approval transactions separately! This exposes your trading intent to MEV bots who can front-run your swap.

DuckyDux bundles provide three layers of MEV protection:

  • Private mempool: Transactions never appear in public mempool
  • Direct builder submission: Bypasses public relayers
  • Atomic execution: No opportunity for sandwich attacks

Gas Sponsorship

DuckyDux’s unique sponsorship model allows you to pay gas fees on behalf of users, enabling truly gasless experiences.

Bundle Types

Regular Bundle: Standard atomic execution with user-paid gas

{ "txs": [ "0x02f87...", // Raw signed transaction 1 "0x02f87..." // Raw signed transaction 2 ] }

Complete Bundle Workflow

Get Quote with Bundle Preparation

When getting a swap quote, always include check_approvals=true to get all necessary transactions:

const response = await fetch('https://api.duckydux.com/v1/swap/quote?' + new URLSearchParams({ sources: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC sinks: '0x6B175474E89094C44Da98b954EedeAC495271d0F', // DAI supplies: '1000000000', // 1000 USDC demands: '-1', slippage: '0.5', sender: userAddress, check_approvals: 'true' // Critical for bundle preparation })); const quote = await response.json();

Sign All Transactions

The API response provides all the necessary transactions with sequential nonces and gas parameters pre-populated. Sign each transaction as provided.

const signedTxs = [] // Sign approval transaction (if needed) if (quote.approvals.length > 0) { const signedApproval = await web3.eth.accounts.signTransaction(quote.approvals[0].tx, privateKey) signedTxs.push(signedApproval.rawTransaction) } // Sign swap transaction(s) for (const tx of quote.txs) { const signedSwap = await web3.eth.accounts.signTransaction(tx, privateKey) signedTxs.push(signedSwap.rawTransaction) }

Submit the Bundle

// Submit regular bundle const bundleResponse = await fetch('https://api.duckydux.com/v1/bundles/send', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ txs: signedTxs }) }); const bundleResult = await bundleResponse.json(); console.log('Bundle ID:', bundleResult.bundleId);

Monitor Bundle Status

Track your bundle’s progress through the execution pipeline:

const bundleId = bundleResult.bundleId // Poll for bundle status const checkStatus = async () => { const statusResponse = await fetch(`https://api.duckydux.com/v1/bundles/${bundleId}`) const status = await statusResponse.json() console.log('Status:', status.status) console.log('Block:', status.blockNumber) return status } // Poll until bundle is included or fails const pollStatus = async () => { let status do { status = await checkStatus() if (status.status === 'pending') { await new Promise(resolve => setTimeout(resolve, 2000)) } } while (status.status === 'pending') return status }

Bundle Status Lifecycle

StatusDescriptionAction Required
pendingBundle received and is awaiting processing.Wait and poll for status updates.
submittedBundle has been sent to block builders for inclusion.Continue polling.
includedBundle was successfully included in a block.Your transaction is complete.
failedAn error occurred before the bundle could be included.Check the error message for details and resubmit if necessary.
expiredThe bundle was not included in its target block and has expired.Resubmit the bundle, potentially with a higher gas fee.

Note on Reverts: Because bundles are executed atomically, a transaction revert will cause the entire bundle to fail before inclusion. You will not see a reverted status; instead, the bundle will be marked as failed.

Best Practices

Pro Tip: Always bundle approval + swap transactions for ERC20 swaps to prevent MEV attacks and ensure atomic execution.

Security Guidelines

  1. Never send approvals separately - Always bundle with the actual swap
  2. Use consecutive nonces - Ensure transaction ordering
  3. Set appropriate gas limits - Account for all transactions in bundle
  4. Monitor bundle status - Don’t assume success without confirmation

Gas Optimization

  • Use moderate gas prices to ensure inclusion - Bundle multiple operations to amortize costs - Consider gas price at bundle submission time

Advanced Use Cases

Arbitrage Bundles

Execute cross-DEX arbitrage with atomic protection:

// Bundle: Buy on DEX A, Sell on DEX B const bundleTxs = [ signedBuyTx, // Buy token on Uniswap signedSellTx, // Sell token on Curve ] await submitBundle({ txs: bundleTxs })

DeFi Strategies

Complex multi-step operations:

// Bundle: Approve + Deposit + Stake const strategyBundle = [ signedApproveTx, // Approve token spending signedDepositTx, // Deposit to protocol signedStakeTx, // Stake LP tokens ]

Error Handling

Common bundle failures and solutions: - Nonce gaps: Ensure consecutive nonces - Gas estimation: Account for all transactions - Slippage: Use appropriate slippage for volatile markets

try { const result = await submitBundle(bundleData) if (result.status === 'failed') { // Handle specific failure reasons if (result.error.includes('nonce')) { // Resubmit with correct nonces } else if (result.error.includes('gas')) { // Increase gas limits } } } catch (error) { console.error('Bundle submission failed:', error) }

Next Steps

Last updated on