Disputing Root Bundles
How to challenge invalid bundle proposals — manually or with automated tooling.
Anyone can dispute a root bundle proposal if they believe it is invalid. Disputes require posting a bond, and the outcome is resolved by UMA's Data Verification Mechanism (DVM) — a decentralized voting system where UMA token holders determine whether the proposal was valid.
Why Disputes Matter
The bond mechanism serves two purposes:
- Prevents fraud — Invalid proposals that would move funds incorrectly can be challenged before execution
- Prevents denial-of-service — The bond requirement makes it expensive to file frivolous disputes that block valid proposals
If a dispute succeeds (the proposal was invalid), the proposer loses their bond to the disputer. If a dispute fails (the proposal was valid), the disputer loses their bond to the proposer.
Manual Dispute Process
Check Bond Requirements
Query the HubPool contract to determine the required bond token and amount.
import { createPublicClient, http, parseAbi } from "viem";
import { mainnet } from "viem/chains";
const publicClient = createPublicClient({
chain: mainnet,
transport: http(),
});
const HUBPOOL = "0xc186fA914353c44b2E33eBE05f21846F1048bEda";
const hubPoolAbi = parseAbi([
"function bondToken() view returns (address)",
"function bondAmount() view returns (uint256)",
]);
const bondToken = await publicClient.readContract({
address: HUBPOOL,
abi: hubPoolAbi,
functionName: "bondToken",
});
const bondAmount = await publicClient.readContract({
address: HUBPOOL,
abi: hubPoolAbi,
functionName: "bondAmount",
});
console.log("Bond token:", bondToken);
console.log("Bond amount:", bondAmount); // Typically ~0.45 ABTMint Across Bond Token (ABT)
ABT functions like WETH — deposit ETH to mint ABT.
import { parseAbi, parseEther } from "viem";
// bondToken address from step 1
const ABT_ADDRESS = bondToken;
const mintHash = await walletClient.writeContract({
address: ABT_ADDRESS,
abi: parseAbi(["function deposit() payable"]),
functionName: "deposit",
value: parseEther("0.5"), // Slightly more than required bond
});
await publicClient.waitForTransactionReceipt({ hash: mintHash });
console.log("ABT minted:", mintHash);Approve HubPool
Grant the HubPool contract permission to transfer your ABT bond.
const approveHash = await walletClient.writeContract({
address: ABT_ADDRESS,
abi: parseAbi(["function approve(address spender, uint256 amount)"]),
functionName: "approve",
args: [HUBPOOL, bondAmount],
});
await publicClient.waitForTransactionReceipt({ hash: approveHash });
console.log("Approval confirmed:", approveHash);Submit Dispute
Call disputeRootBundle() on the HubPool contract.
const disputeHash = await walletClient.writeContract({
address: HUBPOOL,
abi: parseAbi(["function disputeRootBundle()"]),
functionName: "disputeRootBundle",
});
console.log("Dispute submitted:", disputeHash);The dispute is now escalated to UMA's DVM for resolution.
Automated Disputes
The Across relayer repository includes automated dispute tooling that handles bond minting, approval, and submission.
git clone https://github.com/across-protocol/relayer-v3.git
cd relayer-v3
yarn install && yarn buildConfigure your .env file with a mnemonic for the EOA that will submit the dispute:
SECRET=your_mnemonic_phrase_hereEnsure the account is funded with:
- At least 0.45 ETH (to mint ABT for the bond)
- Additional ETH for transaction gas costs
yarn disputeThe script will:
- Check if there's a pending proposal to dispute
- Mint ABT if needed
- Approve the HubPool
- Prompt you for confirmation with a transaction hash
- Submit the dispute transaction
Only dispute if you are confident the proposal is invalid. If the DVM votes that the proposal was valid, you lose your bond (~0.45 ABT / ETH). Run validation checks before disputing — see Validating Root Bundles.
After a Dispute
Once a dispute is filed:
- The pending bundle proposal is paused — it cannot be executed
- The dispute is escalated to UMA's DVM
- UMA token holders vote on whether the proposal was valid per UMIP-157 / UMIP-179 rules
- The vote resolves:
- Proposal invalid → Proposer's bond goes to the disputer. A new bundle must be proposed.
- Proposal valid → Disputer's bond goes to the proposer. The bundle is executed.