Storage Layout
DISCLAIMER // NFA // DYOR
This analysis is based on observations of the contract behavior. We are not smart contract security experts. This document aims to explain what the contract appears to do based on the code. It should not be considered a comprehensive security audit or financial advice. Always verify critical information independently and consult with blockchain security professionals for important decisions.
⊙ generated by robots | curated by humans
| METADATA | |
|---|---|
| Contract Address | 0xb276f62d...1eac1c (etherscan) |
| Network | Ethereum Mainnet |
| Analysis Date | 2026-08-11 |
| Snapshot Block | 25733839 |
Variables
Slots are assigned in declaration order from the verified source and every value below was read directly with cast storage at block 25733839. Solady's Ownable and ReentrancyGuard use fixed high slots rather than sequential ones, which is why the sequential layout begins at slot 0 with the VRF coordinator rather than with the owner.
| SLOT | VARIABLE NAME | TYPE | CURRENT VALUE | PURPOSE |
|---|---|---|---|---|
| 0 | s_vrfCoordinator |
IVRFCoordinatorV2Plus |
0xd7f86b4b...20893a (etherscan) |
Chainlink Verifiable Random Function (VRF) coordinator; the only address permitted to call rawFulfillRandomWords. Owner-replaceable while quiescent. |
| 1 | vrfSubId |
uint256 |
0xbe3fb16e...0ffd33 |
Subscription that pays fulfilment gas. |
| 2 | vrfKeyHash |
bytes32 |
0x8077df51...2bd3d9 |
Chainlink gas lane. |
| 3 | callbackGasLimit / requestConfirmations |
uint32 / uint16 (packed) |
900000 / 3 |
Gas budget for the callback; confirmation depth before fulfilment. |
| 4 | maxActivationsPerAcquisition |
uint256 |
6 |
Cap on staged listings drained or reserved per acquisition. |
| 5 | pendingAcquisitionCount |
uint256 |
0 |
Requests awaiting a VRF word. |
| 6 | unfulfilledVrfCount |
uint256 |
0 |
Outstanding callback liabilities against the subscription. |
| 7 | callbackObserved |
mapping(uint256 => bool) |
— | Ensures each request releases its callback liability exactly once. |
| 8 | unsettledAcquisitionCount |
uint256 |
0 |
The exit gate. Non-zero blocks withdrawals, re-pricing and config changes. |
| 9 | selectionTimeoutBlocks |
uint256 |
30 |
Blocks a request has to receive its word before it is skipped. |
| 10 | maxAcquisitionsPerTx |
uint256 |
5 |
Cap on acquireBatch size. |
| 11 | lastIssuedSequence / nextSequenceToProcess / acquisitionsEnabled / withdrawOnly / whitelistEnabled |
uint64 / uint64 / bool / bool / bool (packed) |
140481 / 140482 / true / false / true |
The settlement queue head and tail, plus the three mode flags. nextSequenceToProcess exceeding lastIssuedSequence means the queue is fully drained. |
| 12 | collectionWhitelisted |
mapping(address => bool) |
— | Deposit whitelist. 47 collections observed active. |
| 13 | whitelistManager |
address |
0x854352b2...5e17c3 (etherscan) |
FWAWhitelist; may edit the whitelist without being owner. |
| 14 | surchargeBps |
uint256 |
250 |
Purchaser premium over expected value. Lowered from 1000. |
| 15 | selectionSlippageBps |
uint256 |
1000 |
Default positive-drift tolerance snapshotted into each request. |
| 16 | settlementDiscountBps |
uint256 |
9000 |
Purchaser's ETH payout as a fraction of backing. Raised from 8500. |
| 17 | settlementWindow |
uint256 |
86400 |
Purchaser's exclusive resolution period, in seconds. |
| 18 | finalizeWindow |
uint256 |
604800 |
Delay before anyone may finalize, in seconds. |
| 19 | minBacking |
uint256 |
50000000000000000 |
Minimum deposit, 0.05 ETH. Raised from 0.01 ETH. |
| 20 | ownerAcquisitionFeeBps |
uint256 |
100 |
Protocol cut of each acquisition fee. Capped only at 10000. |
| 21 | ownerSettlementFeeBps |
uint256 |
100 |
Protocol cut of backing returned to a depositor. Hard-capped at 500. |
| 22 | retainedToProtocol |
bool |
true |
Whether the retained settlement penalty is protocol revenue or shared. |
| 23 | topListingShareBps |
uint256 |
100 |
Slice of each acquisition routed to the top-listing pot. Lowered from 500. |
| 24 | topThresholdBps |
uint256 |
1000 |
Margin by which a challenger must clear the incumbent top. |
| 25 | nextListingId |
uint256 |
149855 |
Monotonic id counter; 149,854 listings created to date. |
| 26 | listings |
mapping(uint256 => Listing) |
— | The listing store. |
| 27 | stuckNFTRecipient |
mapping(uint256 => address) |
— | Records who is owed an NFT whose delivery reverted. |
| 28 | stagingHead |
uint256 |
0 |
Oldest staged listing; 0 when the queue is empty. |
| 29 | stagingTail |
uint256 |
0 |
Newest staged listing. |
| 30 | stagingNext |
mapping(uint256 => uint256) |
— | Forward links of the staging FIFO. |
| 31 | stagingPrev |
mapping(uint256 => uint256) |
— | Backward links of the staging FIFO. |
| 32 | stagedCount |
uint256 |
0 |
Listings waiting outside the selection pool. |
| 33 | maxStagedListings |
uint256 |
0 |
Staging cap; 0 means unlimited. |
| 34 | reservedForSequence |
mapping(uint256 => uint64) |
— | Binds a staged listing to the sequence that will activate it. |
| 35 | reservedStagedCount |
uint256 |
0 |
Staged listings currently reserved. |
| 36 | reservedListingsBySequence |
mapping(uint64 => uint256[]) |
— | The exact activation batch committed per sequence. |
| 37 | slotToListing |
mapping(uint256 => uint256) |
— | Reverse index from tree slot to listing id. |
| 38 | activeListingCount |
uint256 |
5770 |
Listings in the selection pool. |
| 39 | totalWeight |
uint256 |
72357566815035813943552 |
Sum of all active weights; must equal tree[1]. |
| 40 | weightedBackingTotal |
uint256 |
5769999999999999999537847588424043513363 |
Sum of weight × value. Approximately activeListingCount × 1e36. |
| 41 | feeShareTotal |
uint256 |
5770 |
Sum of all active fee shares. Equals activeListingCount because each share is a flat 1. |
| 42 | accFeePerEV |
uint256 |
8026115607382734750819897645708918229739575285927178291 |
Dividend Accumulator, scaled by 1e36. Divided by SCALE this is 8.026115607 ETH of cumulative fees per unit share since deployment. |
| 43 | feeCredit |
mapping(address => uint256) |
— | Withdrawable earnings. 348 addresses hold a non-zero balance totalling 39.895179240 ETH. |
| 44 | accruedOwnerFees |
uint256 |
0 |
Protocol fees awaiting payoutFees. |
| 45 | payoutAddress |
address |
0x1C175b9F...373Bfe (etherscan) |
Splitter. Currently receives nothing because the token slice is 100%. |
| 46 | topListingId |
uint256 |
136185 |
The top-backed listing: a CryptoPunks 721 wrapper backed with 300 ETH. |
| 47 | topListingPot |
uint256 |
9365078075945921140 |
9.365078076 ETH accrued to the current top holder. |
| 48 | token |
address |
0xa0Df17B5...20C845 (etherscan) |
FWAToken; derived from the rewards module at wiring, never set directly. |
| 49 | rewards / acceptBidAsTokensEnabled |
IFWARewards / bool (packed) |
0x6a1a1C0C...92fc78 (etherscan) / true |
The rewards module and the switch allowing settlement in FWAToken. |
| 50 | acquisitionTokenSlice |
mapping(uint256 => uint256) |
— | Per-request reward slice registered at request time. |
| 51 | protocolFeeToTokenBps |
uint256 |
10000 |
Share of protocol fees routed to the FWAToken buyback reserve. Raised from 0 to 8000 then 10000. |
| 52 | tree |
mapping(uint256 => uint256) |
— | The depth-32 Segment Tree. Root at node 1; leaves from node 2³². |
| 53 | nextUnusedSlot |
uint256 |
9653 |
Next never-used tree slot. |
| 54 | freeSlotHead |
uint256 |
1318 |
Head of the LIFO free list of released slots. |
| 55 | nextFreeSlot |
mapping(uint256 => uint256) |
— | Free-list links. |
| 56 | acquisitions |
mapping(uint256 => Acquisition) |
— | Per-request purchaser, escrow, allocated listing and status. |
| 57 | acquisitionMeta |
mapping(uint256 => AcquisitionMeta) |
— | Per-request sequence, deadline, snapshotted tolerances and cached word. |
| 58 | requestIdAtSequence |
mapping(uint64 => uint256) |
— | Sequence-to-request index used by the ordered processor. |
| 59 | acquisitionRefundCredit |
mapping(address => uint256) |
— | Pull refunds owed to purchasers. |
| 60 | acquisitionRefundCreditTotal |
uint256 |
0 |
Aggregate of the above. |
| 61 | acquisitionEscrowTotal |
uint256 |
0 |
Acquisition fees escrowed against unresolved requests. |
Inherited Fixed Slots
| SLOT | SOURCE | PURPOSE |
|---|---|---|
0x8b78c6d8...000000 |
Solady Ownable |
Owner address. Stored at a constant slot rather than sequentially, which is why slot 0 is free for the coordinator. |
| Transient | Solady ReentrancyGuard |
The nonReentrant mutex, held in Transient Storage and cleared at transaction end. |
Struct Layouts
Listing occupies 11 sequential words per entry:
| OFFSET | FIELD | TYPE | NOTES |
|---|---|---|---|
| 0 | collection |
address |
The ERC-721 contract. |
| 1 | depositor |
address |
Who escrowed it and who receives the backing on a keep. |
| 2 | purchaser |
address |
Zero until allocated. |
| 3 | tokenId |
uint256 |
|
| 4 | weight |
uint256 |
1e36 / value. Inversely proportional to backing. |
| 5 | value |
uint256 |
Backing ETH, escrowed for this listing alone. |
| 6 | feeShare |
uint256 |
Always 1. The struct comment describes this as √backing, but the deployed code assigns a literal 1 in both _createListing and updateBacking. The square-root weighting exists in the separate rewards module, not here. |
| 7 | feeDebt |
uint256 |
Dividend checkpoint, ceiled on entry. |
| 8 | slot |
uint256 |
Tree slot; 0 when not active. |
| 9 | allocatedAt |
uint64 |
Timestamp gating the settlement and finalize windows. |
| 10 | status |
ListingStatus |
None, Active, Allocated, Withdrawn, Settled, Staged. |
Observed Status Distribution
Every listing id from 1 to 149,854 was read at block 25733839. The distribution reconciles exactly with nextListingId - 1 and with activeListingCount.
| STATUS | ENUM | COUNT | AGGREGATE BACKING |
|---|---|---|---|
| Active | 1 | 5,770 | 1246.219052680 ETH |
| Allocated | 2 | 56 | 3.788921919 ETH |
| Withdrawn | 3 | 9,314 | — (returned) |
| Settled | 4 | 134,714 | — (resolved) |
| Staged | 5 | 0 | 0 |
| Total | 149,854 |
Diagrams
graph TB
subgraph VRF["VRF Configuration — Slots 0-11"]
S0["Slot 0: s_vrfCoordinator<br/>0xd7f86b4b...20893a"]
S3["Slot 3: callbackGasLimit 900000<br/>requestConfirmations 3"]
S8["Slot 8: unsettledAcquisitionCount — 0<br/>THE EXIT GATE"]
S11["Slot 11: sequences 140481 / 140482<br/>enabled=true, withdrawOnly=false"]
end
subgraph Econ["Economic Parameters — Slots 14-24"]
S14["Slot 14: surchargeBps — 250"]
S16["Slot 16: settlementDiscountBps — 9000"]
S19["Slot 19: minBacking — 0.05 ETH"]
S20["Slot 20: ownerAcquisitionFeeBps — 100<br/>capped at 10000"]
S21["Slot 21: ownerSettlementFeeBps — 100<br/>hard-capped at 500"]
S23["Slot 23: topListingShareBps — 100"]
end
subgraph Pool["Pool Aggregates — Slots 38-42"]
S38["Slot 38: activeListingCount — 5770"]
S39["Slot 39: totalWeight<br/>7.2357e22 = tree root"]
S40["Slot 40: weightedBackingTotal<br/>5.77e39 ≈ count x 1e36"]
S41["Slot 41: feeShareTotal — 5770<br/>equals activeListingCount"]
S42["Slot 42: accFeePerEV<br/>8.026 ETH per share cumulative"]
end
subgraph Liab["Liability Counters — Slots 44-61"]
S44["Slot 44: accruedOwnerFees — 0"]
S47["Slot 47: topListingPot — 9.365 ETH"]
S60["Slot 60: acquisitionRefundCreditTotal — 0"]
S61["Slot 61: acquisitionEscrowTotal — 0"]
end
subgraph Struct["Structures — Slots 26-37, 52-59"]
S26["Slot 26: listings — 149,854 entries"]
S52["Slot 52: tree — depth 32 segment tree"]
S37["Slot 37: slotToListing — 5,770 occupied"]
S30["Slots 28-36: staging FIFO — empty"]
end
VRF --> Econ --> Pool --> Liab --> Struct
style S8 fill:#ffe1e1
style S20 fill:#ffe1e1
style S21 fill:#e1ffe1
style S39 fill:#e8f0ff
style S41 fill:#e8f0ff
The colouring marks the three slots that carry the most weight in the risk assessment. Slot 8 is the exit gate: while it is non-zero no depositor may withdraw or re-price, and no numeric config may change. Slot 20 is the unbounded side of the fee asymmetry — the owner may raise it to 10000 and capture the entire depositor fee stream. Slot 21 is the bounded side, hard-capped at 500, so the settlement cut cannot exceed 5% of backing.
graph LR
subgraph Tree["Segment Tree — slot 52"]
R["node 1<br/>root = totalWeight<br/>7.2357e22"]
L2["node 2"]
R2["node 3"]
LEAF["leaves from node 2^32<br/>slot i maps to node 2^32 + i - 1"]
end
subgraph Slots["Slot Allocation"]
NU["nextUnusedSlot — 9653"]
FH["freeSlotHead — 1318<br/>LIFO free list"]
OCC["5,770 occupied<br/>highest in use: 9651"]
end
R --> L2
R --> R2
L2 --> LEAF
R2 --> LEAF
LEAF --> OCC
FH -.reuse.-> OCC
NU -.extend.-> OCC
style R fill:#e8f0ff
Selection walks the tree from the root, comparing a target drawn as randomWord mod totalWeight against the cumulative weight of each left subtree, descending 32 times to land on a leaf. Because the tree is a mapping rather than an array, capacity for 2³² concurrent listings costs nothing until a slot is actually touched — at the snapshot only 9,652 leaves have ever been used, and 5,770 are occupied.