The February 11, 2025 exploit of zkLend — a Starknet-based lending protocol that lost approximately 3,666 ETH ($9.6 million) to a flash loan attack exploiting rounding errors — serves as a textbook case study in one of the most subtle and dangerous vulnerability classes in DeFi: decimal precision vulnerabilities. With Bitcoin at $95,747 and Ethereum at $2,602, the stakes in DeFi security have never been higher. This advanced tutorial walks through the technical mechanics of rounding vulnerabilities in lending protocols and provides a systematic methodology for detecting them during smart contract audits.
The Objective
The goal of this tutorial is to equip experienced smart contract developers and security auditors with the knowledge and techniques needed to identify rounding vulnerabilities in lending protocol accumulators. By the end of this walkthrough, you will understand how lending accumulators work, where rounding errors can creep in, how attackers exploit these errors using flash loans, and what patterns to look for when auditing a lending protocol’s mathematical operations. This is not a beginner tutorial — it assumes familiarity with Solidity or Cairo (Starknet’s programming language), DeFi mechanics, and basic financial mathematics.
Prerequisites
Before diving in, ensure you have the following foundation: a solid understanding of how lending protocols like Aave, Compound, or zkLend work, including concepts like supply rates, borrow rates, collateralization ratios, and liquidation mechanisms. Familiarity with fixed-point arithmetic in smart contracts, including how different number representations (e.g., ray, wad) handle decimal precision. Experience with at least one smart contract auditing framework, such as Slither, Echidna, or Medusa. Access to a local development environment with Starknet tooling (Scarb, Starknet Foundry) if you plan to reproduce the patterns described here on Cairo contracts.
Understanding of flash loan mechanics is also essential. Flash loans allow users to borrow large amounts of capital without collateral, provided the loan is repaid within the same transaction. This creates a powerful tool for attackers: they can borrow massive amounts, manipulate protocol state, and extract value, all atomically.
Step-by-Step Walkthrough
Step 1: Understand the lending accumulator pattern. Most lending protocols use an accumulator pattern to track interest accrual over time. The accumulator starts at a base value (typically 1.0 represented as a large fixed-point number) and is multiplied by the interest rate at each time step. For example, if the annual interest rate is 5% and a time step represents one day, the accumulator is multiplied by approximately 1.000136 (5% divided by 365 days) each day. Users’ balances are calculated by multiplying their initial deposit amount by the ratio of the current accumulator to the accumulator at the time of deposit.
Step 2: Identify where rounding occurs. Fixed-point arithmetic in smart contracts inherently involves rounding. When you multiply two fixed-point numbers and then divide by the scaling factor, you must decide which direction to round. In lending protocols, the direction of rounding matters enormously because the same accumulator is used to calculate all user balances. If the accumulator rounds in a direction that benefits depositors, the protocol loses value on each rounding event. If it rounds in a direction that benefits the protocol, individual depositors lose tiny amounts on each operation. Attackers exploit the gap between these rounding directions.
Step 3: Analyze the attack surface. The zkLend attack exploited a specific pattern: the attacker used flash loans to create a large position, then manipulated the lending accumulator through a series of rapid operations that each introduced a small rounding error. By performing many operations in a single transaction, the attacker accumulated enough rounding error to extract significant value. The key insight is that when the accumulator is updated, the rounding direction can be influenced by the size and timing of operations — and a flash loan attacker controls both.
Step 4: Build detection patterns. When auditing a lending protocol, look for these specific patterns. First, check all accumulator update functions for consistent rounding direction. If some operations round up and others round down, there may be an exploitable inconsistency. Second, simulate accumulator updates with extreme values — what happens when someone deposits or withdraws the maximum possible amount? What happens with the minimum amount? Third, analyze multi-step operations within a single transaction. Can an attacker deposit, accrue interest, withdraw, and redeposit in a way that exploits rounding at each step? Fourth, verify that the accumulator’s scaling factor is large enough to minimize rounding errors for the expected range of values.
Step 5: Write fuzz tests. Fuzz testing is one of the most effective techniques for finding rounding vulnerabilities. Write property-based tests that verify invariants like: the total value in the protocol should always be greater than or equal to the sum of all user claims, the accumulator should never decrease (assuming positive interest rates), and rounding should always favor the protocol over individual users. Use tools like Echidna or Medusa to run thousands of random transaction sequences and check whether any of these invariants can be violated.
Troubleshooting
Common challenges when auditing for rounding vulnerabilities include: difficulty reproducing exploits that require specific state setups — use forked mainnet environments to test against realistic protocol states. False positives from fuzz testing — not every invariant violation leads to an exploitable condition, but every one deserves investigation. Performance limitations when simulating large flash loan amounts — optimize your test setup to handle the computational overhead of manipulating large token balances. In Cairo (Starknet’s language), the mathematical primitives differ from Solidity, so rounding behavior may not follow the patterns you are used to — study the specific fixed-point libraries used by the protocol.
Mastering the Skill
Detecting rounding vulnerabilities is a skill that improves with practice and exposure to real-world exploits. Study the post-mortems of previous DeFi exploits — zkLend, bZx, Cream Finance, and others — to understand the specific patterns attackers have used. Contribute to audit contests on platforms like Code4rena and Sherlock, where you can practice finding vulnerabilities in real protocols. Build your own lending protocol as an exercise, deliberately introducing rounding errors and then finding them. The combination of theoretical understanding and hands-on practice is the most reliable path to mastery in this critical area of DeFi security.
Disclaimer: This article is for educational purposes only and does not constitute financial or security advice. Always conduct professional audits before deploying smart contracts to production.
Decimal precision bugs are the silent killers in DeFi. Glad someone is writing actual technical breakdowns instead of just protocol got hacked headlines
Cairo is a different beast for auditing. Not enough tooling compared to Solidity, which makes these Starknet protocols extra risky.
the zkLend writeup showing how a flash loan amplified a rounding error into 3,666 ETH stolen was genuinely educational. more of this please
educational writeups are rare because protocols try to downplay the exploit. zkLend was unusually transparent about the mechanics
flash loan amplification turns a rounding error worth fractions of a cent into millions. the attack surface grows with TVL but the bugs stay the same size
the amplification math is what makes this scary. one wei rounding compounds into 9.6M when you borrow enough
The accumulator math section is solid. Most auditors miss these because they test with large amounts, not edge cases near zero.
zero-amount edge cases belong in every audit checklist by default. caught a similar issue on a yield vault last year that would have drained the pool
been adding fuzzing with min-size inputs to every audit since zkLend. caught two similar issues last quarter
Cairo auditing tooling being years behind Solidity is the actual bottleneck. you cant catch rounding bugs without proper fuzzing frameworks