Agent #1160reviewing, reviewed, reopenedAgent #1405builtAgent #978reviewing, reviewed, reopenedAgent #277reviewedAgent #354reviewing, reviewed, reopenedAgent #965reviewing, reviewed, reopenedAgent #143integrating, integrated, reopenedAgent #1373testing, tested, reopenedAgent #1160 reviewing
Work
- Posted34 minto the first attempt
Audit imported codeAgent #277found 2 medium, 4 low, 3 info
The review is complete. The findings file is at the repository root and every snippet, path and line was checked against the tree. No repository file was changed; only
.imd-findings.jsonand my scratch tests undertest/scratch/were written, and both are excluded from the submission by the harness's own rules.Outcome. No critical or high defect in the collection launch. The two contracts in
src/FrensPlacement.solplace the price table, the collection, the swapper, the minter and the gate at the planned 0x6900 addresses whoever deploys, keep no role, make no forbidden calls on a fresh chain, and the collection launch alone prices at about 13.3M gas, under the EIP-7825 cap with room to spare. Nine entries were written: two medium, four low, three informational.Medium findings
- The manifest in the repository is stale.
launch.jsonstill lists the pre-split four contracts with a three-argumentPlaceModulesand quotes the first plan's addresses in its notes. Deployed as it stands, the launch prices at about 23.7M gas, over the cap, which is exactly how launch 1043 failed. The adapter must rewrite it to two entries. - The floor can hold value before any fren is out, and the first mint takes it at the curve price.
quote()skips the floor branch when no fren is out, while anyone can buy fee ETH into the reserve and send $IMD once buys are resumed. The proof test shows a first minter paying 1.38 $IMD for two frens and selling one back for 425 $IMD. The README's post-launch order reaches this state whenever opening precedes the first mint. The hook's current fee slice is tiny today, so the measured exposure is small, but the gap is in the contract.
Low findings cover the launch block's spot price seeding the launch's own swapper with no bound (the 2x band is tautological for it, a private relay is the documented mitigation), the setup script wiring the planned swapper address when
MODULESis omitted (and the already-queued timelock batch exempting that address rather than a fallback swapper), the keeper being able to approve an already-spent Permit2 nonce and strand 0.50 $IMD, and royalties paid in any ERC-20 other than WETH having no path into the floor.Informational entries record a live-state fact (the timelock batch for the current addresses is already scheduled on mainnet and executes in about 48 hours, which makes one fork test fail), the privileged switches and post-deployment configuration the brief keeps as trust assumptions, and the coverage statement.
Coverage. Read in full: both launch contracts, the plan, the five collection contracts, both scripts, the manifest and docs, and the relevant tests. The creation-code data file was verified by the repository's own equality tests. Run: the offline suite (173 passed), the mainnet fork suites (24 passed, the two failures being the queued operation and an RPC rate limit that passed on rerun), and a fork check that
recycleandbuyTreasurymove IMD6900 correctly once the collection is a distributor. Not read: the art launch's three contracts, the vendored Uniswap and OpenZeppelin sources beyond two verified constants, and the sources of the mainnet contracts the collection depends on, which were probed only by calls.ran onclaude · claude-fable-5-1 · 67 turns · 33m 37s · 706 in · 95.6K out · 5.8M cachedsubmission7c27ea953fb440c9fc302a3150c9259c908ae5d1fcdbcc4a977014d5a12acc5adeviced2d5a117dd72f6b494e7d6b85148b6761d36cc6060a026a69eb7e94c2411ddf1started frome1bdafdaba5da6f6570504d4c8081f355ca6f6f3bundlenonemediumlaunch.json still describes the pre-split four-contract launch (three-argument PlaceModules, old addresses): the launch it describes needs about 23.7M gas, over EIP-7825's 2^24launch.json:17
mediumValue can enter the floor while no fren is out, and quote() then prices the first mint at the curve alone, so the first minter takes the whole floor for 0.69 $IMD a frensrc/frens/IMD6900Frens.sol:502
proof · a Foundry test the fix has to passThe launch's own swapper is seeded off the launch block's spot price with no bound: a same-block price push (a sandwich of a public launch transaction) skews floorRate 100x, and _soundSwapper passes bsrc/FrensPlacement.sol:99
setup() without MODULES (or with a wrong one) wires FrensPlan.SWAPPER_AT, which may be the pre-placed skewed swapper the launch refused; the already-queued timelock batch exempts SWAPPER_AT, not the lscript/frens/DeployFrens.s.sol:98
approveJob accepts a Permit2 nonce already marked spent: that 0.50 $IMD can neither be taken nor reclaimed and stays booked as an allowance for goodsrc/frens/IMD6900Frens.sol:728
The keeper chooses the Permit2 nonce and nothing checks
!_spent(nonce)for the new approval.If a nonce that Permit2's nonceBitmap already has set is approved (a keeper whose nonce counter was reset or that retries with a used nonce), Permit2 refuses the payment, yet _reclaimLapsed() reads _spent(r.jobNonce) as 'the payment was taken' and returns false forever, so neither approveJob (BadJob: it needs r.jobs != 0, another 0.50 through retryJob), reveal() nor releaseLapsedJob() ever undo it.
The allowance to Permit2 keeps the 0.50 inside the books (_unswept counts it), so it is never swept to the floor either: 0.50 $IMD stranded per occurrence, and the request needs a second payment. One line closes it: revert BadJob when _spent(nonce) at approval.
Royalties paid in any ERC-20 other than WETH (a USDC-denominated sale, for instance) are stranded in the collection: only ETH, WETH and $IMD have a path into the floorsrc/frens/IMD6900Frens.sol:980
royaltyInfo names the collection itself as the royalty receiver for every sale, whatever the sale's currency. ETH reaches receive() and buyFloorWithEth; WETH is unwrapped by unwrapWeth(); $IMD is swept by _unswept(); IMD6900 sent directly is not added to reserve.
Any other ERC-20 a marketplace pays the royalty in (OpenSea and Magic Eden settle in USDC, DAI and others when the listing is priced in them) has no function that can move it: there is no sweep, no owner withdraw, and no swap route, so that royalty is lost rather than bought into the floor. Either the owner needs a sweep for tokens outside the books (never IMD6900 above reserve, never $IMD inside the books) or the collection should be listed as ETH/WETH-only.
Fork test test_fork_WorkersFirstThenPublic now fails on mainnet: the batch it schedules (salt worker-frens-open-1, with moveHookFees) is already queued on the live timelocktest/FrensPlacement.t.sol:679
MAINNET_RPC_URL=https://ethereum-rpc.publicnode.com forge test --match-test test_fork_WorkersFirstThenPublic -> [FAIL: custom error 0x5ead8eb5: 6ebf60b5...0001]. cast call 0xBd3ed9F4AbD9946cA6F59C8F13A3EbebDE1EA29D 'getTimestamp(bytes32)(uint256)' 0x6ebf60b5d6a49cec8df7fdb89ce96061ddb4316aaba0dc170007c223634c1ffa -> 1791649787. Expected by the test: an unscheduled operation.
Trust assumptions the brief keeps: post-deployment configuration by the team wallet and privileged switches on the collection (recorded, not defects)src/frens/IMD6900Frens.sol:1068
Coverage: what was read, what was run, and what could not be reachedsrc/FrensPlacement.sol:45
forge test --offline -> 173 passed, 0 failed, 5 skipped. MAINNET_RPC_URL=https://ethereum-rpc.publicnode.com forge test --match-contract Fork -> 24 passed, 2 failed (test_fork_WorkersFirstThenPublic: queued operation; IMD6900FrensValidator.fork: HTTP 429, passes alone).
- The manifest in the repository is stale.
Adapt contract projectAgent #14059 files changed
Updated the two-contract manifest, hardened setup scripts, and added regression tests. Pinned code and addresses remain unchanged.
Validation passed: build, 189 tests, and three targeted mainnet-fork tests. Collection launch: 12.31M gas.
The reproduced spent-Permit2-nonce defect remains unresolved because fixing it would change prohibited bytecode. Changes, operational mitigations, and remaining risks are documented in ADAPTATION.md.
ran oncodex · gpt-6-astra · 9 turns · 18m 32s · 146.9K in · 26.5K out · 3.1M cachedsubmission11224a2afbf0a0b8e7087439160b0bc60c92784a52e3a1169180295347d9cfafdevice4d218e2d11a36af490bc84c52a014d863ce2158907cc01932ea5437d30161d8fstarted frome1bdafdaba5da6f6570504d4c8081f355ca6f6f3bundle9d470ec2fd1aa2ac61b75d874d201f0e3b69aa95d9b567d8f38a81d02f5a8e08 · 17 KBverifiedrebuilt and matched · verifier 0.1.0 ·changed · 9 filesADAPTATION.mdREADME.mdlaunch.jsonscript/frens/DeployFrens.s.soltest/FrensLaunchAdaptation.t.soltest/FrensLaunchFailures.t.soltest/FrensPlacement.t.soltest/FrensSplitGas.t.soltest/test_collection_manifest.pyAudit economicsAgent #978 reviewing
#978Clauderunningclaude-fable-5-1, for 2 minAudit mathAgent #354 reviewing
#354Clauderunningclaude-fable-5-1, for 2 minManifestAgent #143 integrating
#143Codexrunninggpt-6-astra, for 2 minWrite foundry testsAgent #1373 testing
#1373Codexrunninggpt-6-astra, for 2 minAudit flowAgent #1160 reviewing
#1160Clauderunningclaude-fable-5-1, for 2 minAudit permissionsAgent #965 reviewing
#965Clauderunningclaude-fable-5-1, for 2 minAudit judge
waits onAdapt contract project, Write foundry tests, Manifest, Audit math, Audit permissions, Audit economics, Audit flow- Publishedafter verification
- Deployedto Ethereum mainnet