Methodology
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 |
Overview
The contract is verified on Etherscan, so this is a source-level analysis rather than a bytecode reconstruction. The work divided into two halves: reading what the code says it does, and checking against the chain whether it does it.
The first decision shaped everything after it. The protocol publishes source at token-works/fwa-relaunch, and it would have been natural to clone that and read from there. Diffing the repository against the Etherscan-verified source showed they are different programs — the repository is a single-commit snapshot from eight days before deployment, still carrying a Uniswap V4 integration that the deployed contract does not have. Every claim in this analysis is therefore taken from the Etherscan source. The repository was used only to understand design intent, and the divergence itself became a finding.
The second decision was to derive rather than trust. The contract exposes convenient aggregates — totalWeight, feeShareTotal, weightedBackingTotal, activeListingCount — and it would have been easy to report those as facts about the pool. Instead each was recomputed from primitive state: every one of the 149,854 listings was read individually, and the aggregates rebuilt from them. That is what makes the solvency reconciliation meaningful, and it is what surfaced the stale √backing comment, since the reconstructed feeShareTotal could only match if every share were a flat 1.
Reading with the code rather than the comments mattered more than usual here. Four separate NatSpec blocks describe the fee-share basis as the square root of backing; the code assigns a literal 1. Chain state agrees with the code.
Thought Process
mindmap
root((FWA Analysis))
Obtain
Etherscan getsourcecode
9 source files
Solidity 0.8.30, runs=1, prague
Runtime bytecode 21,425 bytes
Clone public repo
Diff against deployed
Divergence found
Repo used for intent only
Understand
Standalone, no proxy
92 functions
57 view, 35 state-changing
Segment tree depth 32
Staging FIFO
Sequence ledger
Dividend accumulator
Verify Pricing
Inverse weight NUM/value
EV = weightedBackingTotal/totalWeight
Recompute harmonic mean
from 5,770 backings
matches to the wei
Verify Solvency
Enumerate slots 1..16000
5,770 occupied
Read all 149,854 listings
status distribution
backing sums
Read pendingFees per listing
Read feeCredit per depositor
2,124 ever, 348 non-zero
Sum vs contract balance
surplus 157,011 wei
Governance
ConfigSet event history
43 events
21 constructor
22 owner actions
Storage slot dump
current vs defaults
Owner is deployer EOA
no multisig
no timelock
Risk
Fee asymmetry
settlement capped 5%
acquisition capped 100%
VRF coordinator replaceable
Exit gate vs owner timing
Unaudited complexity
Verification Guide
Analysis used Foundry's cast for chain reads, the Etherscan API v2 for source and event history, gh for repository inspection, and Multicall3 for bulk state enumeration. Every figure quoted in the analysis is reproducible with the commands below at block 25733839.
External Resources
- Etherscan — FWA contract — verified source, ABI, creation transaction, and constructor arguments. The authoritative artifact.
- Etherscan API v2 —
getsourcecodefor the source bundle and compiler settings,getcontractcreationfor deployer and creation block, andlogs/getLogsfor the completeConfigSethistory. - token-works/fwa-relaunch — public repository, read via
gh api. Used to establish design intent and, by diffing, to establish that it is not the deployed code. - Chainlink VRF v2.5 documentation — subscription semantics, callback billing behaviour, and the
requestRandomWordsinterface, needed to reason about the timeout and refund paths. - Solady —
Ownableuses a fixed storage slot rather than a sequential one, which is why the contract's own layout begins at slot 0. - Multicall3 —
0xcA11bde0...76CA11(etherscan), used to batch roughly 160,000eth_callreads into a few hundred requests. Direct JSON-RPC batching was rate-limited by the provider; Multicall3 was not.
Commandline Tools
Tip
Commands below use cast from the Foundry Toolkit. To run the commands below, you must set the RPC URL environment variable:
Establish Contract Identity and Provenance
Confirms the contract is standalone rather than a proxy, and identifies who deployed it and when.
# RUNTIME BYTECODE SIZE — CONFIRMS A REAL CONTRACT AND ITS EIP-170 HEADROOM
cast code 0xb276f62db0ce8ca2ca5bc522695be604521eac1c | wc -c
# CONSTRUCTOR ARGUMENTS — COORDINATOR, SUBSCRIPTION, KEY HASH, GAS LIMIT, VRF SERVICE
curl -s "https://api.etherscan.io/v2/api?chainid=1&module=contract&action=getsourcecode\
&address=0xb276f62db0ce8ca2ca5bc522695be604521eac1c&apikey=$ETHERSCAN_API_KEY"
# DEPLOYER AND CREATION BLOCK
curl -s "https://api.etherscan.io/v2/api?chainid=1&module=contract&action=getcontractcreation\
&contractaddresses=0xb276f62db0ce8ca2ca5bc522695be604521eac1c&apikey=$ETHERSCAN_API_KEY"
Verify the Pricing Model
The central mechanical claim is that inverse weighting makes the acquisition price the harmonic mean of all active backings. These reads establish the inputs; the reconstruction below establishes the identity.
# THE THREE POOL AGGREGATES THAT DRIVE PRICE
cast call 0xb276f62db0ce8ca2ca5bc522695be604521eac1c "totalWeight()(uint256)"
cast call 0xb276f62db0ce8ca2ca5bc522695be604521eac1c "weightedBackingTotal()(uint256)"
cast call 0xb276f62db0ce8ca2ca5bc522695be604521eac1c "activeListingCount()(uint256)"
# THE QUOTED PRICE, SPLIT INTO POOL FEE AND VRF SERVICE FEE
cast call 0xb276f62db0ce8ca2ca5bc522695be604521eac1c "quoteAcquisitionPrice()(uint256,uint256,uint256)"
# SEGMENT TREE ROOT MUST EQUAL totalWeight — A TREE-INTEGRITY CHECK
cast call 0xb276f62db0ce8ca2ca5bc522695be604521eac1c "treeRootWeight()(uint256)"
weightedBackingTotal / totalWeight gives the expected value. Because weight = 1e36 / value, that quotient is algebraically n / Σ(1/vᵢ) — the harmonic mean. We confirmed it numerically by computing the harmonic mean of all 5,770 backings independently and comparing: both give 0.079742869391249362 ETH.
Enumerate the Active Pool
The contract has no function returning the set of active listings. Occupied tree slots are the index: slotToListing[slot] is non-zero exactly for active listings, and slots are recycled through a free list, so probing a bounded range finds all of them.
# HOW FAR THE SLOT SPACE HAS EVER EXTENDED (STORAGE SLOT 53 IS nextUnusedSlot)
cast storage 0xb276f62db0ce8ca2ca5bc522695be604521eac1c 53 --block 25733839
# ONE SLOT LOOKUP; REPEAT ACROSS 1..16000 VIA MULTICALL3 TO FIND ALL 5,770
cast call 0xb276f62db0ce8ca2ca5bc522695be604521eac1c "slotToListing(uint256)(uint256)" 2503
# THE FULL LISTING STRUCT FOR ONE ID — HERE THE TOP-BACKED LISTING
cast call 0xb276f62db0ce8ca2ca5bc522695be604521eac1c \
"listings(uint256)(address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,uint64,uint8)" \
136185
Probing slots 1 to 16,000 returned exactly 5,770 occupied, matching activeListingCount, with the highest in use at 9,651 — comfortably inside the probe range, so the enumeration is complete rather than truncated.
Reconstruct Every Liability
Solvency was tested by rebuilding all eight obligation components from primitive state and comparing the total to the contract's balance. Three of the four counters below read zero at the snapshot; the top-listing pot did not.
# THE CONTRACT'S ACTUAL ETH
cast balance 0xb276f62db0ce8ca2ca5bc522695be604521eac1c --block 25733839
# THE FOUR AGGREGATE LIABILITY COUNTERS
cast call 0xb276f62db0ce8ca2ca5bc522695be604521eac1c "acquisitionEscrowTotal()(uint256)"
cast call 0xb276f62db0ce8ca2ca5bc522695be604521eac1c "acquisitionRefundCreditTotal()(uint256)"
cast call 0xb276f62db0ce8ca2ca5bc522695be604521eac1c "accruedOwnerFees()(uint256)"
cast call 0xb276f62db0ce8ca2ca5bc522695be604521eac1c "topListingPot()(uint256)"
# PER-LISTING UNCREDITED FEES; SUMMED ACROSS ALL 5,770 ACTIVE LISTINGS
cast call 0xb276f62db0ce8ca2ca5bc522695be604521eac1c "pendingFees(uint256)(uint256)" 136185
# PER-DEPOSITOR CREDITED EARNINGS; SUMMED ACROSS ALL 2,124 DEPOSITORS EVER
cast call 0xb276f62db0ce8ca2ca5bc522695be604521eac1c \
"feeCredit(address)(uint256)" 0xd092e74d7aba2084cfbf772d29e3d71300e200ec
Backing sums came from reading every listing id from 1 to 149,854 and partitioning by status. The depositor set for feeCredit was taken from the same sweep rather than from event logs, because feeCredit can only ever be credited to an address that deposited at least one listing, and the provider rate-limited wide eth_getLogs ranges.
Reconstruct the Governance History
ConfigSet is emitted by the constructor and by all three owner dispatchers, so the full parameter history is recoverable from one topic.
# THE EVENT TOPIC
cast keccak "ConfigSet(uint256,uint256)"
# EVERY CONFIG CHANGE SINCE DEPLOYMENT — 43 EVENTS
curl -s "https://api.etherscan.io/v2/api?chainid=1&module=logs&action=getLogs\
&address=0xb276f62db0ce8ca2ca5bc522695be604521eac1c&fromBlock=25546793&toBlock=99999999\
&topic0=0x150110afd46e9924086bf85c855aae25722518b293155bf0ae689dd99a2e88cc\
&page=1&offset=1000&apikey=$ETHERSCAN_API_KEY"
Key names were resolved by parsing the constants out of the verified FWAConfigKeys.sol, since the event carries the numeric key rather than a label.
Read Internal Parameters Not Exposed by Getters
Several economically significant parameters — surchargeBps, minBacking, protocolFeeToTokenBps, whitelistManager, and the three mode flags — are declared internal and have no public getter. They were read directly from storage, with slot positions derived from declaration order in the verified source and cross-checked against the public getters that share packed slots.
# surchargeBps (250), minBacking (0.05 ETH), protocolFeeToTokenBps (10000)
cast storage 0xb276f62db0ce8ca2ca5bc522695be604521eac1c 14 --block 25733839
cast storage 0xb276f62db0ce8ca2ca5bc522695be604521eac1c 19 --block 25733839
cast storage 0xb276f62db0ce8ca2ca5bc522695be604521eac1c 51 --block 25733839
# SLOT 11 PACKS BOTH SEQUENCE COUNTERS AND THE THREE MODE BOOLEANS
cast storage 0xb276f62db0ce8ca2ca5bc522695be604521eac1c 11 --block 25733839
Slot 11 decodes as lastIssuedSequence in bytes 0–7, nextSequenceToProcess in bytes 8–15, then acquisitionsEnabled, withdrawOnly and whitelistEnabled in bytes 16, 17 and 18. Both sequence values agree with their public getters, which confirms the offset derivation for the booleans that have none.
Confirm External Dependencies
# THE COORDINATOR IS CHAINLINK'S CANONICAL VRFCoordinatorV2_5, NOT A LOOK-ALIKE
curl -s "https://api.etherscan.io/v2/api?chainid=1&module=contract&action=getsourcecode\
&address=0xd7f86b4b8cae7d942340ff628f82735b7a20893a&apikey=$ETHERSCAN_API_KEY"
# SUBSCRIPTION BALANCE, REQUEST COUNT, OWNER AND CONSUMER LIST
cast call 0xd7f86b4b8cae7d942340ff628f82735b7a20893a \
"getSubscription(uint256)(uint96,uint96,uint64,address,address[])" \
0xbe3fb16e567de94add08d0270be5e987eab08ac5cddb073ca79c06e16e0ffd33
# THE REWARDS MODULE AND VRF SERVICE BOTH POINT BACK AT THIS POOL
cast call 0x6a1a1C0CfB3D3C538e13D36d608a5bcaa992fc78 "fwa()(address)"
cast call 0xa084c33Fb7a467307452898b8D58165ebd2E5D9f "fwa()(address)"
Diff the Deployed Source Against the Public Repository
# CLONE FOR LOCAL READS (NOT COMMITTED)
gh api repos/token-works/fwa-relaunch --jq '{created_at,pushed_at,license,default_branch}'
git clone --depth 50 https://github.com/token-works/fwa-relaunch.git
# COMPARE IMPORT SETS — THE FASTEST WAY TO SEE THE DIVERGENCE
grep -n '^import\|^contract ' fwa-relaunch/src/FWA.sol
grep -n '^import\|^contract ' <etherscan-extracted>/src__FWA.sol
The deployed file has 2,372 lines and no Uniswap V4 imports; the repository file has 2,756 and declares contract FWA is Ownable, ReentrancyGuard, IUnlockCallback.
Document Verification
The drafted documents were re-checked before publication in five independent passes, each with a narrow scope and each reading from primary sources rather than from the draft. Running them separately matters: a claim can be correctly formatted, non-redundant, neutral in tone, free of filler, and still be factually wrong, and a single reviewer holding all five concerns at once reliably drops the last one.
| DIMENSION | WHAT IT CHECKS | CHECKED AGAINST |
|---|---|---|
| Facts | Addresses, block heights, counts, dates, compiler version, selector values, storage slot contents, and every quantitative claim including the solvency reconciliation | Live chain state via cast and Multicall3 at block 25733839, Etherscan API v2, and the Etherscan-verified FWA.sol |
| Standards | Address display format, explorer links on every on-chain reference, table casing, section separators, icon semantics, glossary term wrapping, nav registration | CLAUDE.md, docs/glossary.md, mkdocs.yml |
| Redundancy | Duplicated tables and paragraphs across the six documents, and the same claim stated with different values in two places | The document set itself |
| Voice | Superlatives asserted without a verification clause, opinion adjectives, speculation about intent or users | CLAUDE.md writing standards |
| Filler | Rhetorical padding — meta-narration, telegraphed structure, formulaic emphasis | CLAUDE.md anti-patterns |
Every disagreement the fact pass raised was re-derived from chain or source before any edit was made, on the assumption that the checker is as capable of being wrong as the draft. A claim was only changed where the contradicting value could be reproduced from a command in the Verification Guide above.
Limitations
Scope
Only the FWA pool contract was analysed at source level. FWARewards, FWAToken, FWATokenHook, FWAVRFService, FWAWhitelist and Splitter were read only far enough to establish their addresses, ownership and the interfaces FWA depends on. Depositor and purchaser returns depend materially on the rewards module and the token, neither of which is analysed here.
Snapshot, not proof
The solvency reconciliation holds at block 25733839. It demonstrates that the books balanced at one block; it is not a proof that they balance in every reachable state. No formal verification, symbolic execution, or fuzzing was performed.
No dynamic testing
Nothing was simulated, forked, or executed. No transaction was traced end to end. Conclusions about control flow come from reading the source, and conclusions about state come from reading the chain.
The rounding argument is directional, not exhaustive
We observe that _activateListing ceils checkpoints while _pendingFees floors credits, and that the observed residue is positive and tiny. We did not enumerate every arithmetic path to prove the residue can never go negative.
Assumptions
The Etherscan source matches the deployed bytecode
Etherscan reports an exact-match verification. We did not independently recompile with Solidity 0.8.30 at runs = 1 with via-IR and compare bytecode hashes.
Slot enumeration is complete
Probing tree slots 1 to 16,000 returned exactly activeListingCount occupied entries with the highest at 9,651, and nextUnusedSlot reads 9,653. We take this as complete coverage rather than a truncated scan.
feeCredit holders are a subset of depositors
feeCredit is incremented in exactly three places — _settleAndRemove, updateBacking, and _vacateTop — and in all three the recipient is a listing's depositor. The depositor set swept from all 149,854 listings is therefore a superset of all possible credit holders.
Chainlink's coordinator behaves as documented
The analysis of timeout, refund and billing paths assumes the coordinator at the configured address is genuine Chainlink infrastructure behaving per its documentation. We verified the contract name on Etherscan but did not audit the coordinator.
Token Cost Breakdown
| PHASE | DESCRIPTION | TOKENS |
|---|---|---|
| Phase 0 | Obtain the Contract | 25 tok |
| Phase 1 | Discovery & Understanding | 40 tok |
| Phase 2 | Deep Dive Analysis | 95 tok |
| Phase 3 | Risk & Trust Analysis | 45 tok |
| Phase 4 | Documentation Generation | 95 tok |
| Phase 5 | Verification pass over the drafted documents | 1,025 tok |
| TOTAL | Complete Contract Analysis | 1.3 mtok |
Phase 5 costs more than the other five phases combined, which is expected for this contract. The verification passes do not re-read the draft; they re-derive it. That means a second full Multicall3 sweep of all 149,854 listings, a second feeCredit sweep across all 2,124 depositor addresses, recomputing keccak over all 92 function signatures, dumping all 62 storage slots against declaration order, and pulling the VrfServiceFeePaid log series to establish what purchasers actually paid rather than what a zero-gas-price eth_call reports. Reconstructing the pool twice is the cost of being able to say the numbers reconcile.
Note: Token costs are estimates based on typical conversation lengths and complexity. Actual consumption may vary by ±10-15% depending on API responses, iterative refinement, and verification steps.