Reproduced Exploit

NiftyApes — a second Seaport offer item locks 500 SIDE

_validateSaleOrder checks that the first offer item is WETH. It does not require offer.length == 1.

Jun 2024•Other•dos•3 min read

Chain

Other

Category

dos

Date

Jun 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: 60495-locked-funds-when-fulfilling-seaport-orders-quantstamp-nifty. Standalone Foundry PoC and full write-up: 60495-locked-funds-when-fulfilling-seaport-orders-quantstamp-nifty_exp in the evm-hack-registry mirror.


Vulnerability classes: vuln/dos/frozen-funds · vuln/input-validation/missing

Reproduction: a self-contained Foundry PoC that compiles and runs offline with only forge-std — no fork and no RPC. Trace: output.txt. PoC: test/60495-locked-funds-when-fulfilling-seaport-orders-quantstamp-nifty_exp.sol.


Key info#

ImpactHIGH — fulfilling a two-item offer locks 500 SIDE in seller financing
ProtocolNiftyApes SellerFinancing
Vulnerable codefulfillOrder pulls every offer item and forwards only WETH
Bug classNon-WETH offer tokens have no withdraw path
FindingQuantstamp (Michael Boyle) — NiftyApes seller financing · id 60495
ReportQuantstamp NiftyApes
SourceAuditVault finding
StatusFixed by requiring a single offer item. Reproduced here as a local synthetic.
Compilersolc 0.8.27, cancun, via IR

TL;DR#

_validateSaleOrder checks that the first offer item is WETH. It does not require offer.length == 1.

The seller fulfills an order whose offer is 1 WETH plus 500 SIDE. The contract pulls both. It forwards only the 1 WETH to the buyer. 500 SIDE remains in the contract, and there is no withdraw. The seller's SIDE balance is 0.

The control rejects offer.length != 1 before any pull. The seller keeps 500 SIDE.

The vulnerable code#

SOLIDITY
for (uint256 i; i < offer.length; i++) {
    // @> VULN: every offer item is pulled; only WETH is forwarded
    Token(offer[i].token).transferFrom(msg.sender, address(this), offer[i].amount);
}
weth.transfer(BUYER, wethOut);

Preconditions#

  • The seller holds 1 WETH and 500 SIDE and has approved the financing contract.
  • The order's first offer item is the WETH leg. A second item is SIDE.
  • The contract forwards only WETH.

Attack walkthrough#

  1. Build an offer of [WETH 1, SIDE 500].
  2. fulfillOrder validates the WETH leg and does not check the length.
  3. The loop pulls both tokens.
  4. 1 WETH is sent to the buyer.
  5. The financing contract still holds 500 SIDE.

Control: offer.length == 1 fails, and the seller still holds 500 SIDE.

Diagrams#

flowchart TD A["Offer is 1 WETH plus 500 SIDE"] --> B["Validation checks only the first item"] B --> C["Both tokens are pulled into seller financing"] C --> D["Only the 1 WETH is forwarded to the buyer"] D --> E["500 SIDE has no withdraw path"] E --> F["The seller balance of SIDE is 0"]

Impact#

The seller loses 500 SIDE. The buyer receives the 1 WETH they were quoted. The SIDE balance stays in seller financing.

Remediation#

In _validateSaleOrder, require order.parameters.offer.length == 1 so the only pulled token is the WETH leg.

Sources#

Taxonomy#

  • severity/high
  • vuln/dos/frozen-funds · vuln/input-validation/missing

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.