Reproduced Exploit

Basin — Incorrectly assigned `decimal1` in `decodeWellData`

When well data encodes decimal1 = 0 (meaning “default to 18”), the decoder checks decimal0 == 0 instead of decimal1 == 0, so decimal1 stays 0. Scaling token1 reserves by 10^(18-0) massively overvalues token1 in calcLpTokenSupply and related swap math.

Jul 2024Otheruntagged2 min read

Chain

Other

Category

untagged

Date

Jul 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: 36914-h-02-incorrectly-assigned-decimal1-parameter-upon-decoding-c. Standalone Foundry PoC and full write-up: 36914-h-02-incorrectly-assigned-decimal1-parameter-upon-decoding-c_exp in the evm-hack-registry mirror.


Vulnerability classes: arithmetic/decimal-mismatch · token-decimal-normalization

Reproduction: self-contained Foundry PoC with only forge-std — no fork. Full trace: output.txt.


Key info#

ImpactHIGH — wrong token1 decimals → severe LP/price misvaluation across Stable2
ProtocolBasin — Stable2 well function
Vulnerable codeStable2.decodeWellData second zero-check uses decimal0
FindingCode4rena — Basin, 2024-07 · #36914 · [H-02] · reporter rare_one
Reportcode4rena.com/reports/2024-07-basin
SourceAuditVault
Compiler^0.8.24 (PoC)

TL;DR#

When well data encodes decimal1 = 0 (meaning “default to 18”), the decoder checks decimal0 == 0 instead of decimal1 == 0, so decimal1 stays 0. Scaling token1 reserves by 10^(18-0) massively overvalues token1 in calcLpTokenSupply and related swap math.

The vulnerable code#

SOLIDITY
if (decimal0 == 0) {
    decimal0 = 18;
}
if (decimal0 == 0) { // @> VULN: should check decimal1
    decimal1 = 18;
}

Root cause#

Copy-paste error on the second zero-default branch. Judge confirmed high severity when decimal0 is non-zero and decimal1 needs the default.

Diagrams#

flowchart TD A["well data (6, 0)"] --> B[decodeWellData] B --> C{"if decimal0 == 0?"} C -- no --> D["decimal1 stays 0"] D --> E["scaleTo18(1e18, 0) = 1e36"] E --> F["LP ~1e36 vs correct 2e18"]

Impact#

Incorrect decimals feed every Stable2 pricing path (calcLpTokenSupply, calcReserve, calcRate, …) — direct economic mispricing.

Sources#

Taxonomy: [[decimal-mismatch]] · [[token-decimal-normalization]] · severity/high · sector/dex


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.