# Attack on rounding errors to get risk free profit

RubiconMarket.buy() rounds spend down, letting attackers repeatedly buy small quantities of low-decimal tokens for risk-free profit.

- Payout: Contest reward
- Severity: medium
- Program: Rubicon
- Credited: KingNFT
- Disclosed: 2025-02-17
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-04-rubicon-findings/issues/540
- Library page: https://bugbountylibrary.com/r/c4%3A2023-04-rubicon%3AM-23
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium technique:source-review vuln:accounting-error vuln:business-logic vuln:precision-loss

**Target:** RubiconMarket.buy()
**Root cause:** The spend calculation in buy() uses integer division that rounds down, so the buyer pays less than the exact proportional amount.
**Steps:**
1. Deploy RubiconMarket and mock tokens with low decimals (e.g., WBTC 8 decimals, GUSD 2 decimals).
2. Create offers selling USDC for WBTC/GUSD at a set price.
3. Attacker calls `market.buy(offerId, quantity)` repeatedly with small quantities (e.g., 599 for WBTC, 19999 for GUSD).
4. Due to rounding down, attacker receives more USDC than the proportional WBTC/GUSD spent.
5. Repeat many times to accumulate profit exceeding gas costs.
**How found:** The auditor noticed that the spend calculation in buy() uses division that rounds down, and hypothesized that with low-decimal tokens the rounding error could be exploited for profit.
**Exploitation:** Attacker repeatedly calls buy() with small quantities on offers involving low-decimal tokens, exploiting the rounding down to receive slightly more than fair value each time. By submitting many transactions, the small per-trade profit accumulates into a significant amount.
**Insight:** When reviewing order book or AMM contracts, check rounding directions in price calculations; tokens with low decimals (e.g., 2 or 8) amplify rounding errors, and repeated small trades can turn negligible per-trade gains into significant profit.
