Reproduced Exploit

OUSD can transfer more than the displayed balance — rounding/precision loss

Rebasing credits can make the token-facing balance larger than the credit deduction for a transfer. Flooring creditsDeducted before validating the token amount lets a three-token transfer succeed when only two tokens are visible.

Jan 2021Ethereumarithmetic2 min read

Loss

A holder can transfer one token more than balanceOf reports

Chain

Ethereum

Category

arithmetic

Date

Jan 2021

Source

AuditVault

EVM Playground

Source-level debugger — step opcodes and Solidity in sync

evm-hack-analyzer

The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.

Loading fork state…

Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: 18213-ousd-allows-users-to-transfer-more-tokens-than-expected-trai. Standalone Foundry PoC and full write-up: 18213-ousd-allows-users-to-transfer-more-tokens-than-expected-trai_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/arithmetic/rounding · vuln/arithmetic/precision-loss · vuln/logic/missing-check

Reproduction: self-contained synthetic Foundry reduction; see output.txt.

Key info#

FieldValue
LossA holder can transfer one token more than balanceOf reports
Vulnerable contractOUSD._executeTransfer
Attacker EOA0x1111111111111111111111111111111111111111
Attack contractOUSD via Exploit
Attack txExploit.run()
Chain / block / dateEthereum model · block 0 · 2021-01
Compilersolc 0.8.24 (synthetic)
Bug classRounded credit deduction without a pre-balance check

TL;DR#

Rebasing credits can make the token-facing balance larger than the credit deduction for a transfer. Flooring creditsDeducted before validating the token amount lets a three-token transfer succeed when only two tokens are visible.

Background#

The AuditVault sequence changes supply and mints across users before exercising the transfer invariant. This local reduction uses the same credit/token rounding relationship without claiming a live exploit.

The vulnerable code#

SOLIDITY
uint256 creditsDeducted = value * creditsPerToken / 1e18; // @> floors first
creditBalances[msg.sender] -= creditsDeducted;

Root cause#

The implementation validates only the rounded credit amount, not the exact token balance that users and integrations observe.

Preconditions#

  • creditsPerToken is fractional due to rebasing.
  • The caller can transfer through the credit-based ERC-20 path.

Attack walkthrough#

  1. The synthetic state gives the attacker one credit at 0.5 credits/token, displaying two tokens.
  2. transfer sends three tokens while deducting one credit.
  3. The Proof event at output.txt:377 captures the over-balance transfer.

Diagrams#

flowchart TD A[Fractional creditsPerToken] --> B[balanceOf floors to 2] B --> C[transfer three tokens] C --> D[creditsDeducted floors to 1] D --> E[Invariant violated]

Remediation#

Check the exact token balance before credit arithmetic, use a rounding direction that cannot undercharge, and test transfer invariants with property-based fuzzing.

How to reproduce#

BASH
cd evm-hack-registry/18213-ousd-allows-users-to-transfer-more-tokens-than-expected-trai_exp
forge test -vvvvv

Sources#

Reference: https://github.com/trailofbits/publications/blob/master/reviews/OriginDollar.pdf


Sources & further analysis#

Reproductions & code

Alerts & third-party analyses

  • Web3Sec X hacked database: search.
  • Rekt leaderboard: search.
  • Solodit incident search: search.

These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.