Batch Timeout Race Condition in Major Cosmos-to-Ethereum Cross-Chain Bridge
During independent security research, SDX Shadow Labs discovered a critical race condition in a prominent Cosmos-to-Ethereum cross-chain bridge module. The vulnerability existed within the state transition logic governing Cosmos-side batch timeout cancellations and Ethereum batch execution events.
This flaw allowed cancelled transactions to silently bypass state cleanup, re-enter the unbatched transaction pool, and be executed multiple times on Ethereum.
Disclosure Timeline: This vulnerability was discovered and reported in April 2026. This public advisory is being published in August 2026 following a 150-day embargo and confirmed successful remediation by the vendor.
1. Background: Cross-Chain Batching
Cross-chain bridges operate by moving assets and state between two disparate networks. To save on Ethereum gas fees, Cosmos-to-Ethereum bridges group multiple user transactions (e.g., token withdrawals) into a single payload called a Batch.
- Users request withdrawals on Cosmos.
- The Cosmos bridge module groups these withdrawals into a Batch and assigns it a unique
Nonce. - Relayers submit this Batch to the Ethereum smart contract for execution.
- If the Ethereum network is congested, the Batch may fail to execute within a predefined block-height window, triggering a Timeout.
2. The Vulnerability Mechanics
The vulnerability lies in how the Cosmos module handled the timing conflict between a Timeout occurring on the Cosmos chain and a delayed BatchExecutedEvent arriving from Ethereum.
When a batch times out, the Cosmos module must securely cancel it. The intended logic was:
- Delete the batch record.
- Return the underlying transactions to the unbatched pool so they can be tried again.
However, a race condition occurred if the Ethereum transaction did eventually execute, but the event relay to Cosmos was delayed.
Race Condition Sequence of Events
| Step | Component | Action | Resulting State |
|---|---|---|---|
| 1. Batch Submit | Relayer | Submits Batch #42 (Tx A, Tx B) to Ethereum. |
Pending on Ethereum |
| 2. Timeout | Cosmos Module | Reaches timeout block-height on Cosmos. | Batch #42 Cancelled |
| 3. Refund | Cosmos Module | Returns Tx A, Tx B to unbatched pool. |
Funds unlocked on Cosmos |
| 4. ⚡ The Race | Ethereum Contract | Finally executes the delayed Batch #42. | Double Spend Begins |
| 5. Cleanup Fail | Cosmos Module | Attempts to clean Cosmos state for Batch #42. | Silent Failure (Batch deleted) |
| 6. Re-Batch | Relayer | Pulls Tx A, Tx B into NEW Batch #43. |
Batch #43 Pending |
| 7. Execution | Ethereum Contract | Executes Batch #43. | Double Spend Complete |
Because the Cosmos module had already deleted the batch record during the timeout, the incoming BatchExecutedEvent could not find the batch in state. Instead of safely halting or flagging an invariant violation, the cleanup function failed silently, logging an error but allowing the state to proceed.
3. Exploitation Scenario
A sophisticated attacker could intentionally engineer this race condition to double-spend funds:
- Initiate Withdrawal: The attacker requests a massive withdrawal of USDC from Cosmos to Ethereum.
- Congest the Network: The attacker waits for or artificially induces a gas spike on Ethereum, causing the Relayer's batch submission to stall.
- Trigger Timeout: The batch crosses the timeout block-height on Cosmos. The Cosmos module cancels the batch and returns the attacker's USDC withdrawal to the pool.
- Force Execution: The attacker privately accelerates the stalled Ethereum transaction (via Flashbots or high gas), forcing the original batch to execute on Ethereum after it was cancelled on Cosmos.
- Re-Batch and Re-Execute: The Cosmos module re-batches the refunded USDC withdrawal into a new batch. The attacker receives their funds on Ethereum a second time.
Impact: Total loss of bridge solvency. An attacker could theoretically drain the entire Ethereum-side liquidity pool of the bridge.
4. Remediation & Secure Architecture
The vendor was notified via our coordinated responsible disclosure protocol. The issue was successfully addressed through architectural changes to the batch lifecycle:
- Tombstoning: Cancelled batches are no longer simply deleted from state. They are marked with a cryptographic tombstone.
- Strict Event Validation: When a
BatchExecutedEventarrives, the system checks against both active and tombstoned batches. - Transaction ID Tracking: The system now strictly tracks the execution state of individual transaction IDs, ensuring a single transaction hash can never be executed on Ethereum more than once, regardless of batch nonces.
Note: All testing and validation was conducted locally using isolated fork-testing frameworks. No mainnet activity was generated, and no real funds were exposed during this research.