Reproduced Exploit

Folks Finance — collateral value skips token decimals

DAI has 18 decimals and USDC has 6. The oracle price is 1 USD per whole token, and the value function never divides by 10decimals. One DAI is therefore worth 1e18. At a 40% collateral factor the borrow cap is 4e17.

May 2024•Ethereum•arithmetic•3 min read

Chain

Ethereum

Category

arithmetic

Date

May 2024

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: 47332-missing-decimal-correction-ottersec-none-folks-finance-x-cha. Standalone Foundry PoC and full write-up: 47332-missing-decimal-correction-ottersec-none-folks-finance-x-cha_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/arithmetic/decimal-mismatch · vuln/logic/price-calculation

Key info#

ProtocolFolks Finance x-chain
FindingOtterSec, May 2024, AuditVault 47332
Vulnerable codeprice is amount * price with no 10**decimals
Harm1 DAI borrows 4e11 USDC. The decimal-corrected cap is 0.4 USDC.
Chainlocal synthetic (audited Ethereum contracts)

TL;DR#

DAI has 18 decimals and USDC has 6. The oracle price is 1 USD per whole token, and the value function never divides by 10**decimals. One DAI is therefore worth 1e18. At a 40% collateral factor the borrow cap is 4e17.

That cap is compared to raw USDC units, so it authorizes 4e17 atomic USDC, which is 4e11 whole USDC. The report's identity: 4e11 * 1e6 = 0.4 * 1e18. The corrected cap is 0.4 * 1e6 = 4e5 atomic USDC, which is 0.4 USDC. The pool was holding exactly the buggy cap, and the borrow empties it.

The vulnerable code#

SOLIDITY
function valueOf(address, uint256 amount) public pure returns (uint256) {
    // @> VULN: amount is not divided by 10**decimals.
    return amount * PRICE;
}

borrowCapacity is that value times 40 / 100. The corrected capacity divides the DAI amount by 1e18, applies 40%, and scales back to 6 decimals.

Root cause#

Token amounts and USD values are added in different scales. The high-decimal collateral is treated as a huge USD balance, and the low-decimal debt is drawn in raw units against that balance.

Preconditions#

Collateral factor 40%. Price of both tokens is 1 USD. The pool holds at least 4e17 atomic USDC, which is the lenders' cash in this run. The borrower posts 1 DAI. No second block.

Attack walkthrough#

  1. The pool holds 4e17 atomic USDC, credited to the lender.
  2. Deposit 1e18 DAI.
  3. valueOf returns 1e18. Capacity is 4e17.
  4. The corrected capacity is 4e5 atomic USDC.
  5. Borrow 4e17. The pool's USDC balance is 0.
  6. The borrower holds 4e11 whole USDC against 1 DAI of collateral.

Diagrams#

flowchart TD A["1 DAI is priced as 1e18"] --> B["40 percent cap is 4e17"] B --> C["VULN: cap is not scaled by USDC decimals"] C --> D["Borrow 4e11 USDC"] D --> E["Lender cash falls to 0"] E --> F["Corrected cap is 0.4 USDC"]

Impact#

The borrower draws 4e11 USDC (4e17 atomic) against 1 DAI. The fair draw is 0.4 USDC (4e5 atomic). The lenders' cash in this pool is taken in full.

Remediation#

Divide the price product by 10**decimals for each token before comparing collateral and debt.

How to reproduce#

BASH
cd 47332-missing-decimal-correction-ottersec-none-folks-finance-x-cha_exp
forge test -vv

Sources#


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.