Reproduced Exploit

Royco — fillLPOrder credits the whole requested amount as a fee

fillLPOrder assigns order.tokenAmountsRequested to the frontend fee, not feePercentage times the filled fraction. An attacker creates an LP order whose requested amount is the book's current reward balance, fills it, and sets frontendRecipient to themselves.

Aug 2024•Ethereum•logic•3 min read

Chain

Ethereum

Category

logic

Date

Aug 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: 46671-vulnerability-in-filllporder-allows-draining-of-reward-token. Standalone Foundry PoC and full write-up: 46671-vulnerability-in-filllporder-allows-draining-of-reward-token_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/logic/fee-calculation · vuln/logic/reward-calculation

Key info#

ProtocolRoyco
FindingCantina, August 2024, AuditVault 46671
Vulnerable codefillLPOrder credits tokenAmountsRequested to frontendRecipient
HarmThe caller claims 1000 reward tokens. The 1% fee on that order is 10.
Chainlocal synthetic (audited Ethereum contracts)

TL;DR#

fillLPOrder assigns order.tokenAmountsRequested to the frontend fee, not feePercentage times the filled fraction. An attacker creates an LP order whose requested amount is the book's current reward balance, fills it, and sets frontendRecipient to themselves.

The book holds 1000. The fee account is credited 1000 and claimFees transfers it. The market fee used here is 1% (100 bps), so the fair credit is 10. The extra 990 is the inventory.

The vulnerable code#

SOLIDITY
order.filled = true;
// @> VULN: frontendRecipient is credited the entire tokenAmountsRequested.
uint256 fee = order.tokenAmountRequested;
fees[frontendRecipient] += fee;

The fair fee on a full fill:

SOLIDITY
return requested * FEE_BPS / BPS; // 1000 * 100 / 10000 = 10

Root cause#

The frontend fee is the order's requested token amount. It is not scaled by the market fee or by how much of the order was filled. Pointing the recipient at the filler, and the requested amount at the book's balance, turns the fee ledger into a withdrawal of the inventory.

Preconditions#

The orderbook holds reward tokens, here 1000. The market fee is 1%. The attacker can create and fill their own LP order in one transaction. No second block.

Attack walkthrough#

  1. The book holds 1000 reward tokens.
  2. Create an LP order with tokenAmountsRequested = 1000.
  3. Fill it with this contract as frontendRecipient.
  4. The fee balance is 1000, not 10.
  5. claimFees transfers 1000. The book balance is 0.
  6. 1000 - 10 = 990 is above the fee schedule.

Control fairFee(1000) returns 10 and does not move the inventory.

Diagrams#

flowchart TD A["Book holds 1000 reward tokens"] --> B["LP order requests 1000"] B --> C["Fill with self as frontend recipient"] C --> D["VULN: fee credited is 1000"] D --> E["claimFees transfers 1000"] E --> F["Fair 1 percent fee is 10"]

Impact#

The caller receives all 1000 reward tokens the book was holding. The fee schedule owes them 10. The other 990 is reward inventory.

Remediation#

Credit tokenAmountsRequested * feePercentage * fillPercentage, not the raw requested amount.

How to reproduce#

BASH
cd 46671-vulnerability-in-filllporder-allows-draining-of-reward-token_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.