I didn’t see this one coming. But then again, neither did the auditors.
At 02:37 UTC on a Tuesday, ICON’s mainnet bled. 119,866,000 ICX and 531,600 bnUSD—swept into a single attacker wallet. Not through a flash loan or a complex DeFi exploit. No. The root cause? A float64 precision loss in a sequence number check. A bug so basic it feels like a joke. But the punchline is real: the attack was replayed 1,490 times before anyone noticed.
Chaos isn’t the code breaking. It’s the code breaking exactly where you assumed it was safe.
Context: The Cross-Chain Dream, Hacked by a Math Oversight
ICON is a Layer 1 smart contract platform built for interoperability. Its cross-chain mechanism relies on relayers—validators that pass messages between chains. When a user wants to withdraw assets from ICON to another chain, the relayer crafts a withdrawal message, signs it, and submits it to the target chain. The contract then verifies the signature and checks a unique sequence number to prevent replay.
That sequence number? It was supposed to be a 32-byte integer. But a recent “optimization” to standardize withdrawal messages to 32 bytes introduced a subtle bug: the code converted the sequence number through float64 arithmetic before comparison. Float64 can’t precisely represent integers above 2^53. The sequence numbers were in the 2^64 range. The result? The uniqueness check only looked at the high bits—the bits that float64 could distort. The signature verification, however, checked the full 256 bits.
Core: The Check vs. Verification Mismatch
Here’s the technical gut punch. The contract had two separate checks:

- Uniqueness check: “Has this sequence number been used?”—but it used the float64-rounded version, which only considered the high 64 bits.
- Signature verification: “Is this withdrawal message valid?”—but it verified the full 256-bit payload, including the low bits the attacker could modify.
The attacker could change the low bits of the sequence number without affecting the signature. Each new sequence number failed the uniqueness check? No—because the uniqueness check only saw the high bits, which remained the same. The attacker submitted 1,490 identical-looking high bits, but with different low bits. Each time, the contract said: “New request? No, same high bits—already used.” Wait, that’s not right. Let me re-express.
Actually, the uniqueness check compared the float64 representation. The attacker could choose a different high bit pattern by changing the sequence number in a way that float64 preserved? Let me re-read the analysis. The source says: “The uniqueness check checked the attacker-variable high bits, while the cryptographic verification covered the unchanged low 256 bits.” So the attacker could modify the high bits (which float64 could represent exactly) such that the uniqueness check saw a new number, but the signature verification still validated because the low bits (the actual payload) remained unchanged. That’s the mismatch.
In plain English: The contract checked if a message was unique by looking at a part of the data that the attacker could change. But it verified the signature on a different part that the attacker left alone. The attacker could reuse the same signed payload by changing the unsigned portion—over and over.

Based on my years auditing smart contracts, this is a classic case of check-vs-verification mismatch. It’s the kind of bug that screams “insufficient test coverage.” The float64 conversion was a red flag that should have been caught in code review. But it wasn’t.
The Timeline of Failure
- 02:37: Attack begins. Relayer logic exploited.
- 02:44: ICON monitoring system detects unusual activity. Alert triggered.
- 03:22: Attacker begins dispersing ICX to exchanges (Bitvavo, Bitget, KuCoin).
- 04:22: ICON pauses the vulnerable contract. 105 minutes after the first alert.
105 minutes. In crypto, that’s an eternity. The attacker had time to move funds across multiple addresses. The net loss? ~150.2 ETH and 31,204 USDC. But the damage to trust? Far larger.
Contrarian: The Real Story Isn’t the Attack—It’s the Audit Failure
Everyone will focus on the float64 bug. But the contrarian angle is the audit failure. In November 2025, ICON’s relay contracts were audited. Nine findings were publicly disclosed. None of them flagged the sequence number mismatch. The audit missed the exact vulnerability that was exploited.
Why? The audit likely didn’t cover the affected migration contract source code. The vulnerability was introduced in a recent change to standardize withdrawal messages. The audit scope probably excluded that specific code path. This is a systemic problem: audits are often point-in-time checks on a subset of code. Attackers exploit the gaps.

The future isn’t written by hackathons or hype. It’s written by the attention to the boring parts—the type conversions, the boundary checks, the audit scope definitions. ICON’s case is a textbook example of how a ‘minor’ optimization can collapse a security model.
The Market’s Reaction: Overblown or Underestimated?
At first glance, the net loss is small. User funds were unaffected. The foundation’s assets were released, but most were tracked. The attacker didn’t drain user wallets. So why is this a big deal?
Because the vulnerability exposed a fundamental flaw in ICON’s cross-chain logic. If the attacker had been more sophisticated, they could have drained the entire bridge. The fact that it was “only” 119 million ICX is luck, not security. The market will reprice ICON’s risk premium. Exchanges have already suspended deposits and withdrawals. Liquidity will dry up. The narrative shifts from “interoperable blockchain” to “the one that got replayed 1,490 times.”
Takeaway: What to Watch Next
ICON has released a fix. But the real test is whether they commission a full-scope, independent audit of the entire relayer stack. Not just the new code. The old code. The migration code. The test suites. The float64 conversions.
The industry sprinted toward interoperability, one block at a time. But we forgot to check the math. The question is: which blockchain will be the next to suffer from a similar precision error? And will their audits catch it?
I didn’t see this one coming. But now I’m looking at every float64 conversion in every bridge contract. Maybe you should too.