Reproduced Exploit

NLX — non-refunded excess fee in `_setPricesFromPriceFeeds`

1. _setPricesFromPriceFeeds reads updateFee = pyth.getUpdateFee(...) and requires msg.value >= updateFee. 2. It calls pyth.updatePriceFeeds{value: updateFee}(...) — only the exact fee is forwarded. 3. Any surplus msg.value - updateFee stays on the oracle module; there is no refund path. 4. HARM in…

Jul 2024Otherfee3 min read

Chain

Other

Category

fee

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: 50881-non-refunded-excess-fee-in-setpricesfrompricefeed-function. Standalone Foundry PoC and full write-up: 50881-non-refunded-excess-fee-in-setpricesfrompricefeed-function_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/fee/missing-refund · vuln/oracle/pyth-update · genome: fee-calculation · direct-drain

Reproduction: a self-contained Foundry PoC that compiles & runs in an isolated project with only forge-std — no fork, no RPC, no anvil_state. Full trace: output.txt. PoC: test/50881-non-refunded-excess-fee-in-setpricesfrompricefeed-function_exp.sol.

AuditVault taxonomy: lang/solidity · platform/halborn · has/github · has/poc · severity/high · sector/oracle · genome: fee-calculation · direct-drain · pyth-oracle-completeness


Key info#

ImpactHIGH — any caller that overpays the Pyth update fee permanently loses the excess ETH; it is trapped on the oracle module
ProtocolNLX / CoreDAO — price-feed oracle module
Vulnerable code_setPricesFromPriceFeeds — forwards only updateFee to Pyth, never refunds msg.value - updateFee
Bug classMissing refund of excess native fee
FindingHalborn — CoreDAO NLX · #50881
Reporthalborn.com/audits/coredao/nlx
SourceAuditVault
StatusAudit finding — fixed by CoreDAO (refund added). Reproduced here as a standalone local PoC.
Compiler^0.8.24 (PoC)

TL;DR#

  1. _setPricesFromPriceFeeds reads updateFee = pyth.getUpdateFee(...) and requires msg.value >= updateFee.
  2. It calls pyth.updatePriceFeeds{value: updateFee}(...) — only the exact fee is forwarded.
  3. Any surplus msg.value - updateFee stays on the oracle module; there is no refund path.
  4. HARM in the PoC: caller sends 0.02 ETH, fee is 0.01 ETH → 0.01 ETH trapped on OracleModule.

The vulnerable code#

SOLIDITY
uint updateFee = pyth.getUpdateFee(pythUpdateData);
require(updateFee <= msg.value, "not enough funds to update price feeds");

pyth.updatePriceFeeds{value: updateFee}(pythUpdateData); // @> VULN: excess never refunded
// FIX: refund msg.value - updateFee to msg.sender

Root cause#

Fee collection and fee consumption are asymmetric: the module accepts an arbitrary overpay but only spends updateFee, with no return of the remainder.

Preconditions#

  • Caller can invoke the price-feed update path with native value.
  • Caller (or UI/integration) sends more than the exact Pyth fee.

Attack walkthrough#

  1. Attacker (or honest user) calls setPricesFromPriceFeeds{value: 0.02 ether}.
  2. Module forwards 0.01 ETH to Pyth; 0.01 ETH remains on the module.
  3. HARM: excess is unreimbursed and stuck.

Diagrams#

sequenceDiagram participant Caller participant Oracle as OracleModule participant Pyth as MockPyth Caller->>Oracle: setPricesFromPriceFeeds{value: 0.02 ETH} Oracle->>Pyth: getUpdateFee() Pyth-->>Oracle: 0.01 ETH Oracle->>Pyth: updatePriceFeeds{value: 0.01 ETH} Note over Oracle: excess 0.01 ETH trapped #59; no refund

Impact#

Users/keepers who overpay the Pyth fee permanently lose the surplus. At scale this is continuous value leakage from every overpaying update.

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.