Cross-chain bridges remain the highest-value targets in DeFi. The February 2026 CrossCurve exploit is a masterclass in what happens when a bridge contract trusts its own validation logic more than the messaging protocol it depends on. One missing access control check. $3 million gone across multiple chains. Let's tear it apart.
What Is CrossCurve?
CrossCurve (formerly EYWA) is a cross-chain liquidity bridge connecting multiple EVM networks. Its architecture combines three components:
-
Axelar-based
ReceiverAxelarcontracts — receive and validate inbound cross-chain messages -
Internal
PortalV2bridge contracts — hold liquidity and release tokens when authorized by the receiver - Multi-validation layer — Axelar, LayerZero, and EYWA's own Oracle Network for redundant verification
The selling point was defense-in-depth: multiple validation layers meant a single point of failure shouldn't be catastrophic. The reality was different.
The Attack: Step by Step
On February 1, 2026, an attacker identified that CrossCurve's ReceiverAxelar contract exposed an expressExecute() function — designed for expedited cross-chain execution — that was publicly callable and lacked the critical gateway origin check.
Step 1: Spoof the Message Origin
The attacker generated a fresh commandId and supplied fake sourceChain and sourceAddress parameters to make the call appear to be a legitimate Axelar cross-chain message:
// What the attacker called — simplified
receiverAxelar.expressExecute(
freshCommandId, // Never-before-seen ID
"arbitrum", // Fake source chain
fakeSourceAddress, // Attacker-controlled "source"
maliciousPayload // Token release instruction
);
Step 2: Craft the Malicious Payload
The ABI-encoded payload instructed the contract to mint/transfer approximately 999.8 million EYWA tokens to the attacker's wallet. The payload format matched what a legitimate Axelar message would produce — the contract couldn't tell the difference.
Step 3: Bypass "Validation"
Here's where the design failed. The contract's validation consisted of:
-
commandIduniqueness check — Had this ID been used before? No (the attacker used a fresh one each time). ✅ Pass. -
Confirmation threshold — Set to
1, effectively disabling multi-guardian verification. ✅ Pass.
What was missing: The Axelar Gateway validation check — the one function call that verifies a message actually originated from Axelar's cross-chain messaging system.
// What SHOULD have been there (and wasn't):
require(
axelarGateway.validateContractCall(
commandId, sourceChain, sourceAddress, payloadHash
),
"Not approved by gateway"
);
Step 4: Multi-Chain Drainage
The attacker repeated this process across every chain CrossCurve supported — Arbitrum, Ethereum, and others. The PortalV2 contract's balance dropped from ~$3 million to near zero.
Post-exploit, the attacker:
- Swapped stolen tokens to WETH via CoW Protocol on Arbitrum
- Bridged funds to Ethereum via Across Protocol
- Minted EYWA on Ethereum (though frozen CEX deposits limited liquidation there)
Root Cause: The One-Line Fix That Would Have Saved $3M
The vulnerability is embarrassingly simple. In Axelar's standard integration pattern, every receiver contract MUST validate that inbound messages passed through the Axelar Gateway:
// Standard Axelar receiver pattern
function execute(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
bytes calldata payload
) external {
// THIS IS THE CRITICAL CHECK
bytes32 payloadHash = keccak256(payload);
require(
gateway.validateContractCall(
commandId, sourceChain, sourceAddress, payloadHash
),
"Not approved by gateway"
);
// Only NOW process the payload
_processPayload(payload);
}
CrossCurve's expressExecute() omitted this check entirely. The only guard was commandId uniqueness — a trivially bypassable control since the attacker can generate unlimited fresh IDs.
Two compounding factors amplified the damage:
| Factor | Expected | Actual |
|---|---|---|
expressExecute() access control |
Restricted to authorized relayers |
public / no restriction |
| Confirmation threshold | Multi-guardian quorum (e.g., 3-of-5) | Set to 1 (single confirmation) |
The Bridge Security Anti-Pattern
CrossCurve's failure illustrates a recurring pattern in bridge exploits:
Bridge Contract Security = Message Validation × Access Control × Quorum
If ANY factor = 0, the entire product = 0
Compare with other bridge exploits:
| Bridge | Year | Root Cause | Loss |
|---|---|---|---|
| Ronin | 2022 | Compromised validator keys (5/9 quorum) | $624M |
| Wormhole | 2022 | Signature verification bypass | $326M |
| Nomad | 2022 | Trusted root set to 0x00 (any message valid) | $190M |
| IoTeX | 2026 | Compromised private key on Ethereum validator | $4.3M |
| CrossCurve | 2026 | Missing gateway validation on expressExecute | $3M |
The pattern is consistent: bridges fail at the validation boundary, not in the fund management logic. The code that moves tokens is usually fine. The code that decides whether to move tokens is where the bodies are buried.
Defensive Patterns for Bridge Developers
1. Never Skip Gateway Validation on Express Paths
The "express" execution path exists for speed, not for skipping security. Every code path that can trigger fund releases MUST go through the messaging protocol's validation:
// WRONG: Express path skips validation for speed
function expressExecute(...) external {
require(!usedCommandIds[commandId], "Already used");
_execute(payload); // No gateway check!
}
// RIGHT: Express path validates first, then executes faster
function expressExecute(...) external {
require(!usedCommandIds[commandId], "Already used");
require(
gateway.validateContractCall(commandId, sourceChain, sourceAddress, keccak256(payload)),
"Not approved by gateway"
);
usedCommandIds[commandId] = true;
_execute(payload);
}
2. Implement Defense-in-Depth That Actually Defends
CrossCurve advertised three validation layers (Axelar, LayerZero, EYWA Oracle). But if the first layer's check is missing, the others never get a chance to reject the message. Defense-in-depth means each layer independently validates, not that they're stacked sequentially with bypass potential.
// Defense-in-depth pattern for bridge receivers
function executeWithMultiValidation(...) external {
// Layer 1: Protocol gateway validation
require(gateway.validateContractCall(...), "Gateway rejected");
// Layer 2: Source chain allowlist
require(allowedSourceChains[sourceChain], "Chain not allowed");
// Layer 3: Source address verification
require(
trustedRemotes[sourceChain] == sourceAddress,
"Untrusted remote"
);
// Layer 4: Rate limiting
require(
rateLimiter.checkAndUpdate(token, amount),
"Rate limit exceeded"
);
_execute(payload);
}
3. Set Meaningful Quorum Thresholds
A confirmation threshold of 1 is functionally the same as no threshold. For multi-guardian systems:
- Minimum viable quorum: 2-of-3 for small bridges
- Production quorum: 5-of-9 or higher for bridges holding >$1M
- Never: Set threshold to 1 in production
4. Restrict Express Execution to Known Relayers
Even with gateway validation, expressExecute() should be callable only by authorized relayer addresses:
modifier onlyRelayer() {
require(authorizedRelayers[msg.sender], "Not an authorized relayer");
_;
}
function expressExecute(...) external onlyRelayer {
// validation + execution
}
Audit Checklist for Cross-Chain Bridge Contracts
Use this checklist when auditing any bridge receiver contract:
- [ ] Every execution path (standard + express) validates messages through the gateway
- [ ]
expressExecute()is not publicly callable without access controls - [ ] Source chain and source address are validated against an allowlist
- [ ] Confirmation/quorum threshold is set to a meaningful value (>1)
- [ ] Rate limiting exists on token releases per time window
- [ ]
commandIdreuse prevention is a secondary check, not the primary security boundary - [ ] Emergency pause functionality exists and is tested
- [ ] Token release amounts are bounded per transaction
- [ ] Multi-guardian/multi-sig validation cannot be bypassed by any code path
Detection: Catching This On-Chain
If you're monitoring bridge contracts, flag these patterns:
# Pseudocode for bridge exploit detection
def detect_bridge_exploit(tx):
# Flag 1: expressExecute called by non-relayer
if tx.function == "expressExecute" and tx.sender not in KNOWN_RELAYERS:
alert("Unauthorized expressExecute call", severity="CRITICAL")
# Flag 2: Large token release without corresponding deposit
if tx.releases_tokens and tx.amount > THRESHOLD:
deposit = find_matching_deposit(tx.commandId, tx.sourceChain)
if not deposit:
alert("Token release without matching deposit", severity="CRITICAL")
# Flag 3: Rapid sequential releases (multi-chain drain pattern)
recent_releases = get_releases(window="10m")
if len(recent_releases) > NORMAL_RATE * 3:
alert("Anomalous release velocity", severity="HIGH")
Timeline
| Time | Event |
|---|---|
| Feb 1, 2026 | Attacker begins exploiting expressExecute() across multiple chains |
| Feb 1, 2026 | PortalV2 balances drop from ~$3M to near zero |
| Feb 1, 2026 | Attacker swaps stolen tokens → WETH via CoW Protocol (Arbitrum) |
| Feb 1, 2026 | Funds bridged to Ethereum via Across Protocol |
| Feb 1, 2026 | CrossCurve team detects incident, shuts down platform |
| Post-exploit | CrossCurve offers 10% white-hat bounty for fund return |
| Post-exploit | EYWA tokens on Ethereum frozen at CEX deposit addresses |
Key Takeaway
The CrossCurve exploit wasn't sophisticated. It didn't require flash loans, oracle manipulation, or zero-day cryptographic breaks. It required calling a public function with fabricated parameters — something any Solidity developer could do in five minutes.
The lesson for every bridge team: your express execution path is your weakest link. If it exists for speed, it will be exploited for theft unless it carries the same validation guarantees as your standard path.
And for auditors: when reviewing bridge contracts, don't just verify that validation exists somewhere in the codebase. Verify that it exists on every single code path that can trigger fund movements. The CrossCurve team had gateway validation logic — they just forgot to call it in the one function that mattered most.
This analysis is part of the DeFi Security Research series. Follow for weekly deep-dives into smart contract vulnerabilities, audit techniques, and defense patterns.
Top comments (0)