Reproduced Exploit

Funding is paid in vBase instead of UA

_getFundingPayments returns funding rate times the vBase position. For Alice's 1 vBase short and a 0.05 cumulative gap, that is 0.05 vBase. _settleUserFundingPayments passes that number to settlePnL with no indexPrice. The index is 20,000 UA per vBase, so she is owed 1,000 UA. She is minted 0.05 vB…

Sep 2022•Ethereum•logic•3 min read

Loss

Alice receives 0.05 vBase and 0 UA instead of 1,000 UA

Chain

Ethereum

Category

logic

Date

Sep 2022

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: 17366-funding-payments-are-made-in-the-wrong-token-trailofbits-inc. Standalone Foundry PoC and full write-up: 17366-funding-payments-are-made-in-the-wrong-token-trailofbits-inc_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/logic/incorrect-calculation · vuln/logic/accounting

Key info#

FieldValue
LossAlice receives 0.05 vBase and 0 UA instead of 1,000 UA
Vulnerable contractClearingHouse._settleUserFundingPayments
Attacker EOA0x1111111111111111111111111111111111111111
Attack contractExploit
Attack txExploit.run()
Chain / block / dateEthereum model · block 0 · 2022-09
Compilersolc 0.8.27, cancun, via IR
Bug classvBase funding notional is settled without the index conversion into UA

TL;DR#

_getFundingPayments returns funding rate times the vBase position. For Alice's 1 vBase short and a 0.05 cumulative gap, that is 0.05 vBase. _settleUserFundingPayments passes that number to settlePnL with no indexPrice. The index is 20,000 UA per vBase, so she is owed 1,000 UA. She is minted 0.05 vBase and her UA balance stays 0. The control multiplies by the index first and mints 1,000 UA, with no vBase.

The vulnerable code#

SOLIDITY
if (fundingPayments != 0) {
    // @> VULN: fundingPayments is vBase notional and is not multiplied by indexPrice
    vault.settlePnL(account, fundingPayments);
}

The figure comes from:

SOLIDITY
upcomingFundingPayment = upcomingFundingRate * vBaseAmountToSettle / 1e18;

upcomingFundingRate is unitless. vBaseAmountToSettle is vBase. The product is vBase. settlePnL is the UA ledger in the reviewed code. This reduction mints that unconverted figure as vBase, which is token A, and mints none of token B.

Root cause#

A funding rate times a vBase amount is not a UA amount. The index price is the missing factor. The clearing house never reads it before settlement, so the user's quote balance is wrong by that factor. Here the factor is 20,000.

Preconditions#

  • One market. Alice is short 1 vBase (1e18). Her stored cumulative funding is 0. The market cumulative is 0.05 (0.05e18).
  • Index price is 20,000 UA per vBase (20000e18).
  • Settlement runs on exit, which is the path the review describes.

Attack walkthrough#

  1. settle sums settleTrader (output.txt). The short is paid globalCum - userCum, which is 0.05, times the 1 vBase position. The product is 0.05 vBase.
  2. settlePnL is called with 50000000000000000 and no index multiplication.
  3. The vault mints 0.05 vBase to Alice. Her UA balance is 0.
  4. 0.05 * 20,000 = 1,000. The UA she should have received is 1000000000000000000000 base units.
  5. Control: the vault multiplies the same 0.05 vBase by the index and mints 1,000 UA. Her vBase balance stays 0.

Diagrams#

flowchart TD Short[Alice settles a 1 vBase short] --> Rate[Funding is 0.05 vBase at an index of 20000] Rate --> Raw[settlePnL is called with the raw 0.05 figure] Raw --> Wrong[Alice receives 0.05 vBase and 0 UA] Wrong --> Owed[That figure times the index is 1000 UA] Owed --> Fix[The control pays those 1000 UA]

Impact#

Alice receives 0.05 vBase (token A, 50000000000000000 base units) and 0 UA (token B). The index-converted payment is 1,000 UA. She is short 1,000 UA relative to the quote asset the funding payment is supposed to be denominated in. The control pays that 1,000 UA and no vBase.

Remediation#

Convert the vBase funding figure with indexPrice before settlePnL, and credit UA:

DIFF
-vault.settlePnL(account, fundingPayments);
+vault.settlePnL(account, fundingPayments * indexPrice / 1e18);

How to reproduce#

BASH
cd evm-hack-registry/17366-funding-payments-are-made-in-the-wrong-token-trailofbits-inc_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.