The January 3, 2024 exploit of Radiant Capital, which resulted in the theft of $4.5 million in ETH from the protocol’s Arbitrum deployment, provides a textbook case study in how seemingly minor mathematical rounding errors can cascade into catastrophic financial losses. For developers and security researchers, understanding the mechanics of this attack is essential for building and auditing robust smart contracts. This tutorial walks through the vulnerability, the attack vector, and the defensive measures that could have prevented the exploit.
The Objective
This tutorial aims to equip experienced smart contract developers with a deep understanding of precision and rounding vulnerabilities in DeFi protocols. By analyzing the Radiant Capital exploit in detail, you will learn to identify similar patterns in your own code and in protocols you audit. The focus is on practical, actionable knowledge that can be applied immediately to improve the security of lending protocols, DEXs, and other financial smart contracts.
Prerequisites
This tutorial assumes familiarity with Solidity, DeFi protocol mechanics (lending, borrowing, flash loans), and basic numerical analysis concepts. You should understand how fixed-point arithmetic works in Ethereum smart contracts, including the use of WAD (10^18) and RAY (10^27) precision formats. Knowledge of Aave-style aToken mechanics is helpful but not required, as we will cover the relevant concepts as they arise in the analysis.
The attack transaction on Arbitrum (0x1ce7e9a9e3b6dd3293c9067221ac3260858ce119ecb7ca860eac28b2474c7c9b) is the primary reference for this tutorial. With ETH trading at approximately $2,210 on the date of the attack, the 4.5 million dollar loss translates to roughly 2,035 ETH — a significant amount by any standard.
Step-by-Step Walkthrough
Step 1: Understanding the Vulnerable Formula
Radiant Capital used the formula (a * RAY + b/2) / b for converting between aToken balances and underlying asset amounts. The RAY constant (10^27) provides high precision for financial calculations. The addition of b/2 implements rounding-to-nearest behavior. This formula is mathematically correct when the ratio between ‘a’ and ‘b’ falls within expected ranges. The vulnerability emerges when ‘b’ approaches the magnitude of ‘a * RAY’.
Step 2: The Normal Case vs. The Exploitable Case
Under normal conditions, the liquidity index is close to 1 * RAY (10^27), and a user’s aToken balance is a large number. The division produces an accurate result with negligible rounding error. However, when the attacker inflates the liquidity index to approximately 271.8 * 10^27 through the flash loan manipulation, the denominator in subsequent calculations becomes comparable to the numerator, and rounding errors become massive.
Step 3: The Attack Sequence
The attacker borrowed 3 million USDC via a flash loan from Aave, then deposited 2 million USDC into Radiant, receiving rUSDCn tokens. A second flash loan through Radiant’s own contract allowed simultaneous borrowing and withdrawal, manipulating the liquidity index. The attacker then funded a new contract with 543,600 USDC — deliberately chosen to be exactly twice the manipulated liquidity index value — and exploited the rounding error in the burn function.
When burning 407,700 rUSDCn to withdraw 407,700 USDC, the calculation produced 1.49999999, which rounded down to 1. This meant only 271,800 rUSDCn were burned instead of 407,700 — a 1/3 discrepancy that directly translated to profit for the attacker.
Step 4: Identifying the Pattern in Code Reviews
When auditing lending protocols, look for any division operation where the divisor can be influenced by user input or where the divisor’s value depends on a mutable state variable like a liquidity index. Specifically flag any formula where the divisor can approach the same order of magnitude as the dividend. In Radiant’s case, the liquidity index should have been bounded by invariant checks to prevent it from being inflated beyond a reasonable range.
Troubleshooting
If you encounter similar rounding patterns in a codebase, the fix involves implementing explicit bounds checking on intermediate values. For the Radiant case, adding a require statement that validates the liquidity index remains within expected bounds after any deposit or withdrawal operation would have prevented the attack. Additionally, using mulDiv operations with built-in overflow and precision checks (available in libraries like OpenZeppelin’s Math) provides a more robust alternative to manual precision arithmetic.
Another defensive measure is to implement reentrancy-style guards on flash loan callbacks. While the Radiant exploit was not technically a reentrancy attack, it exploited the same class of vulnerability: the ability to execute arbitrary logic within a protocol’s callback function that could manipulate state variables in unexpected ways.
Mastering the Skill
To truly master smart contract security auditing, practice analyzing real exploit transactions using tools like Tenderly or Foundry’s debug capabilities. Replay the Radiant Capital attack transaction on an Arbitrum fork to observe the state changes at each step. This hands-on experience builds the intuition necessary to identify similar vulnerabilities in novel protocols. The field of smart contract security is constantly evolving — what was considered safe yesterday may be exploitable tomorrow. Continuous learning through real-world case studies like the Radiant Capital hack is the most effective way to stay ahead of attackers.
Disclaimer: This article is for informational and educational purposes only and does not constitute financial advice. Always conduct your own research before making investment decisions.
4.5M gone because someone didnt scale by an extra 1e9 before dividing. one line of code, one constant, millions gone
the RAY precision issue is well known in Solidity circles. the problem is that fixing it requires a gas-costly precision bump that most protocols refuse to implement
protocols refuse because gas optimization rankings on audit reports reward cheaper over safer. misaligned incentives all the way down
gas optimization audits rank protocols higher for cheaper code. the incentive to save 2000 gas vs prevent a $4.5M exploit is completely broken
the fix is literally multiplying by a higher precision constant. costs like 500 more gas per call. protocols skip it to look better on gas benchmarks and then lose millions
vlad exactly. the fix is literally one extra mul but audit leaderboards rank by gas efficiency so protocols skip it to look better
walkthrough of the Radiant exploit is solid. the manipulation of a and b to be close in magnitude is a clever attack vector
every new lending fork should be required to read this before shipping. seriously
the precision manipulation to make a and b close was elegant in a terrifying way. 4.5M gone because of floating point math