{"assessments":[],"deployments":[],"fuzz":[],"identity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"interpretation":"Records acceptance and evidence. Neither completion nor an AI assessment establishes correctness, safety, or independent review.","jobId":"dc4e3a21-8e12-44c3-9a6d-ae180f5c8c60","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"4cf041cb4d34bd5b4a4f4680c21f4985275db48ec080fc6ddc71335b3f87716d","dependsOn":["audit_imported_code"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"a2cea02e8b33e91eeb784e0e5eaeb571cd62cd69e768d335d5834e24b3aa46e5","skillId":"adapt-contract-project","tools":[]},"key":"adapt_contract_project","kind":"code","role":"implement","skillHash":"a2cea02e8b33e91eeb784e0e5eaeb571cd62cd69e768d335d5834e24b3aa46e5","skillId":"adapt-contract-project","state":"accepted"},{"acceptedSubmissionHash":"64d6700817a0f0749e7883b8d1c814f1f885bf205a0a996b737df2b722628217","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"fd074076eaa9ed4ec998351d8006e8af51e0523f66a4781a3717e9589245b5d4","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"6ce01320ee438a4ce993d9fb19345711ffdbdcc017d2c0d766c41f07fdc6d1a0","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","tools":[]},"key":"audit_imported_code","kind":"code","role":"review","skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","state":"accepted"},{"acceptedSubmissionHash":"ea53859fc94fc2e66454b209fcae90cad0f3bfbd7b5083930fb837995e48d600","dependsOn":["adapt_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"13450f484f890353b466123c677f2765084d809ddbb6a5af4e4964a3361c47b0","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"daccfa80e548183143002ccf07775afce64590dc5c577a4dd97123f99458ca39","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"d4472861693d7be70004da417b876947a2ebf9c9b5600372afb962354b557d12","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"1a0862ff7b61c86e05c3f40211f5de1335ee3a9f4fcabf76b79e41731162ec27","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Deploy the Worker Frens collection (wFREN, 2222 on-chain pixel frens) on Ethereum, exactly as it is in the repository: the COLLECTION launch, two contracts from src/FrensPlacement.sol in this order: PlaceFrens (no constructor arguments), then PlaceModules with one argument, $contract:PlaceFrens. PlaceFrens creates the price table and the collection (IMD6900Frens, named Worker Frens); PlaceModules creates its swapper, ETH minter and workers' and WL gate, through the standard CREATE2 deployer 0x4e59b44847b379578588920cA78FbF26c0B4956C with the creation code in src/FrensCode.sol and the salts in src/FrensPlan.sol: the collection lands at 0x6900d042460d6bdbe68CE994F4dE36706797CCd4, the swapper at 0x69002297DD7980af0d24249f6a44E7046B1Cb1fb, the minter at 0x46B50a3061Ea692e075231c13bc653FdD1Bedd29 and the gate at 0xF83807Ec2E27e1771Fb2594139c1F6925Cfd9B8E whoever deploys. Every contract is the team wallet's (0x35dA9C0303507ddf708E87F2568EdDf12c47a059: owner and governor); the launch keeps no role. About 12.3M gas in one transaction (test_EachLaunchFitsOneTransaction). The art is already live (IMD launch 1067: WorkerFrensRenderer 0x0a2e5e0c1d00fe63ab4e391c052a023cc7a16292); the team wallet points the collection at it with script/frens/DeployFrens.s.sol setup(). This is the re-run of IMD job ea109756, which blocked on its judge's mandatory fixes (dc3bd30d: ETH waiting to be bought into the floor left out of the price; b7d35d29: unswept $IMD left out of the sale and treasury prices; plus the spent-nonce approveJob and the open()/resume() lock-out): all four are fixed in this commit, each proven by a test that fails on the old code (README, 'IMD's audit, fixed'), and the salts were re-mined. The requester's decision for this job: if the audit finds a HIGH or CRITICAL defect that needs src/frens/ changed, fix it, add a test, run 'forge build && python3 script/placement/gen.py' (it re-mines the 0x6900 salts) and report the new addresses; for MEDIUM, LOW or INFO findings, record them in ADAPTATION.md and do not change src/frens/, src/FrensCode.sol, src/FrensPlan.sol, the salts or foundry.toml (any changed byte moves the addresses the Ethereum timelock is set to). Mined salts must contain no f2/f4/ff byte (gen.py enforces it: a salt kept as raw data could trip the admission scan). Everything builds offline (lib/ is vendored); FrensPlacementForkTest and the fork tests need MAINNET_RPC_URL; the two validator tests that use transient storage need forge's --isolate with forge 1.4.","parentJobId":null,"planHash":"6fce21b3d524c7a9d7c5e5500575b29448711d8442bc7eaac5bfca5299e5cfcf","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"dc4e3a21-8e12-44c3-9a6d-ae180f5c8c60","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1095-placefrens-placemodules"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51740","feedbackHash":"308bc0fbea50a2f3fe427bc1bec5d9e8363e829a35da01cc1fbb16ded6617e72","nodeKey":"adapt_contract_project","submissionHash":"1d31793f341484238c8a4550e9806bbd6006da687ba57704a779aa3aa254e7ca","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51533","feedbackHash":"f26d8916ac59d9ead22c690bb9ab29e65b8ee09259e002adaa99ae8c378328d3","nodeKey":"adapt_contract_project","submissionHash":"4cf041cb4d34bd5b4a4f4680c21f4985275db48ec080fc6ddc71335b3f87716d","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51291","feedbackHash":"7f5135d34d4e7fd98a4582770d8cd14027535dc952f1e12a9b6b15516dc6be5b","nodeKey":"audit_economics","submissionHash":"64d6700817a0f0749e7883b8d1c814f1f885bf205a0a996b737df2b722628217","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51234","feedbackHash":"c70b4a66a71a00169783bbc3223bd552ee8efad56881b4de0c6c3e0692eb7b80","nodeKey":"audit_flow","submissionHash":"fd074076eaa9ed4ec998351d8006e8af51e0523f66a4781a3717e9589245b5d4","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52164","feedbackHash":"d0d93181831cc7bf6cad612fb0f32e70ca8d462131cfa45705fe0e3159b6dd76","nodeKey":"audit_imported_code","submissionHash":"6ce01320ee438a4ce993d9fb19345711ffdbdcc017d2c0d766c41f07fdc6d1a0","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52481","feedbackHash":"713df4400a035fb88f469dab5291376db7dffa05096961238362b996eb82f5eb","nodeKey":"audit_judge","submissionHash":"8633ef568b1449ef7982a460eb5eceed5eae10b052fa67b8565e7b42f870b303","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51445","feedbackHash":"bef0be18d3c67d6be7fa54a1759ccbcb5bc198ac58cdb4725e6a69daace7ebf0","nodeKey":"audit_judge","submissionHash":"ea53859fc94fc2e66454b209fcae90cad0f3bfbd7b5083930fb837995e48d600","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50907","feedbackHash":"8563bc37a1755270db6df3f9d4d6b2c33849b0db941fc79dc1ff3cf5278439dd","nodeKey":"audit_math","submissionHash":"13450f484f890353b466123c677f2765084d809ddbb6a5af4e4964a3361c47b0","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52162","feedbackHash":"8468d15e73c2164c1fc9a86685c13d8c2cecd8460e3e6e2c5021c69547d730e5","nodeKey":"audit_permissions","submissionHash":"daccfa80e548183143002ccf07775afce64590dc5c577a4dd97123f99458ca39","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51536","feedbackHash":"68679938c53ae3dfe61d708b7024b1dc1a33ef2a0ed1c8c737da3fd9f4cf02a9","nodeKey":"manifest","submissionHash":"f8fd26ea395ddcca7794e77c101f5b497e6431195b0fbf28ef515219aea217e7","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51728","feedbackHash":"6cd7eb3c835bab868d99867776cf8784af2586032792e2cd167764a8966a2e9c","nodeKey":"manifest","submissionHash":"d4472861693d7be70004da417b876947a2ebf9c9b5600372afb962354b557d12","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51160","feedbackHash":"8f08c201764d70511674aee9037f42d2056280cdb2c46ba1a2aa0aa2a03361b7","nodeKey":"write_foundry_tests","submissionHash":"1a0862ff7b61c86e05c3f40211f5de1335ee3a9f4fcabf76b79e41731162ec27","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51727","feedbackHash":"5a81951628c92b28041d4d1fe547222c91b6bc2227f7591fd1e150fa061c724b","nodeKey":"write_foundry_tests","submissionHash":"6a70a837efd1bdfeb9dc03664b8f59b1506226dba886905528b9cfe7d7b11d08","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"aff35e53bd61fb45e412fdb53bd46f5d9516812464c87a20b6889bcbce0bb4d7","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"5fd2de06561c50a4","findings":[{"citation":"resolved","description":"IMD6900Frens.quote() values the ETH and WETH waiting in the collection at POOL4's instantaneous spot (FrenSwapper.pendingImd, src/frens/FrenSwapper.sol:119: eth * imdPerEth() / 1e18, where imdPerEth is slot0's sqrtPrice squared). FrenMinter.mintWithEth reads that quote (line 75), then buys exactly `cost` $IMD on POOL4 with an exact-output swap whose sqrtPriceLimitX96 is MIN_SQRT_PRICE + 1 (line 158: no cap on how far it moves POOL4), and only then calls requestMintFor (line 79), which re-reads quote() at the price the minter's own swap just set and charges that. The $IMD bought for the first quote and not needed for the second is refunded (lines 80-81). Seam (boundary x precision x invariant): the invariant 'a mint never costs less than the floor it joins' is checked against a marginal spot price that the minting transaction itself moves, while the ETH it values is still 1 ETH. POOL4 is thin on mainnet (block 26150558: liquidity 4.149e20, about 23 ETH / 7,400 $IMD virtual reserves, 316.65 $IMD per ETH), so an ordinary large ETH mint moves it a lot, and a deliberate push is cheap because POOL4's 1% fee is the only round-trip cost. Nothing in the collection stops this: _lockedTierOf's Flash guard only checks that the PoolManager is locked when the tier is read, and the unlock has completed by then. This is the pricing defect ADAPTATION.md keeps as a99c13dac6ce (pending ETH at spot); what is new is (a) the collection's own ETH mint path performs the push as a side effect of a normal mint, with no separate pool access, and (b) the round trip is profitable on today's liquidity, which the earlier disposition said it had not validated.","line":75,"path":"src/frens/FrenMinter.sol","reproduction":"Mainnet fork at block 26150611-26150618 (MAINNET_RPC_URL), DeployFrens.deploy, setMintOpen(true), gate.openPublic(). State: 2 frens out (one holder minted 2 at the curve, floorImd 0.88 $IMD), 1 ETH sent to the collection (royalties/fees waiting). (A) Honest minter path: quote(10) = 1,589.985 $IMD (10 x (316.65 pending + 0.88)/2); quoteEth(10) = 6.4519 ETH. mintWithEth{value: 6.58 ETH}(10, 1589.985e18): ethSpent 6.4519 ETH, and 609.026 $IMD comes straight back to the minter because the swap left POOL4 at about 195 $IMD/ETH and the second quote() charged 980.96 $IMD instead of 1,589.985. The collection still holds exactly 1 ETH. The two existing holders' floor share falls from 317.99 $IMD to about 215 $IMD (quote(1) after = 98.05, 2 x 98.05 = 196.1 plus the IMD the pool returns to them when POOL4 recovers): a 30% loss funded by nothing the holders did. Expected: a quote taken after the ETH->$IMD purchase, or taken before it and enforced as the amount paid (maxPay == paid), so the pending ETH is never valued at a price the mint itself set. (B) Deliberate round trip in one transaction (attacker with 690 $IMD for tier 3 so count 69 is allowed): push POOL4 by buying $IMD with 9.5 ETH, no limit (imdPerEth 316.65 -> 160.98); requestMint(69) pays 5,582.147 $IMD where the honest quote(69) was 10,970.899; sell the $IMD back (imdPerEth restored to 315.35). Round-trip cost: 0.1622 ETH (fees + residual impact). After: per-fren floor 83.07 $IMD, so the attacker's 69 frens are worth 5,731.69 $IMD against 5,582.15 paid (+149.5 $IMD, about 0.47 ETH at spot; net about +0.31 ETH after the round trip), and the two existing frens fall from 318.00 to 166.14 $IMD (-151.9 $IMD, about 0.48 ETH). The gain scales with pending ETH x attacker share of the frens out and is paid by the existing holders; it is profitable from roughly 0.4 ETH pending while few frens are out (the bootstrap and window period, when fees can wait with buys paused). Expected: the holders' share is unchanged by a mint; actual: 48% of their pending-ETH value moved to the minter.","severity":"medium","snippet":"        uint256 cost = frens.quote(count);\n        if (cost > maxPay) revert TooPricey(cost);\n        ethSpent = abi.decode(poolManager.unlock(abi.encode(MINT, cost, msg.value)), (uint256));\n        SafeTransferLib.safeApprove(imd, address(frens), cost);\n        requestId = frens.requestMintFor(msg.sender, count, cost);","title":"mintWithEth quotes before its own unlimited POOL4 swap and charges after it: the pending ETH is valued at the marginal price the mint itself pushed, diluting holders (same root as a99c13dac6ce, cheape"},{"citation":"resolved","description":"FrenMinter.quoteFloor(imdIn) and quoteFloorEth(ethIn) are documented as 'what buyFloor would get' and 'what buyFloorWithEth would get' (lines 99, 104). They run the pair swap with an unbounded price limit (line 149) and the POOL4 leg likewise (line 138). The real buys go through FrenSwapper, which stops each leg at a price limit: the pair pool at pairMoveBips() = fee()/2 = 345 bips (the live hook's fee() returns 690) and POOL4 at 50 bips (FrenSwapper.sol:212, 249, 262). With the pair pool's current liquidity (4.706e22, about 113 $IMD / 19.5M IMD6900 virtual reserves) a buy stops after about 1.93 $IMD, so the view and the write diverge by more than an order of magnitude (numerical gap: queryX omits the term doX applies). Any caller that uses the quote as minOut makes the buy fail: in _buyFloor the swapper's Short() is caught and buyFloor reverts Cap(); buyFloorWithEth reverts with the swapper's Short(). The site's and the keeper's numbers are wrong by the same factor.","line":149,"path":"src/frens/FrenMinter.sol","reproduction":"Mainnet fork at block 26150558-26150611, DeployFrens.deploy. (1) quoteFloor(50e18) returns imd6900Out = 5,589,160.645 IMD6900. Put 50 $IMD in the collection (deal + floorImd = 50e18), next block buyFloor(0): reserve = 306,576.375 IMD6900, floorImd left 48.066 $IMD (1.934 spent), spotRate 173,151 -> 167,377 (exactly the 3.45% limit). buyFloor(5_589_160e18) reverts Cap(). (2) quoteFloorEth(0.25e18) returns 7,415,969.076 IMD6900. vm.deal(collection, 0.25 ETH), next block buyFloorWithEth(0.25e18, 0): reserve = 306,576.375 IMD6900, 0.0588 ETH spent, 16.456 $IMD left in floorImd, 0.1912 ETH left. buyFloorWithEth(0.25e18, 7_415_969e18) reverts Short(). Expected: a quote within a few percent of the write's result (or documented as the unlimited pool quote); actual: 18.2x and 24.2x.","severity":"low","snippet":"                    sqrtPriceLimitX96: imdIs0 ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1","title":"quoteFloor and quoteFloorEth swap without the swapper's price limit: on the live pair pool the quote is 18x (IMD) and 24x (ETH) what buyFloor/buyFloorWithEth actually get, so a keeper that passes the "},{"citation":"resolved","description":"The swap's price limit is derived from an external, mutable value with no lower bound. For fee() in {0, 1}, moveBips = 0 and _limit computes f = Math.sqrt(1e36) = 1e18 exactly, so the limit equals slot0's sqrtPrice; Uniswap v4's Pool.swap rejects a limit equal to the current price (PriceLimitAlreadyExceeded) in both directions. imdToImd6900 then reverts inside _buyFloor's try/catch, which returns false: mints keep going (quote still books floorImd at face), buyFloor reverts Cap() for everyone, and buyFloorWithEth reverts with the pool's error; fee ETH and floor $IMD stop being bought in until the governor wires a different swapper (pairMoveBips is not configurable). The same reading also carries the sandwich bound: the comment's 'half the fee' argument holds only while the hook's fee stays at least twice the configured move, which is true today (690 bips vs 345) but is the hook owner's to change. POOL4_MOVE_BIPS (50) is hard-coded against POOL4's current 1% fee for the same reason. Boundary of an external dependency; the hook is IMD's, not this collection's, so no unprivileged trigger: recorded as info.","line":170,"path":"src/frens/FrenSwapper.sol","reproduction":"Mock the pair hook so fee() returns 0 (or 1) and the PoolManager as v4 (or a fork with the hook's fee set to 0): FrenSwapper.pairMoveBips() = 0; with the pool at sqrtPrice p, _limit(key, false, 0) = p * 1e18 / 1e18 = p; poolManager.swap with sqrtPriceLimitX96 == p reverts PriceLimitAlreadyExceeded; frens.buyFloor(0) reverts Cap() although floorImd > 0 and the swapper is wired; frens.buyFloorWithEth(ethIn, 0) reverts. Expected: a bounded minimum move (or a revert that names the cause); actual: a silent halt of all floor buys.","severity":"info","snippet":"        return IPairFee(pairHook).fee() / 2;","title":"pairMoveBips() = fee()/2 has no floor: a pair-hook fee below 2 bips makes _limit() return the current price and every floor buy revert PriceLimitAlreadyExceeded, silently stopping the floor"},{"citation":"resolved","description":"Each floor buy moves the pair price by pairMoveBips (3.45%) and quote() then values the whole reserve at that pushed spot (reserve * 1e18 / floorRate, FrenSwapper.sol:125), since the slow average (moved by w/(64*FULL_BUY) per block, 0.06% per block at the live pool's 1.9 $IMD per buy) stays above it. Seam precision x invariant: the price used to enforce 'never below the floor' is one the floor's own trade produced, so the floor part of the mint price overstates the reserve's cost by the buy's own impact. The error is one-sided (mints dearer, never cheaper; recycle and buyTreasury pay and charge IMD6900 in kind), so no extraction; recorded for the pricing model and for the site's quotes.","line":146,"path":"src/frens/FrenSwapper.sol","reproduction":"Mainnet fork at block 26150618, DeployFrens.deploy, open mint: requestMint(69) at the curve, then five buyFloor(0) in five successive blocks. $IMD spent 12.11 (48.05 -> 35.93 waiting); reserve 1,763,839.07 IMD6900; spot 173,151 -> 141,268 (1.0345^-6), rateAverage 173,056, floorRate = 141,268; reserve valued at 12.486 $IMD, 3.1% above the 12.11 $IMD it cost. Expected: the reserve valued at what it cost or at an un-pushed price; actual: at the price its own buys set.","severity":"info","snippet":"        rate = spotRate();\n        if (rateAverage != 0 && rateAverage < rate) rate = rateAverage;","title":"floorRate marks the reserve at the pool's post-buy spot, which the floor's own 3.45% buy just set: right after each buy the reserve is valued about 3% above the $IMD paid for it (protective direction,"}],"hash":"13450f484f890353b466123c677f2765084d809ddbb6a5af4e4964a3361c47b0","nodeId":"53c00b78-e520-484c-a801-6fc593f0d33e","outcome":"completed","summary":"Taylor, the math audit is done. The findings file is written and validated, the scratch probes are removed, and no tracked file changed.\n\n**Result: no HIGH or CRITICAL defect in the math.** Four findings are in `.imd-findings.json`, one medium, one low, two info, all with concrete inputs. Every one of the 55 entry points has a coverage row, plus four invariant rows.\n\n**Findings**\n\n- **Medium, mintWithEth ordering (FrenMinter.sol:75).** The quote is read before the minter's own unlimited POOL4 swap and charged after it, so pending ETH is valued at the marginal price the mint itself pushed. On a mainnet fork with 2 frens out and 1 ETH waiting, a 10-fren ETH mint got 609 of 1,590 $IMD refunded and the existing holders lost about 30% of their floor share. A deliberate push-mint-sell round trip costs 0.16 ETH and moves about 0.48 ETH from holders to the minter. This is the same root as the retained finding a99c13dac6ce, but the path is cheaper and the profit is now quantified on live liquidity.\n- **Low, quoteFloor and quoteFloorEth (FrenMinter.sol:149).** They swap without the swapper's price limit. On the live pair pool a 50 $IMD buy actually spends 1.93 $IMD, so the quotes are 18x and 24x what buyFloor and buyFloorWithEth deliver. Passing the quote as minOut always fails.\n- **Info, pairMoveBips has no floor (FrenSwapper.sol:170).** A hook fee below 2 bips makes the swap limit equal the current price, v4 rejects it, and every floor buy stops silently.\n- **Info, reserve marked at the post-buy spot (FrenSwapper.sol:146).** Right after each floor buy the reserve is valued about 3% above what it cost. The error only makes mints dearer.\n\n**What I checked and found sound:** the price table (2,222 entries, minimum 0.6901 so the job deduction never underflows, total 5,422.35 $IMD), all rate conversions and FullMath paths, the rounding directions in quote, recycle and buyTreasury, the pepe reservation arithmetic, the job-payment books, the swap delta casts, and the mint-then-recycle round trip. The floorRate zero-division edge needs roughly 4.7e31 $IMD and is unreachable.\n\n**Limits:** the fork numbers come from the public RPC at blocks 26150558 to 26150618. The WorkerFrensRenderer is already live and was not reviewed. No Slither or Mythril ran.","treeHash":null,"usage":{"cachedInputTokens":3749376,"inputTokens":738,"model":"claude-fable-5-1","outputTokens":89336,"runtime":"claude","turns":41,"wallClockMs":1490175}},{"artifacts":[],"attempt":1,"bundleHash":"25c59b64694b21418cf738464c8a8497f52fc16fc4bc98d435a8eb5b8f3f65c1","device":"4f64b8f2c68244fd","findings":[],"hash":"1a0862ff7b61c86e05c3f40211f5de1335ee3a9f4fcabf76b79e41731162ec27","nodeId":"b2102b17-4275-4b3a-a819-3b4fa7551d6b","outcome":"completed","summary":"Strengthened collection invariants and added five tests covering last-fren protection, treasury payment failures, unswept IMD, and nonce boundaries.\n\n`forge build` passed. `forge test`: **233 passed, 0 failed, 5 fork suites skipped** without an RPC URL. Invariants exercised 1,920 calls; nonce fuzzing ran 1,000 cases.\n\nOnly `test/FrensPlacedInvariants.t.sol` changed.","treeHash":"e2194f587b788eff9c7e2b9e8bc1216740bdc733","usage":{"cachedInputTokens":6864640,"inputTokens":147527,"model":"gpt-6-astra","outputTokens":17608,"runtime":"codex","turns":11,"wallClockMs":681613}},{"artifacts":[],"attempt":1,"bundleHash":"c9bcfd9c28d75aa0578cfdf86a45fbc1f689000b0030d5dea74a051441e69976","device":"4ece7e789ed37442","findings":[],"hash":"1d31793f341484238c8a4550e9806bbd6006da687ba57704a779aa3aa254e7ca","nodeId":"734ebfe7-7e48-479d-9fe5-d1ae23bf0f7c","outcome":"completed","summary":"Corrected stale launch addresses and notes, added audit regression tests, and documented all six findings in [ADAPTATION.md](/root/.identitymd-1975/work/dc4e3a21-8e12-44c3-9a6d-ae180f5c8c60/734ebfe7-7e48-479d-9fe5-d1ae23bf0f7c/ADAPTATION.md).\n\nProduction contracts, salts and addresses remain unchanged. Four reproduced MEDIUM/LOW runtime findings remain documented per your decision.\n\nValidation: build passed; **196 tests passed, 5 RPC-dependent suites skipped**; all four Python checks passed. Collection gas measured **12.45M**, within the limit. No transaction was broadcast.","treeHash":"633f0f9abe666e1e3b2b0990e7f5e5401a5e5507","usage":{"cachedInputTokens":2231040,"inputTokens":124429,"model":"gpt-6-astra","outputTokens":17574,"runtime":"codex","turns":6,"wallClockMs":510154}},{"artifacts":[],"attempt":1,"bundleHash":"151d2a8078a120b2a3fb5f730ccc86a9491e57f6ce9053b018f098095d89bba9","device":"9eee291846dcb046","findings":[],"hash":"4cf041cb4d34bd5b4a4f4680c21f4985275db48ec080fc6ddc71335b3f87716d","nodeId":"734ebfe7-7e48-479d-9fe5-d1ae23bf0f7c","outcome":"completed","summary":"Added reproduction tests, updated ADAPTATION.md, and answered all 15 findings in `.imd-responses.json`.\n\nConfirmed runtime findings remain documented under your MEDIUM/LOW/INFO rule. Contracts, salts and addresses are unchanged.\n\nValidation: `forge build` passed; `forge test` reported **209 passed, 0 failed, 5 skipped**. All four manifest checks passed.","treeHash":"206fc78e6c7b16667ff66114ceb55a4dcd444506","usage":{"cachedInputTokens":2416768,"inputTokens":88159,"model":"gpt-6.1-sol","outputTokens":13433,"runtime":"codex","turns":9,"wallClockMs":509402}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"bd7adba3a8045853","findings":[{"citation":"resolved","description":"The NatSpec at src/frens/IMD6900Frens.sol:800 promises 'recycle pays exactly this, revealed or not', but recycle() (line 902) and buyTreasury() (line 921) call _sweep() before reading floorPerFren(), which books the unswept $IMD (balance minus floorImd, jobBudget and the Permit2 allowance) into floorImd. The public view does not. Unswept $IMD exists routinely: IMD refunds half of every 0.50 job payment straight to the contract (README, fork test test_RefundsJoinTheFloor), and while floor buys are paused (setup() sets maxImdPerBuy = 0) nothing else sweeps it, since buyFloor() reverts Cap when imdIn == 0 and undoes its own sweep. Consequences: (1) buyTreasury() price = 2 x (view + extra/out), so a buyer who sets maxImd from the view gets a Cap revert after every job refund; (2) any integration (site, marketplace floor display, quote() of the minter) reading floorPerFren() under-reports what a recycle pays by extra/out. No funds are lost: the write path always pays more than the view says. This is the view/write divergence the Invariant guide lists; the fix that keeps the design is to add the unswept extra to imdPart in the view (it already is counted in quote() via _unswept()).","line":805,"path":"src/frens/IMD6900Frens.sol","reproduction":"Offline, with the mocks in test/frens/IMD6900Frens.t.sol (MockSwapper rate 70000, flat 0.69 price table): alice requestMint(3) (tokens 1..3); alice recycle(1) so out = 2; imd.mint(frens, 0.25e18) (a job refund arrives). Now floorPerFren() returns (f, fi) with fi = 0 (the mint's own buy swept everything into the reserve). bob calls buyTreasury(1, 2*f, 2*fi) -> reverts Cap, because the write sweeps 0.25e18 first and charges imdPaid = 2 * 0.125e18. alice calls recycle(2) -> receives imdPaid = 0.125e18 although the view said 0. Expected: floorPerFren() equals what recycle pays, as its NatSpec states. Actual: the view is 0.125e18 per fren short (extra / out). Verified in a scratch Foundry test (test_FloorPerFrenViewDivergesFromWrite): view imd part 0, recycle imd part 125000000000000000.","severity":"low","snippet":"        (imd6900Part, imdPart) = (reserve / out, floorImd / out);","title":"floorPerFren() view omits unswept $IMD that recycle() and buyTreasury() settle, so quotes built from it revert or under-report"},{"citation":"resolved","description":"quote() charges a mint exactly its share of the floor (count * value / out, line 500) whenever that exceeds the curve. Of that payment JOB_PRICE goes to the job budget and only paid - JOB_PRICE joins the floor (line 553). After the mint the per-fren floor is (value + paid - 0.5) / (out + count) = value/out - 0.5/(out+count): every fren, the new ones and the existing ones alike, loses 0.5/(out+count). The minter therefore bears only count/(out+count) of the job price and the existing holders bear out/(out+count) of it. With 100 frens out a mint-plus-recycle costs the caller about 0.005 $IMD (plus gas) while draining about 0.495 $IMD from the other holders, and it commissions a 0.50 $IMD five-agent job each time (IMD refunds 0.25 of it to the floor later). The README acknowledges the minter only loses 'their share of the job', so this is the design, not a bug: the caller cannot profit, and the per-cycle drain is bounded by 0.5 $IMD. Recorded so the requester can decide whether the job price should instead be charged on top of the floor share in the floor-bound regime (quote = atFloor + JOB_PRICE when atFloor > price), which would make every mint pay its own job without touching the curve.","line":553,"path":"src/frens/IMD6900Frens.sol","reproduction":"Offline, with the mocks in test/frens/IMD6900Frens.t.sol: alice requestMint(69) then requestMint(31) (100 frens out, tier 3 by $IMD); vm.deal(frens, 0.25 ether); next block buyFloorWithEth(0.25 ether, 0) (MockSwapper: 0.25 ETH -> 750 $IMD worth of IMD6900), so the floor is 8.18 $IMD a fren, above the 0.69 curve. bob requestMint(1) pays quote(1) = 8.18e18; next block bob recycle(101). Expected (if each mint paid its own job): bob down 0.5 $IMD, alice's frens unchanged. Actual: bob is down 4950495049504951 wei (0.5e18/101), alice's 100 frens are down 495049504950495100 wei in total (per-fren floor 8.18e18 -> 8175049504950495049). Verified in a scratch Foundry test (test_JobPriceIsSocialisedInTheFloorRegime).","severity":"info","snippet":"        floorImd += paid - JOB_PRICE;","title":"In the floor-bound regime the 0.50 $IMD job price of a mint is paid almost entirely by existing holders, so mint-and-recycle cycles are nearly free for the caller"}],"hash":"64d6700817a0f0749e7883b8d1c814f1f885bf205a0a996b737df2b722628217","nodeId":"5ee918c2-d36c-4531-9554-339112549b85","outcome":"completed","summary":"The review is complete. The findings file is written and validated, the scratch test is removed, and the tree is otherwise untouched.\n\n**Outcome.** No HIGH or CRITICAL defect in the economics. The four fixes from job ea109756 hold under re-tracing, and the four retained MEDIUM/LOW findings in ADAPTATION.md are still the only material price exposures. I found one new LOW and one INFO, both reproduced with a Foundry scratch test before reporting.\n\n**Findings recorded in `.imd-findings.json`:**\n- **LOW, view/write divergence.** The public `floorPerFren()` view omits the unswept $IMD that `recycle` and `buyTreasury` sweep in first, although its NatSpec says recycle pays exactly it. Unswept $IMD is routine: IMD refunds half of every job payment to the contract, and nothing sweeps it while buys are paused. A treasury buyer who sets `maxImd` from the view gets a Cap revert after any refund, and any front end under-reports the recycle payout. In the scratch run the view said 0 where recycle paid 0.125 $IMD. No funds are lost; the write path always pays more than the view states.\n- **INFO, socialised job price.** In the floor-bound regime a mint pays exactly its floor share, of which 0.50 goes to the job, so every fren, old and new, loses 0.5/(out+count). With 100 frens out, a stranger's mint-and-recycle costs the caller 0.005 $IMD and the existing holders 0.495 $IMD, and commissions a 0.50 $IMD job each cycle. The README acknowledges the minter only loses \"their share of the job\", so this is the design; it is recorded with numbers for the requester's decision, bounded at 0.5 $IMD a cycle and never profitable for the caller.\n\n**Coverage.** All 55 listed entry points have a row, plus rows for the three money invariants I traced (books, mint never below the floor, reserve leaves only via recycle), the launch's roles and arguments, and one `unreached` row for the mainnet-only dependencies: the pair hook's `fee()`, the 400k floor-buy gas budget, the POOL4 hook's behaviour on exact-output swaps, and the real $IMD token decrementing Permit2's allowance. Those are covered by the five fork suites, which skipped here for want of `MAINNET_RPC_URL`. The offline suite passed as a baseline, with 196 tests and 5 skipped.\n\n**Already-retained items I deliberately did not re-report:** pending ETH valued at POOL4 spot, dust pinning the swapper average, duplicate pending Permit2 nonces, and reserve valued at zero when the governor unwires the swapper. I re-traced each and found no cheaper or unprivileged path to them.","treeHash":null,"usage":{"cachedInputTokens":1948419,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":64663,"runtime":"claude","turns":32,"wallClockMs":1315653}},{"artifacts":[],"attempt":1,"bundleHash":"fc4e55f77c6329b14266e96eef48631799076e07a9488f4072a25a03cd6d9501","device":"6e1819675bb7567a","findings":[],"hash":"6a70a837efd1bdfeb9dc03664b8f59b1506226dba886905528b9cfe7d7b11d08","nodeId":"b2102b17-4275-4b3a-a819-3b4fa7551d6b","outcome":"completed","summary":"Added offline failure-path tests and four-wallet invariants covering ETH payments, refunds, reentrancy, worker credits, and supply limits. Only `test/` changed.\n\nValidation passed:\n\n- `forge build`\n- `forge test`: 215 passed, 0 failed, 5 skipped\n- Invariants: 16,384 calls, no unexpected reverts\n\nFork suites remain unverified without `MAINNET_RPC_URL`.","treeHash":"9b3800b65e42bb0802b3684315d299a8163daa8d","usage":{"cachedInputTokens":1498240,"inputTokens":123472,"model":"gpt-6-astra","outputTokens":17260,"runtime":"codex","turns":7,"wallClockMs":474926}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"63458a1bd2b2d376","findings":[{"citation":"resolved","description":"The fix for IMD's High finding dc3bd30d counts the ETH and WETH waiting to be bought into the floor in quote() (IMD6900Frens.sol:499, via _floorValue -> FrenSwapper.floorValue -> pendingImd) and in buyTreasury() (IMD6900Frens.sol:925). pendingImd prices that ETH at imdPerEth(), the raw sqrtPriceX96 of POOL4 (ETH/$IMD) in the current block, with no averaging, no bound and no sanity band. The reserve part is protected (floorRate = min(spot, slow rateAverage)); the ETH part is not. Any caller can move POOL4 within the block before the mint (a swap in a prior transaction of the same block, or atomically through a router before calling requestMint: the Flash check only refuses mints while the PoolManager is unlocked, not after a completed swap in the same transaction). FrenMinter.mintWithEth even does it by construction: it buys the mint's $IMD on POOL4 (zeroForOne, lowering $IMD per ETH) and only then calls requestMintFor, which re-quotes at the lowered pendingImd. Lowering $IMD per ETH lowers quote() and the buyTreasury price; the attacker's fren then owns a full share of the ETH once it is bought in (buyFloorWithEth, by anyone) and recycle() pays it out of the reserve. The exposure is largest exactly when the launch runbook creates it: README step 1-4 pause the floor's buys (setParams(_,0,0)) until the timelock batch lands, while open() may already start the workers'/WL window, so royalties and fees accumulate as un-buyable ETH priced only by POOL4 spot. Cost to the attacker: the POOL4 fee (1%) and hook burn on the push and its reversal; gain: their share (count / (out + count)) of the under-counted pending ETH, so it pays whenever pending ETH is a few ETH and POOL4 is thin or the attacker already has a large $IMD/ETH position to move. Fix candidates (FrenSwapper only, the frens unchanged): value pending ETH at a slow average of imdPerEth seeded like rateAverage (or at the dearer of spot and that average, as floorRate does for IMD6900), or refuse a mint whose POOL4 spot is outside a band of the average.","line":119,"path":"src/frens/FrenSwapper.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IMD6900Frens} from \"src/frens/IMD6900Frens.sol\";\nimport {FrenSwapper} from \"src/frens/FrenSwapper.sol\";\nimport {FrenPrices} from \"src/frens/FrenPrices.sol\";\n\n/// @dev A plain ERC-20 (no Permit2 magic allowance)\ncontract PlainToken {\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    function mint(address to, uint256 a) external {\n        balanceOf[to] += a;\n    }\n\n    function approve(address s, uint256 a) external returns (bool) {\n        allowance[msg.sender][s] = a;\n        return true;\n    }\n\n    function transfer(address to, uint256 a) external returns (bool) {\n        balanceOf[msg.sender] -= a;\n        balanceOf[to] += a;\n        return true;\n    }\n\n    function transferFrom(address f, address to, uint256 a) external returns (bool) {\n        if (allowance[f][msg.sender] != type(uint256).max) allowance[f][msg.sender] -= a;\n        balanceOf[f] -= a;\n        balanceOf[to] += a;\n        return true;\n    }\n}\n\n/// @dev Uniswap v4's PoolManager as the swapper reads it: slot0 of each pool through extsload (StateLibrary), and the\n///      unlock flag through exttload. No swaps: a floor buy simply fails and the $IMD waits (as the frens allow).\ncontract Slot0Stub {\n    mapping(bytes32 => bytes32) internal slots;\n\n    function set(bytes32 slot, uint160 sqrtPriceX96) external {\n        slots[slot] = bytes32(uint256(sqrtPriceX96));\n    }\n\n    function extsload(bytes32 slot) external view returns (bytes32) {\n        return slots[slot];\n    }\n\n    function exttload(bytes32) external pure returns (bytes32) {\n        return bytes32(0);\n    }\n}\n\n/// @notice The pending ETH (royalties, fees) counts in the mint price at POOL4's spot price of the moment, read through\n///         FrenSwapper.pendingImd -> imdPerEth. Unlike the reserve part (floorRate = min(spot, slow average)), that part\n///         has no average and no bound: whoever moves POOL4 in the same block (a swap before the mint, or FrenMinter's\n///         own ETH -> $IMD swap) mints below the floor the fren joins, and the holders' pending fees are diluted.\ncontract PendingEthSpotTest is Test {\n    address constant POOL_MANAGER = 0x000000000004444c5dc75cB358380D2e3dE08A90; // the frens' hardcoded flash check\n    uint256 constant Q96 = 1 << 96;\n\n    PlainToken imd;\n    PlainToken imd6900;\n    PlainToken identity;\n    IMD6900Frens frens;\n    FrenSwapper swapper;\n    Slot0Stub pool;\n    address pairHook = makeAddr(\"pair hook\");\n    address pool4Hook = makeAddr(\"pool4 hook\");\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n\n    function setUp() public {\n        imd = new PlainToken();\n        imd6900 = new PlainToken();\n        identity = new PlainToken();\n        vm.etch(POOL_MANAGER, address(new Slot0Stub()).code);\n        pool = Slot0Stub(POOL_MANAGER);\n        // the pair pool: about 70,225 IMD6900 per $IMD; POOL4: 225 $IMD per ETH\n        pool.set(_slot(_pairId()), uint160(Q96 / 265));\n        pool.set(_slot(_pool4Id()), uint160(Q96 * 15));\n\n        frens = new IMD6900Frens(\n            address(this),\n            address(imd),\n            address(imd6900),\n            address(identity),\n            makeAddr(\"permit2\"),\n            makeAddr(\"x402\"),\n            makeAddr(\"payTo\"),\n            makeAddr(\"keeper\"),\n            makeAddr(\"relayer\"),\n            address(new FrenPrices())\n        );\n        swapper = new FrenSwapper(POOL_MANAGER, address(imd), address(imd6900), address(frens), pairHook, pool4Hook);\n        assertEq(swapper.rateAverage(), swapper.spotRate(), \"seeded at the pool's price\");\n        _rules();\n        frens.sealTraits();\n        frens.setModules(address(swapper), address(0));\n        frens.setMintOpen(true);\n        for (uint256 i; i < 2; ++i) {\n            address u = [alice, bob][i];\n            imd.mint(u, 1_000e18);\n            vm.prank(u);\n            imd.approve(address(frens), type(uint256).max);\n        }\n    }\n\n    function test_PendingEthIsPricedAtPool4SpotOfTheMoment() public {\n        vm.prank(alice);\n        frens.requestMint(2, type(uint256).max); // two frens out, 0.8805 $IMD waiting in the floor\n        assertEq(frens.totalMinted() - frens.inTreasury(), 2);\n        vm.deal(address(frens), 1 ether); // royalties waiting to be bought in: 225 $IMD at POOL4's price\n        assertEq(swapper.pendingImd(address(frens)), 225e18);\n        uint256 honest = frens.quote(1);\n        assertApproxEqAbs(honest, (frens.floorImd() + 225e18) / 2, 2, \"a mint pays its share of the waiting ETH\");\n\n        // POOL4 pushed in this block (ETH sold for $IMD, or bought with it: the direction that lowers $IMD per ETH),\n        // as a swap right before the mint does, or FrenMinter's own exact-output buy of the mint's $IMD\n        pool.set(_slot(_pool4Id()), uint160(Q96 * 3 / 2)); // 2.25 $IMD per ETH\n        assertEq(swapper.pendingImd(address(frens)), 2.25e18);\n        uint256 pushed = frens.quote(1);\n        uint256 before = imd.balanceOf(bob);\n        vm.prank(bob);\n        frens.requestMint(1, type(uint256).max);\n        uint256 paid = before - imd.balanceOf(bob);\n        assertEq(paid, pushed);\n        pool.set(_slot(_pool4Id()), uint160(Q96 * 15)); // the pool is back\n\n        // bob's fren now owns a third of the floor that alice's two frens owned, for a ~1.5 $IMD mint\n        uint256 fair = frens.quote(1);\n        emit log_named_uint(\"honest mint price before the push ($IMD, 1e18)\", honest);\n        emit log_named_uint(\"what bob paid at the pushed POOL4 price\", paid);\n        emit log_named_uint(\"fair floor share of bob's fren after the push\", fair);\n        assertGe(paid, honest, \"a mint never costs less than the floor it joins: broken by a POOL4 spot push\");\n    }\n\n    /* ── helpers ────────────────────────────────────────────────── */\n\n    function _pairId() internal view returns (bytes32) {\n        (address a, address b) =\n            address(imd6900) < address(imd) ? (address(imd6900), address(imd)) : (address(imd), address(imd6900));\n        return keccak256(abi.encode(a, b, uint24(0), int24(60), pairHook));\n    }\n\n    function _pool4Id() internal view returns (bytes32) {\n        return keccak256(abi.encode(address(0), address(imd), uint24(10_000), int24(60), pool4Hook));\n    }\n\n    /// @dev StateLibrary: slot0 lives at keccak256(poolId . POOLS_SLOT(6))\n    function _slot(bytes32 poolId) internal pure returns (bytes32) {\n        return keccak256(abi.encodePacked(poolId, bytes32(uint256(6))));\n    }\n\n    function _rules() internal {\n        uint8[8] memory n = [3, 13, 4, 3, 6, 3, 12, 16];\n        for (uint8 t; t < 8; ++t) {\n            uint16[] memory caps = new uint16[](n[t]);\n            uint8[] memory tiers = new uint8[](n[t]);\n            for (uint8 v; v < n[t]; ++v) {\n                caps[v] = t == 0 ? (v == 0 ? 2222 : 0) : 2222;\n            }\n            frens.setTraitRules(t, caps, tiers);\n        }\n    }\n}","reproduction":"State: 2 frens out (alice minted 2 at the table's 0.6901 + 0.6904; floorImd = 0.8805 $IMD after the 0.50 job), the frens hold 1 ETH of royalties, POOL4 at 225 $IMD per ETH (sqrtPriceX96 = 15 * 2^96), the pair pool at ~70,225 IMD6900 per $IMD (2^96 / 265). quote(1) = (0.8805 + 225) / 2 = 112.94 $IMD. Then POOL4's sqrtPriceX96 is set to 1.5 * 2^96 (2.25 $IMD per ETH, what a large ETH->$IMD swap in the same block does): pendingImd = 2.25 $IMD, quote(1) = (0.8805 + 2.25) / 2 = 1.565 $IMD. bob calls requestMint(1, max) and pays 1.565 $IMD (expected: never less than 112.94, the floor his fren joins). After the price returns, quote(1) = 75.65 $IMD: bob's fren holds a third of the 225 $IMD of pending royalties for 1.57 paid, and alice's two frens fell from 112.94 to 75.65 each. Run: forge test --match-path test/scratch/PendingEthSpot.t.sol (fails: 1565250000000000000 < 112940250000000000000).","severity":"medium","snippet":"        return eth == 0 ? 0 : FullMath.mulDiv(eth, imdPerEth(), 1e18);","title":"Pending ETH in the floor price is valued at POOL4's instantaneous spot: a same-block push mints below the floor the fren joins"},{"citation":"resolved","description":"_average samples only the first buy of a block, whatever its size, and returns for every later one. The size weighting (w = min(imdIn, FULL_BUY)) stops dust from steering the average, but not from occupying the block's single sample. Anyone can send dust ETH to the frens (receive()) and call buyFloorWithEth(1e12, 0) at the top of each block (the ETH route has its own one-a-block gate, so this also takes that block's fee-ETH buy): the average moves by w/(64*FULL_BUY) of nothing, and the mint's or arb bot's full 50 $IMD buy that follows in the block does not move it at all. Repeated each block for gas only, the average never converges: a swapper whose seed was accepted within 2x at the launch (PlaceModules._soundSwapper) stays 2x off, and after a market move the floor keeps being valued at a stale rate (floorRate = min(spot, average)) for as long as the griefer pays gas. Fix candidate (FrenSwapper only): sample the largest buy of the block (keep the block's sampled weight and let a bigger buy replace the sample), or weight by the block's cumulative $IMD volume.","line":155,"path":"src/frens/FrenSwapper.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {FrenSwapper} from \"src/frens/FrenSwapper.sol\";\n\n/// @dev The swapper with its averaging open (no pool manager: nothing seeded, the first buy sets it)\ncontract AverageProbe is FrenSwapper {\n    constructor() FrenSwapper(address(0xdead), address(1), address(2), address(3), address(4), address(5)) {}\n\n    function average(uint256 imdIn, uint256 out) external {\n        _average(imdIn, out);\n    }\n}\n\n/// @notice _average samples only the first buy of a block (averagedAt == block.number returns). A dust buy at the top\n///         of the block (anyone: send 1e12 wei to the frens and call buyFloorWithEth(1e12, 0), or a 1-wei-of-$IMD floor\n///         buy) takes that sample and moves the average by next to nothing; the real full buy that follows in the same\n///         block moves it not at all. Repeated every block, the slow average never converges: it is pinned wherever it\n///         was (a skewed seed stays skewed; a stale average keeps mints priced off the market) at the cost of gas.\ncontract AveragePinTest is Test {\n    AverageProbe s;\n    uint256 constant RATE = 70_000e18;\n\n    function setUp() public {\n        s = new AverageProbe();\n        s.average(1e18, 70_000e18);\n        assertEq(s.rateAverage(), RATE);\n    }\n\n    function test_DustBuyFirstInTheBlockPinsTheAverage() public {\n        uint256 full = s.FULL_BUY();\n        for (uint256 i; i < 100; ++i) {\n            vm.roll(vm.getBlockNumber() + 1);\n            s.average(1e12, 1e12 * 2 * RATE / 1e18); // a dust buy first: the block's only sample\n            s.average(full, full * 2 * RATE / 1e18); // then the floor's full buy at a rate twice the average: ignored\n        }\n        // a hundred full buys a block at 2x should have moved the average most of the way (see test_DustMovesItNextToNothing)\n        assertGt(s.rateAverage(), 2 * RATE - RATE / 2, \"100 blocks of full buys at 2x move the average most of the way\");\n    }\n}","reproduction":"AverageProbe (FrenSwapper with no pool manager, so the first buy seeds the average at 70,000e18). For 100 blocks: _average(1e12, 2x rate) first, then _average(50e18, 2x rate) in the same block. Expected (test_DustMovesItNextToNothing shows 100 full buys at 2.6x move the average past 2x): rateAverage > 105,000e18. Actual: rateAverage = 70,000.0000021e18, moved by 2e-8 of the way. Run: forge test --match-path test/scratch/AveragePin.t.sol (fails: 70000002187499966162064 <= 105000000000000000000000).","severity":"low","snippet":"        if (imdIn == 0 || out == 0 || averagedAt == block.number) return;","title":"A dust buy at the top of every block pins the swapper's slow average: the floor's real buy in the same block is never sampled"},{"citation":"resolved","description":"The audit's Low fix (_spent(nonce)) reads Permit2's bitmap, which only knows nonces already consumed. A nonce approved for request A and not yet taken is still unspent there, so approveJob(B, sameNonce, sameDeadline) is accepted: both requests write the same permit digest to approvedDigest and the allowance to Permit2 grows to 1.0 $IMD. Permit2 can take one payment (nonce spent); the other request's approval then reads as spent too (r.jobApproved && _spent(r.jobNonce)), so neither _reclaimLapsed nor releaseLapsedJob ever undoes it: its 0.50 stays approved to Permit2 forever, _unswept books it as a payment Permit2 may take, and it never reaches the floor (the same stranding the spent-nonce fix was for). The shared digest also stays approved (isValidSignature) after both requests reveal. The README's keeper discipline ('never reuse a nonce already assigned to another pending job') is the only guard, as the earlier fix relied on the keeper before. Fix candidate: keep a mapping of nonces in use (set in approveJob, cleared in _unapprove) and refuse them alongside _spent(nonce).","line":715,"path":"src/frens/IMD6900Frens.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IMD6900Frens, IFrenSwapper} from \"src/frens/IMD6900Frens.sol\";\nimport {FrenPrices} from \"src/frens/FrenPrices.sol\";\n\ncontract PlainToken {\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    function mint(address to, uint256 a) external {\n        balanceOf[to] += a;\n    }\n\n    function approve(address s, uint256 a) external returns (bool) {\n        allowance[msg.sender][s] = a;\n        return true;\n    }\n\n    function transfer(address to, uint256 a) external returns (bool) {\n        balanceOf[msg.sender] -= a;\n        balanceOf[to] += a;\n        return true;\n    }\n\n    function transferFrom(address f, address to, uint256 a) external returns (bool) {\n        if (allowance[f][msg.sender] != type(uint256).max) allowance[f][msg.sender] -= a;\n        balanceOf[f] -= a;\n        balanceOf[to] += a;\n        return true;\n    }\n}\n\n/// @dev Permit2 as the frens see it: the nonce bitmap, and a settlement that spends a nonce and pulls the payment\ncontract Permit2Stub {\n    mapping(address => mapping(uint256 => uint256)) public nonceBitmap;\n\n    function DOMAIN_SEPARATOR() external pure returns (bytes32) {\n        return keccak256(\"permit2\");\n    }\n\n    /// @dev IMD takes one approved payment: the nonce is spent and JOB_PRICE moves through the allowance\n    function settle(PlainToken token, address owner, uint256 nonce, address to) external {\n        require(nonceBitmap[owner][nonce >> 8] & (1 << (nonce & 0xff)) == 0, \"nonce used\");\n        nonceBitmap[owner][nonce >> 8] |= 1 << (nonce & 0xff);\n        token.transferFrom(owner, to, 0.5e18);\n    }\n}\n\n/// @notice approveJob refuses a nonce Permit2 already spent (the audit's fix) but not a nonce another request's pending,\n///         unspent approval already uses. Two requests approved over the same nonce and deadline share one digest: IMD\n///         can take one payment, the other is then \"spent\" too, and its 0.50 $IMD stays in the Permit2 allowance for\n///         good: the books count it as a payment Permit2 may take, so it never reaches the floor.\ncontract DuplicateNonceTest is Test {\n    PlainToken imd;\n    PlainToken imd6900;\n    Permit2Stub permit2;\n    IMD6900Frens frens;\n    address keeper = makeAddr(\"keeper\");\n    address payTo = makeAddr(\"imdPayTo\");\n    uint256 relayerKey = 0xA11CE;\n    address alice = makeAddr(\"alice\");\n\n    function setUp() public {\n        imd = new PlainToken();\n        imd6900 = new PlainToken();\n        permit2 = new Permit2Stub();\n        frens = new IMD6900Frens(\n            address(this),\n            address(imd),\n            address(imd6900),\n            address(new PlainToken()),\n            address(permit2),\n            makeAddr(\"x402\"),\n            payTo,\n            keeper,\n            vm.addr(relayerKey),\n            address(new FrenPrices())\n        );\n        _rules();\n        frens.sealTraits();\n        frens.setMintOpen(true);\n        imd.mint(alice, 100e18);\n        vm.prank(alice);\n        imd.approve(address(frens), type(uint256).max);\n    }\n\n    function test_TwoPendingApprovalsOverOneNonceStrandAJobPayment() public {\n        vm.prank(alice);\n        uint256 a = frens.requestMint(1, type(uint256).max);\n        vm.prank(alice);\n        uint256 b = frens.requestMint(1, type(uint256).max);\n        uint256 deadline = block.timestamp + 600;\n        IMD6900Frens.Quote memory q =\n            IMD6900Frens.Quote(\"r\", bytes32(\"s\"), \"q\", bytes32(\"qh\"), bytes32(\"ph\"), \"job.open\", deadline);\n        vm.prank(keeper);\n        (bytes32 da,) = frens.approveJob(a, 5, deadline, q);\n        vm.prank(keeper);\n        (bytes32 db,) = frens.approveJob(b, 5, deadline, q); // accepted: nonce 5 is pending, not spent\n        assertEq(da, db, \"one digest for two payments\");\n        assertEq(imd.allowance(address(frens), address(permit2)), 1e18, \"two payments' worth approved\");\n\n        permit2.settle(imd, address(frens), 5, payTo); // IMD takes one of them: nonce 5 is spent\n        assertEq(imd.balanceOf(payTo), 0.5e18, \"IMD was paid once\");\n        assertEq(imd.allowance(address(frens), address(permit2)), 0.5e18);\n\n        // both requests reveal; the lapsed path can't undo b's approval: its nonce reads as spent\n        vm.warp(deadline + 1);\n        _reveal(a, 1 << 2); // a pepe with face 1\n        _reveal(b, 2 << 2); // a pepe with face 2\n        assertEq(frens.jobBudget(), 0);\n        vm.expectRevert(IMD6900Frens.BadJob.selector);\n        frens.releaseLapsedJob(b);\n        assertEq(frens.isValidSignature(da, \"\"), bytes4(0x1626ba7e), \"the shared digest is still approved\");\n        // the 0.50 of the payment IMD never took stays approved to Permit2, and the books keep it out of the floor\n        assertEq(\n            imd.balanceOf(address(frens)),\n            frens.floorImd() + frens.jobBudget() + imd.allowance(address(frens), address(permit2)),\n            \"the books add up only with the stranded allowance\"\n        );\n        assertEq(imd.allowance(address(frens), address(permit2)), 0, \"nothing stays approved after both reveals\");\n    }\n\n    function _reveal(uint256 id, uint24 combo) internal {\n        uint24[] memory c = new uint24[](1);\n        c[0] = combo;\n        uint256 d = block.timestamp + 1 hours;\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(relayerKey, frens.voucherDigest(id, c, \"job\", keccak256(\"o\"), d));\n        frens.reveal(id, c, \"job\", keccak256(\"o\"), d, abi.encodePacked(r, s, v), 1);\n    }\n\n    function _rules() internal {\n        uint8[8] memory n = [3, 13, 4, 3, 6, 3, 12, 16];\n        for (uint8 t; t < 8; ++t) {\n            uint16[] memory caps = new uint16[](n[t]);\n            uint8[] memory tiers = new uint8[](n[t]);\n            for (uint8 v; v < n[t]; ++v) {\n                caps[v] = t == 0 ? (v == 0 ? 2222 : 0) : 2222;\n            }\n            frens.setTraitRules(t, caps, tiers);\n        }\n    }\n}","reproduction":"alice mints requests a and b (0.50 job each). keeper: approveJob(a, 5, D, q) then approveJob(b, 5, D, q): accepted, same digest, allowance(frens, permit2) = 1.0 $IMD. Permit2 settles one payment over nonce 5 (0.50 to imdPayTo, allowance 0.5). After D passes both requests reveal in full; releaseLapsedJob(b) reverts BadJob (its nonce is spent). Expected: allowance 0 and the untaken 0.50 in floorImd. Actual: allowance(frens, permit2) = 0.5 $IMD forever, isValidSignature(digest) still 0x1626ba7e, balance = floorImd + jobBudget + 0.5. Run: forge test --match-path test/scratch/DuplicateNonce.t.sol (fails: 500000000000000000 != 0).","severity":"low","snippet":"        if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours || _spent(nonce)) revert BadJob();","title":"approveJob refuses a spent Permit2 nonce but not one another request's pending approval already uses: the second payment's 0.50 $IMD is stranded in the allowance"},{"citation":"resolved","description":"setModules documents address(0) as a valid 'none' for the swapper, and the README's manual recovery and PlaceModules' replacement path both have the governor re-wiring it. In that state _floorValue returns 0, so quote() counts only floorImd and the unswept $IMD: the IMD6900 reserve, the bulk of a floor once fees have been bought in, is priced at zero. requestMintFor still runs (the floor buy just returns false), and recycle() still pays reserve / out in IMD6900. Any unprivileged minter in the window between unwiring and re-wiring (or if the governor leaves it unset while the mint is open) takes a reserve share for the curve's price. The precondition is a governor action, so this is a guard gap rather than an open drain, but nothing in the contract refuses it. Fix candidate: in quote(), revert (or price the reserve at the last known rate) when swapper == address(0) && reserve != 0, or have setModules refuse address(0) while reserve != 0 and mintOpen.","line":811,"path":"src/frens/IMD6900Frens.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IMD6900Frens, IFrenSwapper} from \"src/frens/IMD6900Frens.sol\";\nimport {FrenPrices} from \"src/frens/FrenPrices.sol\";\n\ncontract PlainToken {\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    function mint(address to, uint256 a) external {\n        balanceOf[to] += a;\n    }\n\n    function approve(address s, uint256 a) external returns (bool) {\n        allowance[msg.sender][s] = a;\n        return true;\n    }\n\n    function transfer(address to, uint256 a) external returns (bool) {\n        require(a != 0, \"zero\");\n        balanceOf[msg.sender] -= a;\n        balanceOf[to] += a;\n        return true;\n    }\n\n    function transferFrom(address f, address to, uint256 a) external returns (bool) {\n        if (allowance[f][msg.sender] != type(uint256).max) allowance[f][msg.sender] -= a;\n        balanceOf[f] -= a;\n        balanceOf[to] += a;\n        return true;\n    }\n}\n\n/// @dev Pays 70,000 IMD6900 per $IMD, minted fresh\ncontract FixedSwapper is IFrenSwapper {\n    PlainToken immutable out;\n    PlainToken immutable imdToken;\n\n    constructor(PlainToken o, PlainToken i) {\n        out = o;\n        imdToken = i;\n    }\n\n    function imdToImd6900(uint256 imdIn, uint256, address to) external returns (uint256 got) {\n        imdToken.transferFrom(msg.sender, address(this), imdIn);\n        got = imdIn * 70_000;\n        out.mint(to, got);\n    }\n\n    function ethToImd6900(uint256, address to) external payable returns (uint256 got) {\n        got = msg.value * 70_000 * 3000;\n        out.mint(to, got);\n    }\n\n    function floorRate() external pure returns (uint256) {\n        return 70_000e18;\n    }\n\n    function pendingImd(address holder) public view returns (uint256) {\n        return holder.balance * 3000;\n    }\n\n    function floorValue(uint256 r, address holder) external view returns (uint256 v) {\n        v = r / 70_000 + pendingImd(holder);\n    }\n}\n\n/// @notice With no swapper wired (setModules(address(0), _): \"address(0): none\"), quote() values the IMD6900 reserve\n///         at nothing, while recycle() still pays it out: anyone mints at the curve's price and sells straight back for\n///         a share of the reserve.\ncontract NoSwapperFloorTest is Test {\n    PlainToken imd = new PlainToken();\n    PlainToken imd6900 = new PlainToken();\n    IMD6900Frens frens;\n    FixedSwapper swapper;\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n\n    function setUp() public {\n        frens = new IMD6900Frens(\n            address(this), address(imd), address(imd6900), address(new PlainToken()), makeAddr(\"permit2\"),\n            makeAddr(\"x402\"), makeAddr(\"payTo\"), makeAddr(\"keeper\"), makeAddr(\"relayer\"), address(new FrenPrices())\n        );\n        swapper = new FixedSwapper(imd6900, imd);\n        uint8[8] memory n = [3, 13, 4, 3, 6, 3, 12, 16];\n        for (uint8 t; t < 8; ++t) {\n            uint16[] memory caps = new uint16[](n[t]);\n            uint8[] memory tiers = new uint8[](n[t]);\n            for (uint8 v; v < n[t]; ++v) caps[v] = t == 0 ? (v == 0 ? 2222 : 0) : 2222;\n            frens.setTraitRules(t, caps, tiers);\n        }\n        frens.sealTraits();\n        frens.setModules(address(swapper), address(0));\n        frens.setMintOpen(true);\n        for (uint256 i; i < 2; ++i) {\n            imd.mint([alice, bob][i], 10_000e18);\n            vm.prank([alice, bob][i]);\n            imd.approve(address(frens), type(uint256).max);\n        }\n    }\n\n    function test_NoSwapperMintsAtTheCurveAndRecyclesForTheReserve() public {\n        vm.prank(alice);\n        frens.requestMint(2, type(uint256).max); // 0.8805 $IMD bought into the reserve at once\n        imd6900.mint(address(frens), 0); // nothing else\n        // royalties bought in over time: the reserve is worth 300 $IMD (2 frens out: 150 each)\n        vm.deal(address(frens), 0.1 ether);\n        vm.roll(block.number + 1);\n        frens.buyFloorWithEth(0.1 ether, 0);\n        uint256 reserveImd = frens.reserve() / 70_000;\n        assertApproxEqAbs(reserveImd, 300.88e18, 1e16);\n        assertGe(frens.quote(1), reserveImd / 2, \"with a swapper the mint pays the floor it joins\");\n\n        frens.setModules(address(0), address(0)); // the governor unwires the swapper (a replacement in progress)\n        uint256 q = frens.quote(1);\n        assertEq(q, frens.priceOf(2), \"the reserve counts for nothing now: the curve's 0.6908\");\n        vm.prank(bob);\n        frens.requestMint(1, q);\n        vm.prank(bob);\n        (uint256 got6900,) = frens.recycle(3);\n        emit log_named_uint(\"bob paid ($IMD 1e18)\", q);\n        emit log_named_uint(\"bob took, in $IMD at 70,000 per\", got6900 / 70_000);\n        assertLe(got6900 / 70_000, q, \"selling straight back never pays\");\n    }\n}","reproduction":"2 frens out, 0.1 ETH of royalties bought in: reserve worth 300.88 $IMD (150.44 per fren), quote(1) >= 150 with the swapper wired. Governor: setModules(address(0), address(0)). quote(1) = priceOf(2) = 0.6908 $IMD. bob: requestMint(1, 0.6908e18) then recycle(3): receives reserve / 3 = 100.29 $IMD worth of IMD6900 for 0.69 paid (expected: a sale straight back never returns more than the mint cost). Run: forge test --match-path test/scratch/NoSwapperFloor.t.sol (fails: 100293500000000000000 > 690800000000000000).","severity":"low","snippet":"        return swapper == address(0) ? 0 : IFrenSwapper(swapper).floorValue(r, address(this));","title":"With no swapper wired, quote() values the IMD6900 reserve at nothing while recycle() still pays it: anyone mints at the curve and sells straight back for a reserve share"},{"citation":"resolved","description":"The audit-fix commit re-mined the salts (src/FrensPlan.sol, script/placement/addresses.json, README): the collection now lands at 0x6900d042460d6bdbe68CE994F4dE36706797CCd4, the swapper at 0x69002297DD7980af0d24249f6a44E7046B1Cb1fb, the minter at 0x46B50a3061Ea692e075231c13bc653FdD1Bedd29 and the gate at 0xF83807Ec2E27e1771Fb2594139c1F6925Cfd9B8E (test_ProtectedFactoryDeploysBothLaunchesInOrder asserts these). launch.json's notes, ADAPTATION.md's address table (lines 25-28) and test/test_collection_manifest.py (lines 17-20, which asserts the stale strings are present and therefore passes) still carry the previous plan's addresses. The manifest notes are not deployment authority, but they are what the adapter, the reviewer and the operator read for the timelock batch targets and the swapper exemption; a batch queued against 0x6900453d... would exempt nothing. The contracts list itself (PlaceFrens, PlaceModules $contract:PlaceFrens) is correct.","line":15,"path":"launch.json","reproduction":"grep -n 0x6900 launch.json ADAPTATION.md src/FrensPlan.sol: launch.json:15 and ADAPTATION.md:25-28 say 0x69007Ce8.../0x6900453d.../0xBbb2.../0x3F8d...; FrensPlan.sol:30-36 says 0x6900d042.../0x69002297.../0x46B5.../0xF838.... python3 test/test_collection_manifest.py passes because it asserts the stale addresses. Expected: every document agrees with FrensPlan (the task statement's addresses).","severity":"low","snippet":"collection 0x69007Ce82E0BF7981780585afF7c597415903547, swapper 0x6900453deFAc8Bb12eabdcf57CCC5a14E7628AeE, minter 0xBbb2796c9C54330788915990Ba36FDDe6dC198cF, gate 0x3F8d1553Cb71C8B5af013Ce985591d9B9BCD9ce2","title":"launch.json notes, ADAPTATION.md and test_collection_manifest.py name the pre-re-mine addresses, not where the pinned code lands"},{"citation":"resolved","description":"Recorded for the adapter, not as defects (the brief requires the team wallet's setup() and the prior round dispositioned them, ADAPTATION.md item 8): (1) the collection is configured after deployment, not in a constructor: setRenderer, setModules, setTraitRules/addPairRule/sealTraits, setMintOpen, gate.setWlRoot; (2) the governor can stop minting at any time (setMintOpen(false)), pause the floor's buys (setParams(_, 0, 0)), change the swapper that spends the floor's $IMD (setModules; a malicious swapper takes every buy's imdIn), change keeper/relayer/payee (setRoles) and tiers/caps; setGovernor is one-step; (3) the owner or the governor can block every peer transfer by setTransferValidator (a rejecting validator freezes the collection on marketplaces and OTC; recycle and buyTreasury bypass it); (4) the relayer alone decides which combos each request reveals as, within the rules; the keeper alone approves job payments; (5) IMD6900 sent to the collection outside buyFloor/buyFloorWithEth/buyTreasury is never booked (no sweep like _unswept for $IMD) and no function can move it; (6) the tier is a balance snapshot at the mint, so $IMD borrowed outside Uniswap v4 (another lender, or a friend's same-transaction transfer) reaches a tier; (7) the IMD6900 distributor status and the pair hook's fee exemption are external timelock-controlled switches: losing distributor status would make every recycle and treasury buy revert. No DELEGATECALL, CALLCODE or SELFDESTRUCT: both launch contracts' runtimes scan clean (PlaceFrens 193 bytes, PlaceModules 358 bytes) and the fresh-chain trace test asserts none in the nested creations.","line":1079,"path":"src/frens/IMD6900Frens.sol","reproduction":"FrensLaunchReview.t.sol: test_Audit_OwnerOrGovernorCanBlockAllPeerTransfers (a rejecting validator reverts transferFrom/safeTransferFrom for holders), test_Audit_GovernorCanCloseMintAgain (setMintOpen(false) then requestMint reverts MintClosed), test_Audit_ConstructorLeavesTheDocumentedTeamSetup (renderer, swapper, gate, rules all unset at deployment). test_SwapperMayPullOnlyTheBuy shows the swapper gets exactly each buy's imdIn: a governor-chosen swapper can keep it.","severity":"info","snippet":"    function setMintOpen(bool open) external onlyGovernor {","title":"Trust assumptions retained by design: post-deployment configuration and governor/owner powers over minting, the floor and transfers"}],"hash":"6ce01320ee438a4ce993d9fb19345711ffdbdcc017d2c0d766c41f07fdc6d1a0","nodeId":"3176fbf1-e7a9-4122-b516-4dd450bcd3ab","outcome":"completed","summary":"The review is complete. The findings are in `.imd-findings.json` at the repository root, and the working tree is otherwise unchanged (the scratch tests were removed after they were run and embedded as proofs).\n\n**Outcome.** No critical or high defect was found, so nothing in `src/frens/` needs to change and the re-mined 0x6900 addresses stand. One medium and four low findings are recorded for ADAPTATION.md, plus one info entry listing the retained trust assumptions. Four findings carry a Foundry proof that fails on the current code.\n\n**Findings**\n\n- **Medium, pending ETH priced at POOL4 spot.** The fix for the earlier High (ETH left out of the price) values the waiting ETH at POOL4's raw sqrtPrice of the moment, with no averaging, unlike the reserve part. A same-block push of POOL4 (a prior swap, or FrenMinter's own ETH-to-$IMD buy) lowers the quote below the floor the fren joins. Proof: 2 frens out, 1 ETH pending, quote falls from 112.94 to 1.57 $IMD. The exposure is largest in the launch runbook's own window, when floor buys are paused and royalties accumulate as unbuyable ETH. Fix lives in FrenSwapper (average or band the ETH price).\n- **Low, dust buy pins the slow average.** `_average` samples only the first buy of a block, so a 1e12 wei ETH buy each block keeps the average from ever converging. Proof: 100 blocks of full buys at 2x move the average by 2e-8 of the way.\n- **Low, duplicate in-flight Permit2 nonce.** `approveJob` refuses a spent nonce but accepts one another request's pending approval already uses; after IMD takes one payment, the other's 0.50 $IMD is stranded in the allowance forever. Proof included.\n- **Low, swapper unwired prices the reserve at zero.** With `setModules(address(0), _)`, quote ignores the IMD6900 reserve while recycle still pays it: mint at 0.69, recycle for 100.29 $IMD worth. Governor precondition, so low. Proof included.\n- **Low, stale addresses.** launch.json notes, ADAPTATION.md and test_collection_manifest.py still name the pre-re-mine addresses (0x69007Ce8…, 0x6900453d…) while FrensPlan, README and addresses.json have the current ones (0x6900d042…, 0x69002297…). The Python test passes only because it asserts the stale strings.\n- **Info, trust assumptions.** Post-deployment configuration via setup(), governor control over minting, floor buys, swapper, roles and tiers, owner/governor ability to block peer transfers through the validator, unbooked direct IMD6900 transfers, and the external distributor and fee-exemption switches.\n\n**Coverage.** Read in full: src/FrensPlacement.sol, src/FrensPlan.sol, src/frens/IMD6900Frens.sol, FrenSwapper.sol, FrenMinter.sol, FrenWorkerGate.sol, FrenPrices.sol, both deploy scripts, the existing audit tests and the gen.py salt miner. Skimmed only: WorkerFrensRenderer.sol (live art launch, view-only), src/FrensCode.sol (generated bytes, verified by the repo's own identity test). Not reached: the live IMD6900 token, the pair and POOL4 hooks, the x402 proxy and the Limit Break validator, beyond live reads confirming the $IMD token gives Permit2 no infinite allowance (so approveJob cannot overflow), the pair hook's fee is 690 bips, and the planned collection address is still empty. Offline: 191 existing tests pass; both launch contracts' runtimes scan clean for DELEGATECALL, CALLCODE and SELFDESTRUCT; both mined salts contain no f2/f4/ff byte. The fork suites were not run.","treeHash":null,"usage":{"cachedInputTokens":4329205,"inputTokens":642,"model":"claude-fable-5-1","outputTokens":92445,"runtime":"claude","turns":52,"wallClockMs":1884644}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"a4e285247c6f8aed","findings":[{"citation":"resolved","description":"IMD6900Frens.quote() values the ETH and WETH waiting in the collection at POOL4's instantaneous spot (FrenSwapper.pendingImd, src/frens/FrenSwapper.sol:119 `return eth == 0 ? 0 : FullMath.mulDiv(eth, imdPerEth(), 1e18);`). FrenMinter.mintWithEth reads that quote (line 75), buys exactly `cost` $IMD on POOL4 with an exact-output swap whose limit is MIN_SQRT_PRICE + 1 (line 158, no cap on how far POOL4 moves), then calls requestMintFor(msg.sender, count, cost) (line 79), which re-reads quote() at the price the minter's own swap just set and charges that; the difference is refunded to the minter (lines 80-81). Two consequences, both reproduced offline with a constant-product POOL4 at the specialist's mainnet-like reserves (23.37 ETH / 7,400 $IMD, 1% fee): (A) every sizeable honest ETH mint while ETH is pending pays less than the quote it was shown and the existing holders bear the difference; (B) anyone can push POOL4 with ETH in the same transaction, mint at the pushed quote out of their own $IMD, and sell the pushed $IMD back, at a profit, taking about half of the existing holders' pending-ETH value. The collection's Flash guard does not apply: the unlock has completed before the tier is read. This is the pricing defect ADAPTATION.md retains as a99c13dac6ce (pending ETH at spot), which that disposition said had not been validated economically; it is validated here, and the collection's own ETH mint path performs the push as a side effect of a normal mint. Severity kept at medium (loss bounded by the pending ETH, needs ETH pending while few frens are out and POOL4 thin: the bootstrap and window phase, exactly when the README has floor buys paused); the requester may want to revisit the retained-medium disposition now that the round trip is shown profitable. Fix that keeps the design: value pending ETH at its dearest of spot and a slow average (as floorRate does for IMD6900), or in FrenMinter take the quote after the POOL4 purchase and enforce paid == cost (no refund of the difference).","line":75,"path":"src/frens/FrenMinter.sol","reproduction":"test/scratch/EthMintSelfPush.t.sol (offline, passes on this code: the behaviour exists). Setup: fresh IMD6900Frens + FrenSwapper + FrenMinter over a constant-product POOL4 mock (x = 23.37 ETH, y = 7,400 $IMD, 1% fee; imdPerEth 316.645), pair pool slot0 fixed, floor buys paused (setParams(1,0,0)), alice requestMint(2) (2 frens out, floorImd 0.88), 1 ETH dealt to the collection. (A) quote(10) = 1,587.626 $IMD. bob (69 $IMD bag, tier 2) mintWithEth{20 ether}(10, 1587.626e18): the swap leaves imdPerEth at 194.929; ethSpent 6.4479 ETH; 608.581 $IMD refunded to bob; bob paid 979.045. Collection still holds 1 ETH. After an arbitrageur restores POOL4 to 316.645: one existing fren's share (pending + floorImd)/out = 108.006 $IMD, against 158.763 before the mint and 158.721 had bob paid the quote he was shown. Expected: a mint never lowers the existing holders' share; actual: -32%. (B) honest quote(69) = 10,954.622 $IMD. Attacker (7,000 $IMD stash, 20 ETH) in one transaction: buys $IMD with 9.5 ETH, no limit (imdPerEth 316.645 -> 160.527); requestMint(69) pays quote(69) = 5,568.541; sells exactly the $IMD bought back (imdPerEth 314.824). Round trip cost 0.1352 ETH (42.56 $IMD at spot); the 69 frens are worth 69 x 82.870 = 5,718.006 $IMD against 5,568.541 paid (+149.465 $IMD). Net about +107 $IMD (~0.34 ETH). Existing holders' share 158.763 -> 82.870 per fren. The specialist's mainnet fork numbers (block 26150611: push to 160.98, 5,582.147 paid vs 10,970.899 honest, +149.5 $IMD, 0.1622 ETH round trip) could not be re-run here (no MAINNET_RPC_URL) but match this model within 1%.","severity":"medium","snippet":"        uint256 cost = frens.quote(count);","title":"mintWithEth quotes before its own unlimited POOL4 swap and is charged after it: the pending ETH is valued at the price the mint itself pushed, and a deliberate push-mint-unpush round trip is profitabl"},{"citation":"resolved","description":"quote() (lines 498-500) values one share as (unswept + floorImd + reserve at floorRate + pendingImd)/out and buyTreasury() (lines 922-926) charges 2 x (reserve/out, floorImd/out + _floorValue(0)/out), both counting the ETH and WETH in the contract at POOL4's price. recycle() pays floorPerFren() after _sweep(): (reserve/out, floorImd/out) only; the ETH part is left out. The dc3bd30d fix put pending ETH into the mint and treasury prices but not into the sale price, so the asymmetry now runs against the recycler: whoever sells while ETH waits gives their whole share of it to the remaining holders. The recycler can only avoid it by first calling buyFloorWithEth, which is capped at maxEthPerBuy per buyDelayBlocks, reverts while buys are paused (the whole bootstrap phase) and reverts while the ETH route fails. No mint/recycle arbitrage results (the mint price is the higher one), so this is a bounded loss to an identifiable party under common state (royalties pending), low. Fix options that keep the design: pay the recycler its ETH share in ETH alongside the two in-kind parts, or sweep pending ETH into the reserve before pricing a sale and refuse the recycle while it cannot be swept.","line":903,"path":"src/frens/IMD6900Frens.sol","reproduction":"test/scratch/JudgeRepro.t.sol test_RecyclePaysNoPendingEthShare (unit mocks from test/frens/IMD6900Frens.t.sol: MockSwapper rate 70,000, ETH valued at 3,000 $IMD; passes on this code). alice requestMint(1); next block bob requestMint(1): 2 out, reserve 2 x 0.19 $IMD of IMD6900, floorImd 0. vm.deal(frens, 1 ether). quote(1) = 1500.19e18 (the mint share counts 1,500 $IMD of the ETH). alice recycle(1) returns (13,300e18 IMD6900 = 0.19 $IMD, 0): nothing of the ETH; address(frens).balance still 1 ether. bob buyTreasury(1, max, 1) reverts Cap; bob buyTreasury(1, max, max) charges (26,600e18 IMD6900, 6,000e18 $IMD): 2 x 3,000 $IMD for the ETH share of the fren alice just sold for 0.19. Expected: the sale and the buy of the same share count the same assets, as they do for unswept $IMD since b7d35d29; actual: the recycler loses 1,500 $IMD of value per share.","severity":"low","snippet":"        (paid, imdPaid) = floorPerFren();","title":"recycle() pays no share of the pending ETH/WETH that quote() and buyTreasury() price in: a holder who sells to the treasury while fees wait forfeits that share to the holders who stay (audit_permissio"},{"citation":"resolved","description":"The NatSpec at line 800 says 'recycle pays exactly this', but recycle() (line 902) and buyTreasury() (line 921) call _sweep() before reading floorPerFren(), which books the unswept $IMD (balance minus floorImd, jobBudget and the Permit2 allowance) into floorImd; the public view does not. Unswept $IMD arises routinely: IMD refunds half of every 0.50 job payment to the contract, and while floor buys are paused (setup() sets maxImdPerBuy = 0) nothing sweeps it. A buyTreasury caller who sets maxImd = 2 x view gets a Cap revert after every job refund; a site or marketplace reading floorPerFren() under-reports the recycle payout by extra/out. No funds lost (the write path always pays at least what the view says). Fix: add the unswept extra to imdPart in the view (quote() already counts it via _unswept()).","line":805,"path":"src/frens/IMD6900Frens.sol","reproduction":"test/scratch/JudgeRepro.t.sol test_FloorPerFrenViewDivergesFromRecycle (unit mocks; passes on this code). alice requestMint(3); alice recycle(1) (2 out); imd.mint(frens, 0.25e18) (a job refund). floorPerFren() = (36,633.33e18, 0): imdPart 0. bob buyTreasury(1, 2 x 36,633.33e18, 0) reverts Cap (the write sweeps 0.25e18 first and asks 2 x 0.125e18). alice recycle(2) returns (36,633.33e18, 0.125e18). Expected: the view equals what recycle pays; actual: the view is 0.125e18 per fren short.","severity":"low","snippet":"        (imd6900Part, imdPart) = (reserve / out, floorImd / out);","title":"floorPerFren() view omits the unswept $IMD that recycle() and buyTreasury() sweep in first, so a treasury buyer who sets maxImd from the view gets Cap and integrations under-report what a recycle pays"},{"citation":"resolved","description":"buyFloorWithEth calls _buyGate() first (line 869), which writes lastEthBuyBlock = block.number before any size check; the only lower bound on ethIn is ethIn == 0. A 1 wei buy passes (on the live pools 1 wei still yields a nonzero IMD6900 amount, so _credit's got == 0 check does not stop it) and the honest buy in the same block reverts TooSoon. Repeated each block the griefer pays only gas and 1 wei, and the contract's ETH (hook fee slice, royalties, unwrapped WETH) is never bought into the reserve. The $IMD pace was protected against dust taking the mints' turn (test_DustEthBuyCannotTakeTheMintsTurn) but the ETH pace has no dust protection. While the ETH stays pending, quote() and buyTreasury() charge for it at spot (finding 1) while recycle() pays nothing for it (finding 2), so the griefing also enlarges both of those effects. No direct theft; a griefing race. Fix: require ethIn >= a dust floor (e.g. min(address(this).balance, maxEthPerBuy)), or let a buy below the threshold through without consuming the block turn.","line":870,"path":"src/frens/IMD6900Frens.sol","reproduction":"test/scratch/JudgeRepro.t.sol test_DustEthBuyTakesTheBlockTurn (unit mocks; passes on this code). alice requestMint(2) (reserve 0.88e18 x 70,000); vm.deal(frens, 10 ether); buyDelayBlocks 1, maxEthPerBuy 0.25 ether. For 50 successive blocks: griefer buyFloorWithEth(1, 0) succeeds; bot buyFloorWithEth(0.25 ether, 0) in the same block reverts TooSoon. After 50 blocks address(frens).balance == 10 ether - 50 and the reserve grew by exactly 50 x 70,000 x 3,000 = 1.05e10 wei of IMD6900. Expected: 10 ETH bought in at 0.25 ETH a block (40 blocks); actual: all of it still pending.","severity":"low","snippet":"        if (ethIn == 0 || ethIn > address(this).balance || ethIn > maxEthPerBuy) revert Cap();","title":"buyFloorWithEth has no minimum size: a 1-wei buy at the top of each block takes the fee ETH's block turn, so royalties can be kept pending indefinitely for gas only (audit_flow 68948df9)"},{"citation":"resolved","description":"The quotes are documented as 'what buyFloor would get' and 'what buyFloorWithEth would get' (lines 99, 104). Their unlockCallback path swaps the whole input with the extreme limit on both legs (POOL4 at line 138, the pair at line 149) and ignores maxImdPerBuy / maxEthPerBuy. The real buys go through FrenSwapper, whose legs stop at _limit(key, dir, pairMoveBips()) (half the pair hook's fee(): 345 bips with the live 690) and _limit(pool4, true, POOL4_MOVE_BIPS) (50 bips), returning the unspent input (src/frens/FrenSwapper.sol lines 212, 249, 262, 231-234, 272-273). The divergence is in the code regardless of pool state: one path has a limit, the other does not. Impact: a keeper or site that passes the quote as minOut has every buy fail (buyFloor: the swapper's Short is caught in _buyFloor and surfaces as Cap; buyFloorWithEth: Short), and dashboards overstate the floor's growth. No loss of funds. Fix: run the quotes with the same limits (pairMoveBips(), POOL4_MOVE_BIPS) and return the input actually spent, or document that they are unlimited pool quotes not to be used as minOut.","line":149,"path":"src/frens/FrenMinter.sol","reproduction":"Code trace (deterministic): FrenMinter.unlockCallback QUOTE_FLOOR_ETH leg line 138 `sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1`, pair leg line 149 `imdIs0 ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1`; FrenSwapper.unlockCallback line 212 `sqrtPriceLimitX96: _limit(key, zeroForOne, pairMoveBips())`, _ethViaImd lines 249 and 262 with POOL4_MOVE_BIPS and pairMoveBips(). The repo's own offline model confirms the quote path's limit: test/helpers/FrensEthFixture.sol requires `p.sqrtPriceLimitX96 == TickMath.MAX_SQRT_PRICE - 1` for the pair leg and FrenMinterOffline passes. Both specialists' mainnet-fork measurements (block 26150558-26150640: quoteFloor(50e18) = 5,589,160.645e18 IMD6900 vs buyFloor(0) getting 306,576.375e18 for 1.934 $IMD at the 3.45% limit; quoteFloorEth(0.25e18) = 7.4e24 vs buyFloorWithEth getting 306,576.375e18 with 0.1912 ETH returned; buyFloor(5_589_160e18) reverts Cap and buyFloorWithEth(0.25e18, 7_415_969e18) reverts Short) agree with each other but could not be re-run here (no MAINNET_RPC_URL). Expected: a quote within the buy's limits; actual: the quote never applies them.","severity":"low","snippet":"                    sqrtPriceLimitX96: imdIs0 ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1","title":"FrenMinter.quoteFloor / quoteFloorEth swap with no price limit while buyFloor / buyFloorWithEth stop at the swapper's half-fee limits: a minOut taken from the quote makes every real buy revert and the"},{"citation":"resolved","description":"approveJob stores only the nonce and deadline; the permit digest also depends on imdPayTo (line 760), re-read at clearing time. If the governor changes the payee with setRoles while a payment is in flight and the payment then lapses, _reclaimLapsed -> _unapprove clears the digest for the new payee (never set) and approvedDigest[oldDigest] stays true for ever while the allowance is reduced and the job money re-used. No funds can move: the digest binds the deadline and Permit2 refuses an expired permit, and the allowance was taken back. Residue only: 'ERC-1271: valid only for the payments approveJob approved' is no longer exact after a payee change. Fix: store the permit digest (or the payee) in the Request at approval and clear that in _unapprove.","line":740,"path":"src/frens/IMD6900Frens.sol","reproduction":"test/scratch/JudgeRepro.t.sol test_StaleDigestAfterPayeeChange (unit mocks; passes on this code). alice requestMint(1); keeper approveJob(id, 1, now + 600, q) -> permitDigest, isValidSignature(permitDigest) == 0x1626ba7e, allowance(frens, permit2) == 0.5e18. Governor setRoles(0, 0, payTo2). warp(deadline + 1). keeper approveJob(id, 2, now + 600, q) (reclaims the lapsed payment first): allowance == 0.5e18 (one payment approved, as intended) but isValidSignature(permitDigest) is still 0x1626ba7e. Expected: the reclaimed payment's digest is no longer approved.","severity":"info","snippet":"        approvedDigest[_permit2Digest(r.jobNonce, r.jobDeadline)] = false;","title":"setRoles(imdPayTo) while a job payment is approved: _unapprove recomputes the Permit2 digest with the new payee, clears a key that was never set and leaves the old digest approved after the reclaim (a"},{"citation":"resolved","description":"After the opening requestMintFor(minter, count, maxPay) has no restriction on who may name `minter` (only the closed-mint branch is governor-only), and the gate's spend() trusts whatever minter the collection passes (src/frens/FrenWorkerGate.sol lines 121-129 check only msg.sender == frens). During the window a stranger can pick any wallet holding credits, pay the curve price and burn that wallet's credits at a moment of the stranger's choosing. The victim receives the frens (minted to them, at the tier of their bag at that instant), so there is no loss of funds and the payer pays; what the worker loses is control over timing (the tier snapshot, relevant to WL wallets whose tier depends on their bag) and batching (one 0.50 job per request). The gate's NatSpec ('the credit is the minter's, not the payer's') describes this as the design. Recorded for the requester: if the credit is meant to be the worker's to spend, spend() could take the payer and accept payer == minter or payer == the trusted FrenMinter.","line":538,"path":"src/frens/IMD6900Frens.sol","reproduction":"test/scratch/JudgeRepro.t.sol test_AnyoneSpendsAWorkersCredit (unit mocks + FrenWorkerGate; passes on this code). Gate wired (setModules(swapper, gate)); worker claims a WL amount of 1 (credits[worker] == 1). carol (no credit) calls frens.requestMintFor(worker, 1, max): succeeds; credits[worker] == 0, workerMinted == 1, worker owns token 1; worker's own requestMint(1, max) now reverts NoCredit(0). Expected under the window's wording (one fren per NFT, the holder's to mint): the stranger is refused.","severity":"info","snippet":"        if (mintOpen && workerGate != address(0)) IWorkerGate(workerGate).spend(minter, count);","title":"Anyone can spend a worker's window credits by paying a mint for them: requestMintFor is open to any payer once the mint is open and FrenWorkerGate.spend charges the named minter, not the payer (audit_"},{"citation":"resolved","description":"The swap's price limit is derived from an external, mutable value with no lower bound. For fee() in {0, 1}, moveBips = 0 and _limit computes f = sqrt(1e36) = 1e18 exactly, so the limit equals slot0's sqrtPrice in both directions. Uniswap v4's Pool.swap rejects a limit equal to the current price (zeroForOne: limit >= price; else limit <= price -> PriceLimitAlreadyExceeded; upstream Pool.sol, not vendored here). imdToImd6900 then reverts inside _buyFloor's try/catch: mints keep going, buyFloor reverts Cap for everyone, buyFloorWithEth reverts with the pool's error, and fee ETH and floor $IMD stop being bought in until the governor wires a different swapper (pairMoveBips is not configurable). The same reading carries the sandwich bound: 'half the fee' holds only while the hook's fee stays at least twice the move. The hook is IMD's, not this collection's, so no unprivileged trigger: info.","line":170,"path":"src/frens/FrenSwapper.sol","reproduction":"test/scratch/JudgeRepro.t.sol LimitRepro.test_ZeroFeeLimitEqualsPrice (passes on this code): a FrenSwapper subclass over a slot0-only pool (sqrtPrice p = 2^96/265) and a hook whose fee() returns 0: pairMoveBips() == 0, _limit(pairKey(), true, 0) == p and _limit(pairKey(), false, 0) == p. With v4's Pool.swap that limit reverts PriceLimitAlreadyExceeded in either direction, so frens.buyFloor(0) reverts Cap although floorImd > 0 and the swapper is wired. Expected: a bounded minimum move or a revert naming the cause; actual: a silent halt of all floor buys.","severity":"info","snippet":"        return IPairFee(pairHook).fee() / 2;","title":"pairMoveBips() = fee()/2 has no floor: a pair-hook fee below 2 bips makes _limit() return the current price and every floor buy revert PriceLimitAlreadyExceeded, silently stopping the floor (audit_mat"},{"citation":"resolved","description":"quote() charges a mint exactly its share of the floor (count x value/out, line 500) whenever that exceeds the curve; of that JOB_PRICE goes to the job budget and only paid - JOB_PRICE joins the floor (line 553). The per-fren floor after the mint is value/out - 0.5/(out+count): every fren, new and old, loses 0.5/(out+count). The minter bears count/(out+count) of the job price and the existing holders out/(out+count). With 100 frens out a mint-plus-recycle costs the caller about 0.005 $IMD plus gas while draining about 0.495 $IMD from the other holders and commissioning a 0.50 $IMD job each time. The README acknowledges the minter loses only 'their share of the job', so this is the design: the caller cannot profit and the drain is bounded by 0.50 $IMD a cycle (gas-bounded). Recorded so the requester can decide whether to charge the job on top of the floor share in the floor-bound regime (quote = atFloor + JOB_PRICE when atFloor > price).","line":553,"path":"src/frens/IMD6900Frens.sol","reproduction":"test/scratch/JudgeRepro.t.sol test_JobPriceSocialised (unit mocks; passes on this code). alice requestMint(69) then requestMint(31) (100 out); vm.deal(frens, 0.25 ether); next block buyFloorWithEth(0.25 ether, 0) (750 $IMD worth in): per-fren floor 8.18 $IMD, above the 0.69 curve. bob requestMint(1) pays quote(1) = 8.18e18; next block bob recycle(101). bob is down 4,950,495,049,504,951 wei (0.5e18/101) in $IMD value and the per-fren floor fell 8.18e18 -> 8,175,049,504,950,495,049 (0.5e18/101 each, 0.495 $IMD over alice's 100 frens). Expected if each mint paid its own job: bob down 0.5 $IMD, alice unchanged.","severity":"info","snippet":"        floorImd += paid - JOB_PRICE;","title":"In the floor-bound regime a mint pays only count/(out+count) of its own 0.50 $IMD job price; the existing holders pay the rest, so mint-and-recycle cycles are nearly free for the caller and drain up t"}],"hash":"8633ef568b1449ef7982a460eb5eceed5eae10b052fa67b8565e7b42f870b303","nodeId":"463f747b-1d65-42f4-af9a-2db03a4f9627","outcome":"completed","summary":"The review is complete. `.imd-findings.json` holds 9 findings and a 58-row coverage record (all 55 entry points plus 3 invariants); every snippet was checked against its line, and all 9 scratch reproductions pass on the current code. No tracked file was changed.\n\n**Judgement of the 11 specialist findings**\n\n- **Kept, merged, 9 total.** The two quoteFloor/quoteFloorEth reports (flow and math) were the same root cause and became one low. The floorRate \"post-buy spot\" info was dropped: it is the documented \"IMD6900 at its dearest\" design, protective in direction, not a defect.\n- **No high or critical.** No src/frens change is required under the requester's rule; all findings go to ADAPTATION.md. Mainnet fork claims could not be re-run because no RPC URL is set; I reproduced them offline instead and say so in each entry.\n\n**Findings by severity**\n\n1. **Medium.** mintWithEth quotes before its own unlimited POOL4 swap and charges after it, so pending ETH is valued at the price the mint itself pushed. I rebuilt this with a constant-product POOL4 at the specialist's mainnet-like reserves. An honest 10-fren ETH mint got 608 of 1,588 quoted IMD refunded and cut each existing holder's share from 158.8 to 108.0 IMD. A deliberate push-mint-unpush round trip with 69 frens netted about 107 IMD (roughly 0.34 ETH) and halved the two existing holders' pending-ETH value. This is the same root as the retained a99c13 finding, whose disposition said the economics were unvalidated. They are validated now, and I flag that the requester may want to revisit that retained-medium decision.\n2. **Low.** recycle pays no share of pending ETH that quote and buyTreasury price in. The recycler forfeits that share to remaining holders.\n3. **Low.** floorPerFren view omits unswept IMD, so a maxImd taken from it reverts buyTreasury after every job refund.\n4. **Low.** A 1-wei buyFloorWithEth takes the block's ETH turn. 50 blocks of griefing left 10 ETH pending with the reserve up by only 1.05e10 wei.\n5. **Low.** The minter's floor quotes run without the swapper's price limits, so a quote used as minOut makes every real buy fail.\n6. **Info.** Changing imdPayTo mid-payment leaves a stale ERC-1271 digest approved. Inert, since Permit2 rejects expired permits.\n7. **Info.** Any payer can spend a worker's window credits by naming them in requestMintFor. The worker still receives the frens.\n8. **Info.** A pair-hook fee under 2 bips makes the swap limit equal the current price and silently halts floor buys.\n9. **Info.** In the floor-bound regime existing holders pay most of each mint's 0.50 IMD job price. Acknowledged design, recorded for the requester's decision.\n\n**My own pass** over the entry points found nothing beyond the specialists' leads. The job-payment state machine, reveal chaining, LastFrenOut guard, reserve invariant and the placement contracts all hold as traced.","treeHash":null,"usage":{"cachedInputTokens":3105937,"inputTokens":642,"model":"claude-fable-5-1","outputTokens":62890,"runtime":"claude","turns":34,"wallClockMs":1243279}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"c0f94d5c3d395bef","findings":[],"hash":"c84eb9baf58648ece853ef928516e24f46d9a70c9f3114f9a979c3b20d728905","nodeId":"61c5d82a-0f17-4e8b-ad1e-3e502b75047d","outcome":"failed","summary":"the task produced no changes; the agent's last message was: [launch.json](/home/imd3/.identitymd/work/dc4e3a21-8e12-44c3-9a6d-ae180f5c8c60/61c5d82a-0f17-4e8b-ad1e-3e502b75047d/launch.json) already matches the brief and compiled constructors, so no revision was needed.\n\n- Schema and all four manifest checks passed.\n- `forge build` passed.\n- `forge test`: 209 passed, 0 failed; 5 fork suites skipped because `MAINNET_RPC_URL` is absent.\n\nNo tracked files changed.","treeHash":null,"usage":{"cachedInputTokens":1873664,"inputTokens":85057,"model":"gpt-6-astra","outputTokens":8806,"runtime":"codex","turns":10,"wallClockMs":573300}},{"artifacts":[],"attempt":2,"bundleHash":"418a08470af06b1169a473dc12b9fd52d096231e472c6d43b82e09a171095cb0","device":"d6415252dcd668d9","findings":[],"hash":"d4472861693d7be70004da417b876947a2ebf9c9b5600372afb962354b557d12","nodeId":"61c5d82a-0f17-4e8b-ad1e-3e502b75047d","outcome":"completed","summary":"Updated [launch.json](/home/imd/.identitymd-3/work/dc4e3a21-8e12-44c3-9a6d-ae180f5c8c60/61c5d82a-0f17-4e8b-ad1e-3e502b75047d/launch.json) with clearer setup and audit notes, preserving the required constructors and deployment order. Only the manifest changed.\n\nValidation passed:\n\n- Schema, constructor ABI and four manifest checks.\n- `forge build --offline`.\n- `forge test --offline`: 209 passed, 0 failed; 5 fork suites skipped without `MAINNET_RPC_URL`.","treeHash":"2d812a91187956144748eea2ddd41dfb123d9763","usage":{"cachedInputTokens":720896,"inputTokens":92579,"model":"gpt-6-astra","outputTokens":5874,"runtime":"codex","turns":6,"wallClockMs":304774}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"f9cb4fd544aa3c68","findings":[{"citation":"resolved","description":"Asymmetry (economics x asymmetry seam) between the three prices of one share of the floor. quote() (line 498-500) values a share as (unswept + floorImd + reserve at floorRate + pendingImd) / out, and buyTreasury() (line 922-926) charges 2 x (reserve/out, floorImd/out + _floorValue(0)/out), both counting the ETH and WETH sitting in the contract at POOL4's price. recycle() pays only floorPerFren() = (reserve/out, floorImd/out) after _sweep(): the ETH/WETH part is left out entirely. The ea109756 fix (dc3bd30d) put the pending ETH into the mint and treasury prices so nobody can buy under it and sell over it once it is bought in; it did not mirror that into the sale price, so the asymmetry now runs the other way: whoever recycles while ETH waits gives their whole share of it to the remaining holders. The holder can only avoid it by first calling buyFloorWithEth, which is capped at maxEthPerBuy (0.25 ETH) per buyDelayBlocks, reverts outright while buys are paused (setParams(_,0,0), the whole bootstrap phase) and is not wrapped in try/catch, so it also reverts while the ETH route fails (before the collection is an IMD6900 distributor). Impact: a bounded loss to an identifiable party (the recycler) under common state (royalties/hook fees pending), in favour of the other holders; no attacker profit path was found beyond passively holding, so severity is low rather than medium. Fix options that keep the design: pay the recycler its ETH share in ETH (address(this).balance / out, and the WETH likewise) alongside the two in-kind parts, or sweep the pending ETH into the reserve before pricing the sale the way _sweep() does for $IMD and refuse the recycle while it cannot be swept.","line":903,"path":"src/frens/IMD6900Frens.sol","reproduction":"State: mint open, swapper wired (any swapper whose floorValue counts holder.balance, as FrenSwapper.pendingImd does), flat 0.69 price table. 1) alice requestMint(1), next block bob requestMint(1): two frens out, reserve = 2 x 0.19 $IMD of IMD6900 (job 0.5 each), floorImd = 0. 2) 1 ETH arrives at the collection (a royalty); with the swapper valuing ETH at 3000 $IMD, pendingImd = 3000e18. 3) quote(1) = 1500.19e18: the mint price of one share counts 1500 $IMD of the ETH. 4) alice recycle(1) returns (paid, imdPaid) = (0.19e18 x 70_000 IMD6900, 0): worth 0.19 $IMD, nothing of the ETH; address(this).balance is still 1 ether and bob's single fren now backs all of it. 5) buyTreasury(1, max, 2 x imdPart + 1) by bob reverts Cap because the treasury asks 2 x (imdPart + 3000e18) in $IMD for the very fren alice just sold for 0.19. Expected: the sale price of a share and the buy/mint price of the same share count the same assets (as recycle and buyTreasury already do for unswept $IMD since b7d35d29). Actual: the recycler gets the in-kind parts only and loses 1500 $IMD of value per share to the remaining holder. Verified by test/scratch/PermissionsAsymmetry.t.sol test_RecyclePaysNoShareOfThePendingEthTheMintAndTreasuryPricesCount (passes on this code: it asserts the gap, logging mint share 1500.19 vs recycle payout 0.19).","severity":"low","snippet":"        (paid, imdPaid) = floorPerFren();\n        _transfer(msg.sender, address(this), tokenId);\n        reserve -= paid;\n        floorImd -= imdPaid;","title":"recycle pays no share of the pending ETH/WETH that quote and buyTreasury price in: a holder who sells to the treasury while fees wait forfeits that share to the holders who stay"},{"citation":"resolved","description":"Access x asymmetry seam. requestMintFor(minter, count, maxPay) has no restriction on who may name `minter` after the opening (only the closed-mint branch is governor-only), and the gate's spend() trusts whatever minter the collection passes (FrenWorkerGate.sol line 121-129 checks only msg.sender == frens). So during the workers' window a stranger can pick any wallet holding credits, pay the curve price (0.69 $IMD plus the 0.50 job inside it) and burn that wallet's credits at a moment of the stranger's choosing. The victim does receive the frens (minted to `minter`, at the tier of its bag at that moment), so there is no loss of funds and the attacker pays for the gift; what the worker loses is control over when its credits are used (the tier snapshot, which for a WL wallet depends on what it holds at that instant) and over batching (one 0.50 job per request). Identity.md holders are tier 3 through identityTier regardless, so for the window's main audience the only effect is the forced timing. Recorded as information for the requester: if the credit is meant to be the worker's to spend, spend() could require msg.sender-of-the-mint to be the minter or an approved payer (FrenMinter passes msg.sender), e.g. by having requestMintFor forward the payer and the gate accept payer == minter || payer == trusted minter contract.","line":538,"path":"src/frens/IMD6900Frens.sol","reproduction":"State: mint open, gate wired (setModules(swapper, gate)), worker holds identity.md #1 and claims it: gate.claim([1], worker) gives credits[worker] = 1. attacker (no credits, holds $IMD) calls frens.requestMintFor(worker, 1, type(uint256).max). Expected under the window's wording (one fren per NFT, the holder's to mint): the stranger has no credit and the call is refused. Actual: the call succeeds, credits[worker] == 0, workerMinted == 1, the worker owns token 1, and the worker's own frens.requestMint(1, max) now reverts NoCredit(0). Verified by test/scratch/PermissionsAsymmetry.t.sol test_AnyoneCanSpendAWorkersWindowCreditByMintingForThem (passes on this code).","severity":"info","snippet":"        if (mintOpen && workerGate != address(0)) IWorkerGate(workerGate).spend(minter, count);","title":"Anyone can consume a worker's window credits by paying a mint for them: requestMintFor is open to any payer once the mint is open and FrenWorkerGate.spend charges the minter, not the payer"}],"hash":"daccfa80e548183143002ccf07775afce64590dc5c577a4dd97123f99458ca39","nodeId":"113393b2-a5d6-40b8-b9ea-8424884a27ca","outcome":"completed","summary":"All 55 entry points have a row, both findings' snippets match the tree byte for byte, and the findings file plus the scratch test are git-ignored, so the submission changes no tracked file.\n\n## Recap\n\n**Outcome:** the permission model of the Worker Frens collection launch holds. I found no HIGH or CRITICAL defect in the Access Control, Trust Gap or Asymmetry areas, so no source, salt or address change is needed. The findings file holds one LOW and one INFO, each with a concrete reproduction verified by a passing scratch test.\n\n**What I covered**\n- Every modifier and inline guard on all 55 state-changing entry points across the collection, swapper, minter and gate, plus the two placers: who holds each role after deployment (owner, governor and gate owner are the team wallet; keeper, relayer and payee are the plan's constants; the placers keep nothing), ownership handover paths, the ERC-1271 job signing surface, the floor's three prices against each other, and the storage-write symmetry of mint, reveal, retry, approve and reclaim.\n- The baseline suite passes offline (196 tests). The Flash guard's transient slot matches keccak(\"Unlocked\") minus one.\n\n**Findings written to `.imd-findings.json`**\n- **Low, economics × asymmetry.** The mint price and the treasury price count the ETH/WETH waiting in the contract, but `recycle` pays only the in-kind parts. With two frens out and 1 ETH pending, the mint prices one share at 1500.19 $IMD while a recycler receives 0.19. The holder can only escape it by sweeping ETH first, which is capped per block and reverts entirely while buys are paused or the ETH route fails.\n- **Info, access × asymmetry.** Once the mint is open, any payer can name any `minter` in `requestMintFor`, and the gate charges that minter's window credits. A stranger can burn a worker's credit at a time of the stranger's choosing. The worker receives the fren, so no funds are lost.\n\n**Already known, not re-reported:** the four retained MEDIUM/LOW items in ADAPTATION.md (spot-priced pending ETH, dust-pinned average, duplicate pending nonces, swapper removal) and the admin-only trust assumptions. The coverage rows reference them where relevant.\n\n**Not reached:** nothing in my area. Fork-dependent behaviour (the real $IMD token's approve semantics, the x402 proxy's witness type, the seat-operator call on IMD6900) was not verifiable offline and is noted as periphery outside this area.","treeHash":null,"usage":{"cachedInputTokens":1896401,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":76682,"runtime":"claude","turns":39,"wallClockMs":1481214}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"fa8fc4653a9e883d","findings":[],"hash":"ea53859fc94fc2e66454b209fcae90cad0f3bfbd7b5083930fb837995e48d600","nodeId":"463f747b-1d65-42f4-af9a-2db03a4f9627","outcome":"completed","summary":"All nine earlier findings are settled under the requester's stated rule, and nothing new reproduces. The findings file is written with an empty list and 58 coverage rows.\n\n**What I verified**\n\n- **Source is unchanged.** No file under src, script, lib or foundry.toml differs from the fix commit. The four CREATE2 addresses stand, and the manifest test passes.\n- **Every earlier finding still reproduces** on this code, and the author's new suite at test/FrensRevisionRisks.t.sol demonstrates each one with a passing retained-risk test. The medium ETH-mint push, the recycle pending-ETH omission, the floorPerFren view gap, the one-wei block-turn grief, the unlimited floor quotes, the stale payee digest, the stranger-spends-credit, the fee-floor limit and the socialised job price all match my reproductions. The model mint numbers match mine exactly.\n- **Disposition is correct.** The requester ruled that MEDIUM, LOW and INFO findings are recorded in ADAPTATION.md with no source change, because any byte moves the timelocked addresses. ADAPTATION.md records all nine with test names, limits and the offline-model caveat. The author's \"disputed\" verdicts deny only the requested repair, not the behaviour, and the record says so plainly.\n- **Severity stands.** Nothing new raises the ETH-mint finding to HIGH. Loss is bounded by the ETH pending at the time, needs a thin POOL4 and few frens out, and the author's model confirms the same bounded figures.\n- **Full suite** runs clean: 233 passed, 0 failed, 5 skipped for lack of a mainnet RPC. The one-transaction gas test passes.\n\n**One non-blocking note**\n\nREADME line 105 still says the follow-up audit left \"four remaining\" findings. With this round there are thirteen retained items, all in ADAPTATION.md and all pointing there from the README. The sentence is stale but not wrong enough to open a revision, so I left it out of the findings list.\n\n**Open operational items** the record already carries: keep floor buys unpaused or sweep pending ETH before opening the mint, keep a swapper wired during an open mint, and treat the minter's floor quotes as pool quotes rather than minimum outputs.","treeHash":null,"usage":{"cachedInputTokens":749041,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":15404,"runtime":"claude","turns":14,"wallClockMs":350647}},{"artifacts":[],"attempt":1,"bundleHash":"38151f4d262e672f78a49da52bba54908b7505f617b036d208b3d347508fd5ec","device":"78f562a53c16db4c","findings":[],"hash":"f8fd26ea395ddcca7794e77c101f5b497e6431195b0fbf28ef515219aea217e7","nodeId":"61c5d82a-0f17-4e8b-ad1e-3e502b75047d","outcome":"completed","summary":"Updated only [launch.json](/home/imd-worker-551/.identitymd/work/dc4e3a21-8e12-44c3-9a6d-ae180f5c8c60/61c5d82a-0f17-4e8b-ad1e-3e502b75047d/launch.json), preserving the exact two-contract order and arguments and clarifying deployment notes.\n\nValidation passed: schema, compiled constructor ABIs, four manifest checks, and `forge build`. `forge test --isolate`: **196 passed, 0 failed, 5 skipped** because `MAINNET_RPC_URL` is unset.","treeHash":"e3d58c7cd9063bb0613a8c798ba7d6e7d8445b7c","usage":{"cachedInputTokens":1549440,"inputTokens":68195,"model":"gpt-6-astra","outputTokens":8383,"runtime":"codex","turns":10,"wallClockMs":603107}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"d00f790fc692b1a4","findings":[{"citation":"resolved","description":"Execution trace, across transactions. buyFloorWithEth calls _buyGate() first (line 869), which writes lastEthBuyBlock = block.number before any size check; the only lower bound on ethIn is `ethIn == 0`. A 1 wei buy passes every check (on the live pools 1 wei of ETH still yields a nonzero IMD6900 amount through both hops, so _credit's `got == 0` check does not stop it), and the honest buy of the same block (the arb bot's 0.25 ETH) then reverts TooSoon. Repeated at the top of every block the griefer pays only gas and the frens contract's ETH (hook fee slice, marketplace royalties, unwrapped WETH) is never bought into the reserve. The $IMD path was protected against exactly this (`test_DustEthBuyCannotTakeTheMintsTurn`: a dust ETH buy cannot take the mints' $IMD turn, because the two paces are separate), but the ETH pace itself has no dust protection. Consequence while the ETH stays pending: quote() and buyTreasury() charge minters and treasury buyers for the pending ETH (at POOL4 spot, the retained MEDIUM a99c13...), while recycle() pays only reserve/out and floorImd/out (floorPerFren, line 805), so every holder who recycles during the griefing forfeits their share of the pending ETH to the holders who remain (a holder-griefer collects it when they stop), and the spot-valued pending balance that the retained MEDIUM depends on is kept large instead of being drained at 0.25 ETH a block. No direct theft; a griefing race that costs the griefer gas and 1 wei a block. Fix (changes src/frens, so by the requester's rule record only): require ethIn >= a dust floor (e.g. maxEthPerBuy / 100, or min(address(this).balance, maxEthPerBuy) so a buy must take all that is affordable), or let a buy below the threshold through without consuming the block turn.","line":870,"path":"src/frens/IMD6900Frens.sol","reproduction":"State: 2 frens out, swapper wired, frens holds 10 ether of fee ETH, buyDelayBlocks = 1, maxEthPerBuy = 0.25 ether. For each of 50 blocks: griefer calls buyFloorWithEth(1, 0) first (succeeds: lastEthBuyBlock = block.number, reserve grows by dust); the honest bot's buyFloorWithEth(0.25 ether, 0) in the same block reverts TooSoon. Expected: the 10 ETH is bought into the reserve at 0.25 ETH a block (40 blocks). Actual: after 50 blocks address(frens).balance == 10 ether - 50 wei and the reserve moved by less than 1e12 wei of IMD6900; meanwhile floorPerFren() = (reserve/2, floorImd/2) excludes the 10 ETH that quote(1) charges for. Scratch test test/scratch/DustEthTurn.t.sol (unit mocks from test/frens/IMD6900Frens.t.sol) passes on this code, i.e. the behaviour exists.","severity":"low","snippet":"        if (ethIn == 0 || ethIn > address(this).balance || ethIn > maxEthPerBuy) revert Cap();","title":"buyFloorWithEth has no minimum size: a 1-wei buy at the top of each block takes the fee ETH's block turn, so royalties can be kept pending indefinitely for gas only"},{"citation":"resolved","description":"Periphery: view/write divergence in a helper the site and keeper are told to use (natspec lines 99 and 104: 'what buyFloor would get', 'what buyFloorWithEth would get'). The quote path in unlockCallback swaps the whole input with the extreme sqrtPriceLimit (line 138 for the POOL4 leg, line 149 for the pair leg) and ignores maxImdPerBuy / maxEthPerBuy. The real buys go through FrenSwapper, whose swaps stop at _limit(key, dir, pairMoveBips()) (half the pair hook's fee; fee() is 690 bips live, so 345 bips) and _limit(pool4, true, POOL4_MOVE_BIPS) (50 bips), returning the unspent input to the frens contract (FrenSwapper.sol lines 212, 231-234, 249, 272-273). On the live pools the limits bind at a small fraction of a default-size buy, so the quote is not a conservative estimate but far above what the buy can ever return. Impact: anyone using the quote as minOut for buyFloor / buyFloorWithEth (the bot the README says runs them) has every buy fail (buyFloor: the swapper's Short is caught and surfaces as Cap; buyFloorWithEth: Short); dashboards that use it to show the floor's expected growth are wrong by an order of magnitude. No loss of funds. Fix (src change, record only): run the quote with the same price limits as FrenSwapper (pairMoveBips(), POOL4_MOVE_BIPS) and return the input actually spent, or document that the quotes are unlimited and must not be used as minOut.","line":149,"path":"src/frens/FrenMinter.sol","reproduction":"Mainnet fork at block 26150640 (MAINNET_RPC_URL), fresh IMD6900Frens + FrenSwapper + FrenMinter with the FrensPlan addresses, swapper wired. (a) minter.quoteFloor(50e18) returns imd6900Out = 5589160.645140992522932970e18. Give the frens 50e18 $IMD with floorImd = 50e18 and call frens.buyFloor(5589160645140992522932970): reverts Cap (the swapper reverted Short inside the try/catch). frens.buyFloor(0) then succeeds and gets 306576.375232454770069472e18 IMD6900 for 1.934 $IMD, leaving floorImd = 48.065680272752549103e18 waiting at the 3.45% limit. (b) minter.quoteFloorEth(0.25e18) returns 7423123.860936437499339958e18. Give the frens 0.25 ether and call frens.buyFloorWithEth(0.25 ether, 7423123860936437499339958): reverts FrenSwapper.Short. buyFloorWithEth(0.25 ether, 0) gets 306576.375232454770069472e18 and 0.191239184558031743 ETH comes back unspent (POOL4's 0.5% limit). Expected: the quote matches what the buy gets (natspec). Scratch fork test test/scratch/QuoteDivergence.fork.t.sol passes on this code.","severity":"low","snippet":"                    sqrtPriceLimitX96: imdIs0 ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1","title":"FrenMinter.quoteFloor / quoteFloorEth simulate an unlimited swap, while buyFloor / buyFloorWithEth stop at the half-fee price limit: a minOut taken from the quote makes the real buy revert, and the qu"},{"citation":"resolved","description":"Execution trace, mid-operation config mutation. approveJob stores only the nonce and deadline of the approved payment; the permit digest is a function of (nonce, deadline, imd, JOB_PRICE, x402Proxy, imdPayTo). _permit2Digest re-reads imdPayTo (line 760) at clearing time. If the governor changes the payee with setRoles while a payment is in flight and that payment then lapses, _reclaimLapsed -> _unapprove computes the digest for the new payee and writes false to a key that was never true; approvedDigest[oldDigest] stays true for ever while the allowance is reduced and the job money is re-used. No funds can move: the digest binds the deadline and Permit2 refuses an expired permit, and the allowance was taken back, so isValidSignature answering the ERC-1271 magic for an expired permit is inert. Residue only: the contract's statement 'ERC-1271: valid only for the payments approveJob approved' is no longer exact after a payee change. Fix (src change, record only): store the permit digest (or the payee) in the Request at approval and clear that stored digest in _unapprove.","line":740,"path":"src/frens/IMD6900Frens.sol","reproduction":"Unit mocks: requestMint(1); keeper approveJob(id, nonce 1, deadline = now + 600, q) -> returns permitDigest, isValidSignature(permitDigest) == 0x1626ba7e, allowance(frens, permit2) == 0.5e18. Governor setRoles(0, 0, payTo2). warp(deadline + 1). reveal(id, ...) in full (or releaseLapsedJob(id)): allowance == 0 and jobBudget == 0 (the 0.50 went to the floor) as intended, but isValidSignature(permitDigest) is still 0x1626ba7e. Expected: the reclaimed payment's digest is no longer approved. Scratch test test/scratch/StaleDigest.t.sol passes on this code.","severity":"info","snippet":"        approvedDigest[_permit2Digest(r.jobNonce, r.jobDeadline)] = false;","title":"setRoles(imdPayTo) while a job payment is approved: _unapprove recomputes the Permit2 digest with the new payee, clears a key that was never set and leaves the old digest approved after the reclaim"}],"hash":"fd074076eaa9ed4ec998351d8006e8af51e0523f66a4781a3717e9589245b5d4","nodeId":"ada2ddb0-2aed-4b72-8155-f81b2e890fb4","outcome":"completed","summary":"Review complete. The findings file is written and validated, every one of the 55 verifier entry points has a coverage row, and the working tree is clean apart from the findings file.\n\n**Outcome: no HIGH or CRITICAL defect in the control flow.** The collection's money paths (mint pricing, floor buys, recycle, treasury, job payments, reveals) trace correctly from entry to final state, and the repository's 20 fork tests plus the 6 placement fork tests all pass against live mainnet state through a public RPC. I recorded three reproducible lower-severity items in `.imd-findings.json`:\n\n- **LOW: `buyFloorWithEth` has no minimum size.** A 1-wei buy at the top of each block consumes the fee ETH's block turn, so the honest 0.25 ETH buy reverts TooSoon every block and royalties can be kept pending for gas only. While pending, mints and treasury buys are charged for that ETH but recyclers forfeit their share. The $IMD path was protected against this; the ETH path was not. Reproduced with the unit mocks.\n- **LOW: `FrenMinter.quoteFloor` and `quoteFloorEth` quote an unlimited swap** while the real buys stop at the half-fee limit. On the live pools the quote for 50 $IMD reports 5.59M IMD6900 against 306k actually bought, and the ETH quote is off by about 24x. A minOut taken from the quote makes every real buy revert. Reproduced on a mainnet fork.\n- **INFO: changing `imdPayTo` with a job payment in flight** leaves the old Permit2 digest approved after the reclaim because `_unapprove` recomputes the digest with the new payee. Inert, since Permit2 enforces the deadline and the allowance is taken back.\n\nLive checks that bear on the launch, all consistent with the README: the planned addresses hold no code yet, the real $IMD gives Permit2 a zero (not infinite) allowance so the books cannot overflow, and the timelock batch queued on-chain targets the re-mined collection and swapper addresses. The retained MEDIUM/LOW items from the earlier audit were confirmed as still present and not duplicated. I did not read the renderer or the external hook and x402 proxy code; those are marked unreached.","treeHash":null,"usage":{"cachedInputTokens":3407027,"inputTokens":610,"model":"claude-fable-5-1","outputTokens":97667,"runtime":"claude","turns":56,"wallClockMs":1623226}}],"verification":[{"checks":[{"durationMs":166246,"exitCode":0,"name":"build","output":"Compiling 98 files with Solc 0.8.30\nSolc 0.8.30 finished in 166.04s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> test/FrensPlacement.t.sol:786:9:\n    |\n786 |         uint256 s = _find(j, key, 0) + key.length;\n    |         ^^^^^^^^^\nNote: The shadowed declaration is here:\n   --> test/FrensPlacement.t.sol:477:5:\n    |\n477 |     DeployFrens s;\n    |     ^^^^^^^^^^^^^\n\nWarning (2018): Function state mutability can be restricted to view\n   --> test/FrensLaunchAdaptation.t.sol:199:5:\n    |\n199 |     function test_PlacedReportsTheLaunchModules() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/frens/IMD6900Frens.sol:964:66\n    │\n964 │         return (ITransferValidator721.validateTransfer.selector, true);\n    │                                                                  ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenWorkerGate.sol:58:68\n   │\n58 │     constructor(address owner_, address frens_, address identity_, address strategy_) {\n   │                                                                    ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenWorkerGate.sol:58:33\n   │\n58 │     constructor(address owner_, address frens_, address identity_, address strategy_) {\n   │                                 ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[calls-loop]: external call inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:76:30\n   │\n76 │             address holder = identity.ownerOf(id);\n   │                              ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:77:78\n   │\n77 │             if (holder != msg.sender && (holder != strategy || msg.sender != ISeatStrategyMin(strategy).seatOperator()))\n   │                                                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:79:17\n   │\n79 │                 revert NotYours(id);\n   │                 ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:81:30\n   │\n81 │             if (claimed[id]) revert AlreadyClaimed(id);\n   │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:73:9\n   │\n73 │         address imd6900_,\n   │         ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:74:9\n   │\n74 │         address frens_,\n   │         ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:72:9\n   │\n72 │         address imd_,\n   │         ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:76:9\n   │\n76 │         address imdPoolHook_\n   │         ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:75:9\n   │\n75 │         address pairHook_,\n   │         ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/FrenWorkerGate.sol:105:9\n    │\n105 │         emit WlRootSet(root);\n    │         ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-events-access-control]: `keeper` is changed without an event but is used for access control\n     ╭▸ src/frens/IMD6900Frens.sol:1009:36\n     │\n1009 │         if (keeper_ != address(0)) keeper = keeper_;\n     │                                    ━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `governor` is changed without an event but is used for access control\n     ╭▸ src/frens/IMD6900Frens.sol:1075:9\n     │\n1075 │         governor = g;\n     │         ━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/FrensPlacement.sol:54:13\n   │\n54 │ ┏             abi.encodePacked(\n55 │ ┃                 FrensCode.FRENS,\n56 │ ┃                 abi.encode(\n57 │ ┃                     FrensPlan.OWNER,\n   ‡ ┃\n68 │ ┃             )\n   │ ┗━━━━━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/FrensPlacement.sol:93:29\n   │\n93 │           bytes memory init = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n94 │ ┃             FrensCode.SWAPPER,\n95 │ ┃             abi.encode(\n96 │ ┃                 FrensPlan.POOL_MANAGER, FrensPlan.IMD, FrensPlan.IMD6900, f, FrensPlan.PAIR_HOOK, FrensPlan.POOL4_HOOK\n97 │ ┃             )\n98 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/FrensPlacement.sol:109:13\n    │\n109 │ ┏             abi.encodePacked(\n110 │ ┃                 FrensCode.MINTER, abi.encode(FrensPlan.POOL_MANAGER, f, FrensPlan.POOL4_HOOK, FrensPlan.PAIR_HOOK)\n111 │ ┃             )\n    │ ┗━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/FrensPlacement.sol:115:13\n    │\n115 │             abi.encodePacked(FrensCode.GATE, abi.encode(FrensPlan.OWNER, f, FrensPlan.IDENTITY, FrensPlan.IMD6900))\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:177:16\n    │\n177 │         return uint160(zeroForOne ? uint256(p) * 1e18 / f : uint256(p) * f / 1e18);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/frens/WorkerFrensRenderer.sol:59:29\n   │\n59 │           bytes memory json = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n60 │ ┃             '{\"name\":\"Worker Fren #',\n61 │ ┃             tokenId.toString(),\n62 │ ┃             '\",\"description\":\"',\n   ‡ ┃\n68 │ ┃             \"}\"\n69 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:77\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                                                             ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/frens/WorkerFrensRenderer.sol:75:29\n   │\n75 │           bytes memory json = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n76 │ ┃             '{\"name\":\"Worker Fren #',\n77 │ ┃             tokenId.toString(),\n78 │ ┃             '\",\"description\":\"',\n   ‡ ┃\n82 │ ┃             '\",\"attributes\":[{\"trait_type\":\"Status\",\"value\":\"Unrevealed\"}]}'\n83 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:39\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                       ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:55\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                                       ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:211:35\n    │\n211 │                 amountSpecified: -int256(amountIn),\n    │                                   ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/WorkerFrensRenderer.sol:125:54\n    │\n125 │                     bytes memory out = new bytes(j - start);\n    │                                                      ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/WorkerFrensRenderer.sol:124:21\n    │\n124 │                 if (k == i) {\n    │                     ━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:219:23\n    │\n219 │         uint256 pay = uint256(uint128(-paid));\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:219:31\n    │\n219 │         uint256 pay = uint256(uint128(-paid));\n    │                               ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/frens/WorkerFrensRenderer.sol:158:17\n    │\n158 │ ┏                 abi.encodePacked(\n159 │ ┃                     '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 84 84\" width=\"840\" height=\"840\">',\n160 │ ┃                     '<image width=\"84\" height=\"',\n161 │ ┃                     (N * PENDING_FRAMES).toString(),\n    ‡ ┃\n165 │ ┃                     'dur=\"1.3s\" calcMode=\"discrete\" repeatCount=\"indefinite\"/></image></svg>'\n166 │ ┃                 )\n    │ ┗━━━━━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:221:13\n    │\n221 │             poolManager.settle{value: pay}();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:225:13\n    │\n225 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:227:23\n    │\n227 │         uint256 out = uint256(uint128(got));\n    │                       ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:227:31\n    │\n227 │         uint256 out = uint256(uint128(got));\n    │                               ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:118:35\n    │\n118 │             if (r.length == 68 && bytes4(r) == Quoted.selector) {\n    │                                   ━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:208:35\n    │\n208 │                 bytes1 v = bytes1(uint8(l * 3 / 5));\n    │                                   ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:248:35\n    │\n248 │                 amountSpecified: -int256(ethIn),\n    │                                   ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:253:27\n    │\n253 │         uint256 ethPaid = uint256(uint128(-d1.amount0()));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:253:35\n    │\n253 │         uint256 ethPaid = uint256(uint128(-d1.amount0()));\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:254:26\n    │\n254 │         uint256 imdGot = uint256(uint128(d1.amount1()));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:254:34\n    │\n254 │         uint256 imdGot = uint256(uint128(d1.amount1()));\n    │                                  ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:221:42\n    │\n221 │         _draw(cv, _entry(Art.BG0 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                          ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:221:62\n    │\n221 │         _draw(cv, _entry(Art.BG0 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                              ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:261:35\n    │\n261 │                 amountSpecified: -int256(imdGot),\n    │                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:267:27\n    │\n267 │         uint256 imdPaid = uint256(uint128(-paid));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:267:35\n    │\n267 │         uint256 imdPaid = uint256(uint128(-paid));\n    │                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:268:23\n    │\n268 │         uint256 out = uint256(uint128(got));\n    │                       ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:268:31\n    │\n268 │         uint256 out = uint256(uint128(got));\n    │                               ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:253:74\n    │\n253 │         if (ch > 2 || face >= FACES || coat > 2 || shirt > 5 || hat > 2) revert Missing();\n    │                                                                          ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:350:31\n    │\n350 │         if (i >= Art.ENTRIES) revert Missing();\n    │                               ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:270:9\n    │\n270 │         poolManager.settle{value: ethPaid}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:356:72\n    │\n356 │         if (p.codehash != bytes32(_hex(Art.CHUNK_HASHES, c * 64, 64))) revert BadArt();\n    │                                                                        ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:288:25\n    │\n288 │                         revert(28, 4)\n    │                         ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:232:56\n    │\n232 │         _draw(back, _entry(Art.BG0 + Art.PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                        ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:232:76\n    │\n232 │         _draw(back, _entry(Art.BG0 + Art.PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                                            ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:138:79\n    │\n138 │ …     imdKey(), SwapParams({zeroForOne: true, amountSpecified: -int256(amount), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1}), \"\"\n    │                                                                 ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:239:23\n    │\n239 │             _fren(cv, uint24(combo));\n    │                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:140:25\n    │\n140 │                 imdIn = uint256(uint128(e.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:140:33\n    │\n140 │                 imdIn = uint256(uint128(e.amount1()));\n    │                                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:148:39\n    │\n148 │                     amountSpecified: -int256(imdIn),\n    │                                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:153:35\n    │\n153 │             revert Quoted(amount, uint256(uint128(imdIs0 ? p.amount1() : p.amount0())));\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:153:43\n    │\n153 │             revert Quoted(amount, uint256(uint128(imdIs0 ? p.amount1() : p.amount0())));\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:261:22\n    │\n261 │         int256 dx = -int256(CX);\n    │                      ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:262:22\n    │\n262 │         int256 dy = -int256(CY);\n    │                      ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:158:70\n    │\n158 │ …     imdKey(), SwapParams({zeroForOne: true, amountSpecified: int256(imdOut), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1}), \"\"\n    │                                                                ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:160:25\n    │\n160 │         uint256 ethIn = uint256(uint128(-d.amount0()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:160:33\n    │\n160 │         uint256 ethIn = uint256(uint128(-d.amount0()));\n    │                                 ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:161:23\n    │\n161 │         uint256 got = uint256(uint128(d.amount1()));\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:161:31\n    │\n161 │         uint256 got = uint256(uint128(d.amount1()));\n    │                               ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenMinter.sol:165:9\n    │\n165 │         poolManager.settle{value: ethIn}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:321:56\n    │\n321 │         for (uint256 i; i < n; ++i) b[at + i] = bytes1(uint8(v >> (8 * i)));\n    │                                                        ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/FrenMinter.sol:183:29\n    │\n183 │             if (!withEth && cost > a) continue;\n    │                             ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:178:17\n    │\n178 │             try frens.quote(n) returns (uint256 q) {\n    │                 ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:22\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                      ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:51\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                                                   ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:84\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                                                                                    ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:184:22\n    │\n184 │             if (n <= frens.maxMint(_tier(withEth ? a : a - cost, b, c))) return n;\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/frens/WorkerFrensRenderer.sol:370:49\n    │\n370 │             extcodecopy(p, add(raw, 32), add(1, mul(first, 33)), mul(frames, 33))\n    │                                                 ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/FrenWorkerGate.sol:135:9\n    │\n135 │         emit PublicOpened(workerMinted);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:314:9\n    │\n314 │         address imd6900_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:315:9\n    │\n315 │         address identity_,\n    │         ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:316:9\n    │\n316 │         address permit2_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:321:9\n    │\n321 │         address priceTable_\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:312:9\n    │\n312 │         address owner_,\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:317:9\n    │\n317 │         address x402Proxy_,\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:313:9\n    │\n313 │         address imd_,\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:318:9\n    │\n318 │         address imdPayTo_,\n    │         ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:319:9\n    │\n319 │         address keeper_,\n    │         ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:320:9\n    │\n320 │         address relayer_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:335:9\n    │\n335 │         emit ICreatorToken.TransferValidatorUpdated(address(0), DEFAULT_TRANSFER_VALIDATOR);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:372:16\n    │\n372 │         return uint8(table >> (8 * trait));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:377:16\n    │\n377 │         return uint8((uint256(combo) >> _shift(trait)) & ((1 << _bits(trait)) - 1));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:386:38\n    │\n386 │             if (minTiers[v] > TIERS) revert BadTraits();\n    │                                      ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/IMD6900Frens.sol:408:13\n    │\n408 │             chars += _rules[v].cap;\n    │             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:371:30\n    │\n371 │         if (trait >= TRAITS) revert BadTraits();\n    │                              ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-no-eth]: external call can be reentered before `floorImd` is updated\n    ╭▸ src/frens/IMD6900Frens.sol:849:13\n    │\n849 │         try IFrenSwapper(swapper).imdToImd6900{gas: FLOOR_BUY_GAS}(imdIn, minOut, address(this)) {}\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:985:9\n    │\n985 │         ITransferValidator721(validator).validateTransfer(msg.sender, from, to, id);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:561:9\n    │\n561 │         emit FrensMinted(requestId, minter, first, count, tier, paid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:859:9\n    │\n859 │         emit FloorBought(spent, 0, got, reserve);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:979:13\n    │\n979 │             revert LastFrenOut();\n    │             ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:557:82\n    │\n557 │         requests[requestId] = Request(minter, tier, lowTier, false, count, 0, 1, uint32(first), 0, 0);\n    │                                                                                  ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/IMD6900Frens.sol:623:38\n    │\n623 │             h = keccak256(abi.encode(h, combos[i]));\n    │                                      ━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:615:13\n    │\n615 │         if (block.timestamp > deadline) revert BadVoucher();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:643:13\n    │\n643 │             emit Revealed(requestId, tokenId, combo, jobId, outputHash);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:632:28\n    │\n632 │             if (code != 0) revert BadCombo(code);\n    │                            ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:634:90\n    │\n634 │             if (valueOf(combo, CHARACTER) == 0 && pepe.minted + openLowTier >= pepe.cap) revert BadCombo(3);\n    │                                                                                          ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:627:22\n    │\n627 │         r.revealed = uint8(upTo);\n    │                      ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:665:53\n    │\n665 │         if (!r.jobApproved || _spent(r.jobNonce) || block.timestamp <= r.jobDeadline) return false;\n    │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:689:9\n    │\n689 │         emit JobPaid(requestId, msg.sender);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:715:13\n    │\n715 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours || _spent(nonce)) revert BadJob();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:715:53\n    │\n715 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours || _spent(nonce)) revert BadJob();\n    │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:731:9\n    │\n731 │         emit JobApproved(requestId, nonce, deadline, permitDigest, quoteDigest);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:722:25\n    │\n722 │         r.jobDeadline = uint40(deadline);\n    │                         ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint40' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/frens/IMD6900Frens.sol:873:9\n    │\n873 │         IFrenSwapper(swapper).ethToImd6900{value: ethIn}(minOut, address(this));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:895:9\n    │\n895 │         emit FloorBought(imdIn, ethIn, got, reserve);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/IMD6900Frens.sol:873:9\n    │\n873 │         IFrenSwapper(swapper).ethToImd6900{value: ethIn}(minOut, address(this));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:888:9\n    │\n888 │         emit WethUnwrapped(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:911:9\n    │\n911 │         emit Recycled(tokenId, msg.sender, paid, imdPaid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:934:9\n    │\n934 │         emit TreasuryBought(tokenId, msg.sender, paid, imdPaid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:955:35\n    │\n955 │     function setTransferValidator(address validator) external onlyCurator {\n    │                                   ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:957:9\n    │\n957 │         emit ICreatorToken.TransferValidatorUpdated(getTransferValidator(), validator);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n     ╭▸ src/frens/IMD6900Frens.sol:1016:26\n     │\n1016 │     function setRenderer(address r) external onlyCurator {\n     │                          ━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1019:9\n     │\n1019 │         emit Setting(\"renderer\", 0, r);\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1025:9\n     │\n1025 │         emit Setting(\"artFrozen\", 1, renderer);\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n     ╭▸ src/frens/IMD6900Frens.sol:1041:17\n     │\n1041 │                 revert BadTraits();\n     │                 ━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n     ╭▸ src/frens/IMD6900Frens.sol:1053:84\n     │\n1053 │             if (m[i] == 0 || m[i] > MAX_PER_REQUEST || (i > 0 && m[i] < m[i - 1])) revert BadTraits();\n     │                                                                                    ━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1069:9\n     │\n1069 │         emit Setting(\"royalty\", bps, address(0));\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:530:21\n    │\n530 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n531 │         bytes memory sig = _sign(frens, relayerKey, id, c, d);\n532 │         vm.warp(d + 1);\n    │         ────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:540:17\n    │\n540 │         vm.warp(block.timestamp + 30 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:685:50\n    │\n685 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:686:17\n    │\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:699:17\n    │\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:721:50\n    │\n721 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:722:17\n    │\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:737:50\n    │\n737 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:296:21\n    │\n296 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:742:17\n    │\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:753:17\n    │\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:760:17\n    │\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/FrensPlacedInvariants.t.sol:379:17\n    │\n379 │         vm.roll(block.number + bound(blocks, 0, 2));\n    │         ────────━━━━━━━━━━━━─────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/FrensPlacedInvariants.t.sol:414:17\n    │\n414 │         vm.roll(block.number + bound(blocks, 0, 2));\n    │         ────────━━━━━━━━━━━━─────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":6483,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/frens/IMD6900FrensValidator.fork.t.sol:IMD6900FrensValidatorForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 251.88µs (0.00ns CPU time)\n\nRan 1 test for test/frens/FrenSwapper.fork.t.sol:FrenSwapperForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 317.82µs (0.00ns CPU time)\n\nRan 1 test for test/frens/IMD6900FrensPermit2.fork.t.sol:IMD6900FrensPermit2ForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 554.06µs (0.00ns CPU time)\n\nRan 1 test for test/frens/FrenMinter.fork.t.sol:FrenMinterForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 786.81µs (0.00ns CPU time)\n\nRan 1 test for test/FrensPlacement.t.sol:FrensPlacementForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.34ms (0.00ns CPU time)\n\nRan 1 test for test/FrensResidualRisks.t.sol:FrensAverageResidualRiskTest\n[PASS] test_Risk_DustFirstPinsAverageAcrossOneHundredBlocks() (gas: 9697080)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.63ms (2.50ms CPU time)\n\nRan 6 tests for test/frens/FrenSwapperAverage.t.sol:FrenSwapperAverageTest\n[PASS] test_AFullBuyMovesItOneStep() (gas: 103606)\n[PASS] test_ASmallBuyMovesItInProportion() (gas: 52842)\n[PASS] test_DustMovesItNextToNothing() (gas: 7606082)\n[PASS] test_FloorRateIsTheLowerReading() (gas: 17282)\n[PASS] test_MovesDownToo() (gas: 52658)\n[PASS] test_OnceABlock() (gas: 91443)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 3.26ms (2.88ms CPU time)\n\nRan 3 tests for test/FrensResidualRisks.t.sol:FrensResidualRisksTest\n[PASS] test_Risk_DuplicatePendingNonceStrandsOnePayment() (gas: 1554494)\n[PASS] test_Risk_PendingEthSpotPushDilutesExistingHolders() (gas: 5288577)\n[PASS] test_Risk_UnwiringSwapperAllowsCheapReserveCapture() (gas: 992804)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 4.73ms (2.09ms CPU time)\n\nRan 9 tests for test/frens/IMD6900FrensValidator.t.sol:IMD6900FrensValidatorTest\n[PASS] test_AuthorizationDoesNotOutliveTheSale() (gas: 193293)\n[PASS] test_AuthorizationFromTwoTopLevelCallsIsLost() (gas: 86867)\nLogs:\n  sale authorized in a separate top-level call: blocked\n\n[PASS] test_AuthorizationIsForThatFrenOnly() (gas: 394696)\n[PASS] test_AuthorizationIsForThatOperatorOnly() (gas: 479809)\n[PASS] test_ConduitAloneIsBlocked() (gas: 53267)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 79352)\n[PASS] test_HolderSendsItHerself() (gas: 87060)\n[PASS] test_OnlyTheAuthorizerAuthorizes() (gas: 74587)\n[PASS] test_ZoneAuthorizedSaleGoesThroughInOneFrame() (gas: 124594)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 7.91ms (1.04ms CPU time)\n\nRan 13 tests for test/FrensRevisionRisks.t.sol:FrensRevisionRisksTest\n[PASS] test_Risk_DuplicatePendingNonceStrandsOnePayment() (gas: 1554719)\n[PASS] test_Risk_DustEthTakesFiftyBlockTurns() (gas: 6374674)\n[PASS] test_Risk_EthMintPushesItsOwnPendingEthPrice() (gas: 5152892)\nLogs:\n  shown IMD: 1587628858579375267300\n  paid IMD: 979046693648650397080\n  refunded IMD: 608582164930724870220\n\n[PASS] test_Risk_FloorBoundJobCostIsSocialised() (gas: 3564025)\n[PASS] test_Risk_FloorQuotesUseExtremeLimits() (gas: 6173436)\n[PASS] test_Risk_FloorViewOmitsJobRefund() (gas: 857288)\n[PASS] test_Risk_PayeeChangeLeavesExpiredPermitDigest() (gas: 742744)\n[PASS] test_Risk_PendingEthSpotPushDilutesExistingHolders() (gas: 5288831)\n[PASS] test_Risk_PushMintUnpushHasPositiveMarkedValue() (gas: 6578235)\nLogs:\n  round trip ETH cost: 135188802651085725\n  marked IMD surplus: 106904119598384566859\n\n[PASS] test_Risk_RecycleOmitsPendingEthPricedByTreasury() (gas: 1093066)\n[PASS] test_Risk_StrangerSpendsWorkersCredit() (gas: 1493190)\n[PASS] test_Risk_UnwiringSwapperAllowsCheapReserveCapture() (gas: 992877)\n[PASS] test_Risk_ZeroOrOneFeeLimitEqualsCurrentPrice() (gas: 6176076)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 8.07ms (8.68ms CPU time)\n\nRan 16 tests for test/FrensLaunchAdaptation.t.sol:FrensLaunchAdaptationTest\n[PASS] test_BootstrapThenOpenAndResume() (gas: 2315891)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\n[PASS] test_FirstFrensBeforeFeesPreventsPublicFloorCapture() (gas: 3435830)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_FirstFrensRequiresPausedBuysAndNonzeroCount() (gas: 1785691)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Fix_SpentNonceIsRefused() (gas: 2311018)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeRefuseAnOwnerlessFloor() (gas: 1828783)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeRejectChangedWiring() (gas: 2680591)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeWarnWhenAverageLags() (gas: 4784847)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  WARNING: the swapper's average is more than 2x off the pool's price\n  WARNING: the swapper's average is more than 2x off the pool's price\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\n[PASS] test_PlacedRejectsMissingOrWrongModules() (gas: 126575)\n[PASS] test_PlacedRejectsWrongCollectionAndForeignDependencies() (gas: 65561)\n[PASS] test_PlacedReportsTheLaunchModules() (gas: 51824)\n[PASS] test_ResumeRequiresDistributorAndActualSwapperExemption() (gas: 2164855)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Risk_FirstMintCapturesExistingFloorIfRunbookIsBypassed() (gas: 3054450)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Risk_LaunchBlockPushSeedsSwapperAndSetupRejectsAfterPriceReturns() (gas: 2572174)\n[PASS] test_Risk_UnsupportedRoyaltyTokenHasNoFloorRoute() (gas: 3781565)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_SetupPausesBeforeFirstMintEvenWhenAlreadyDistributor() (gas: 1815518)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_SetupUsesTheLaunchsReplacementSwapper() (gas: 6981779)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 12.41ms (9.79ms CPU time)\n\nRan 3 tests for test/FrensLaunchReview.t.sol:FrensFactoryReviewTest\n[PASS] test_Audit_ConstructorLeavesTheDocumentedTeamSetup() (gas: 25294534)\n[PASS] test_FreshChainCallsOnlyContractsCreatedByTheLaunch() (gas: 28696930)\n[PASS] test_ProtectedFactoryDeploysBothLaunchesInOrder() (gas: 25493481)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 15.59ms (12.39ms CPU time)\n\nRan 8 tests for test/FrensLaunchReview.t.sol:FrensAuditReviewTest\n[PASS] test_Audit_GovernorCanCloseMintAgain() (gas: 463670)\n[PASS] test_Audit_OwnerOrGovernorCanBlockAllPeerTransfers() (gas: 1379074)\n[PASS] test_Fix_LapsedJobApprovalIsReleasedByTheReveal() (gas: 1037000)\n[PASS] test_Fix_LastFrenOutStaysOutSoFeesHaveAnOwner() (gas: 1203616)\n[PASS] test_Fix_LaunchAdoptsASwapperSeededAtThePoolsPrice() (gas: 2008131)\n[PASS] test_Fix_LaunchRefusesTheWrongArt() (gas: 18811)\n[PASS] test_Fix_LaunchReplacesASwapperSeededOffASkewedPrice() (gas: 2745326)\n[PASS] test_Fix_LaunchsOwnSwapperStartsAtThePoolsPrice() (gas: 237014)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 27.53ms (4.54ms CPU time)\n\nRan 18 tests for test/frens/FrenWorkerGate.t.sol:FrenWorkerGateTest\n[PASS] test_ARefusedMintTakesNothing() (gas: 104954)\n[PASS] test_AnNftCountsOnce_whoeverHoldsItLater() (gas: 151129)\n[PASS] test_CreditsGoWhereTheHolderSays() (gas: 639698)\n[PASS] test_NoGateNoWindow() (gas: 442079)\n[PASS] test_OnlyFrensSpends() (gas: 32613)\n[PASS] test_OnlyTheHolderClaims() (gas: 67063)\n[PASS] test_OpenPublic() (gas: 528969)\n[PASS] test_OwnerMintsBeforeTheOpening() (gas: 2296619)\n[PASS] test_SeatOperatorClaimsTheStrategysNfts() (gas: 138514)\n[PASS] test_TheCreditIsTheMinters() (gas: 576375)\n[PASS] test_WindowClosesAfter420() (gas: 32492721)\n[PASS] test_WindowNeedsACreditPerFren() (gas: 714583)\n[PASS] test_WindowNeverOvershoots() (gas: 36198264)\n[PASS] test_WlAndWorkersShareThe420() (gas: 619410)\n[PASS] test_WlOnlyAsListed_once() (gas: 254028)\n[PASS] test_WlRaisedAmount_claimsTheDifference() (gas: 226858)\n[PASS] test_WlRootOnlyTheOwner_andNoRootNoWl() (gas: 62252)\n[PASS] test_WlWalletsMintInTheWindow_asMuchAsListed() (gas: 750975)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 78.55ms (9.44ms CPU time)\n\nRan 12 tests for test/FrensArtFraming.t.sol:FrensArtFramingTest\n[PASS] testFuzz_EntryPastTheIndexIsMissing(uint256) (runs: 256, μ: 13976, ~: 13943)\nLogs:\n  Bound result 1571160335840753621519211871272212879868819158337193\n\n[PASS] testFuzz_UnknownBackgroundIsMissing(uint256) (runs: 256, μ: 35607, ~: 35592)\nLogs:\n  Bound result 1480728434740302527548024551067683867461272045680188196814626140024713529382\n\n[PASS] test_ATamperedChunkDrawsNothing() (gas: 90530)\n[PASS] test_AttributesDoNotValidate() (gas: 369298)\n[PASS] test_ChunksAreFramedPushData() (gas: 2731712)\n[PASS] test_EveryValueHasAName() (gas: 15679313)\n[PASS] test_IndexHashesAreTheChunks() (gas: 788314)\n[PASS] test_IndexPointsInsideTheChunks() (gas: 525933)\n[PASS] test_LaunchLayersAreWellFormed() (gas: 46186857)\n[PASS] test_NoSuchFrenIsMissing() (gas: 4375022)\n[PASS] test_PalettesFitTogether() (gas: 13989817)\n[PASS] test_WithoutTheSwarmNothingDraws() (gas: 5947823)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 92.49ms (173.42ms CPU time)\n\nRan 17 tests for test/frens/FrenMinterOffline.t.sol:FrenMinterOfflineTest\n[PASS] testFuzz_BuyImdConservesPaymentAndRefund(uint256,uint256) (runs: 1000, μ: 330759, ~: 330902)\nLogs:\n  Bound result 8281817457324249984001\n  Bound result 18239\n\n[PASS] test_AllQuotesUnwindPoolAccountingAndTokenTransfers() (gas: 763581)\n[PASS] test_ClosedMintRollsBackAnAlreadySettledSwap() (gas: 641895)\n[PASS] test_EthMintSpendsCallersCreditAndKeepsTheirTier() (gas: 1038475)\n[PASS] test_ExcessOutputGoesBackToThePayer() (gas: 866146)\n[PASS] test_InvalidCountsDoNotLeavePaidSwapsBehind() (gas: 982596)\n[PASS] test_LastFrenAndSoldOutDoNotAcceptMoreEth() (gas: 62017818)\n[PASS] test_MinterCannotBorrowAnotherWalletsGateCredit() (gas: 703774)\n[PASS] test_OneWeiUnderfundingRollsBackAndExactPaymentSucceeds() (gas: 1190355)\n[PASS] test_OnlyPoolManagerCanEnterAnyCallbackMode() (gas: 396447)\n[PASS] test_PartialExactOutputCannotMintOrBuy() (gas: 716999)\n[PASS] test_QuoteAndMintMaxPayDifferByOneWei() (gas: 940830)\n[PASS] test_QuotesBubblePoolErrorsInsteadOfReturningZero() (gas: 719702)\n[PASS] test_RefundCallbackCannotReenterEitherValueEntryPoint() (gas: 1404208)\n[PASS] test_RefundRejectionRevertsMintAndBuyAtomically() (gas: 2302902)\n[PASS] test_TierFailureRestoresCreditsSwapAndPayment() (gas: 778989)\n[PASS] test_ZeroBuyRevertsAndOneWeiBuySettles() (gas: 657108)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 92.39ms (105.65ms CPU time)\n\nRan 86 tests for test/frens/IMD6900Frens.t.sol:IMD6900FrensTest\n[PASS] test_AMintNeverFailsOnTheSwap() (gas: 1003226)\n[PASS] test_BatchCannotRepeatAFren() (gas: 641744)\n[PASS] test_BatchCountIsOneTo69() (gas: 2185647)\n[PASS] test_BatchCountsTheCapsAsItGoes() (gas: 11780401)\n[PASS] test_BatchHoldsPepesForLowTiers() (gas: 12176238)\n[PASS] test_BatchPaysOneJob() (gas: 700213)\n[PASS] test_BatchRevealIsExactlyTheVoucher() (gas: 1011974)\n[PASS] test_BatchRevealsThemAll() (gas: 2256463)\n[PASS] test_BatchSoldOut() (gas: 90792483)\n[PASS] test_BuyTreasuryRespectsMaxPay() (gas: 1842004)\n[PASS] test_BuyingFromTheTreasuryRaisesTheFloor() (gas: 3101386)\n[PASS] test_CurveWhileAboveTheFloor() (gas: 1599825)\n[PASS] test_DearerImd6900RaisesTheFloorPrice() (gas: 2861964)\n[PASS] test_DustEthBuyCannotTakeTheMintsTurn() (gas: 540029)\n[PASS] test_EveryFrenIsOneOfAKind() (gas: 947080)\n[PASS] test_FailedJob_payAnother() (gas: 1173029)\n[PASS] test_FeeEthBuysTheFloor() (gas: 1708734)\n[PASS] test_FloorBuysAreAnyones_onePerBlock() (gas: 1893053)\n[PASS] test_FloorBuysAreCapped() (gas: 55694600)\n[PASS] test_FloorIsReserveOverFrensOut() (gas: 2761464)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 2145216)\n[PASS] test_FrenSentStraightToTheTreasuryCounts() (gas: 2660288)\n[PASS] test_IsACreatorToken() (gas: 41743)\n[PASS] test_JobPaymentIsPinnedToImdsPayee() (gas: 980756)\n[PASS] test_KeeperApprovesTheJobPaidFor() (gas: 657241)\n[PASS] test_KeeperCannotApproveASpentNonce() (gas: 490236)\n[PASS] test_KeeperCannotApproveLongLivedPayments() (gas: 434019)\n[PASS] test_LapsedPaymentIsUsedAgain() (gas: 719034)\n[PASS] test_LastFrenOutStaysOut() (gas: 1615217)\n[PASS] test_LaterPartsAgreeOnWhatIsRevealed() (gas: 1347339)\n[PASS] test_LowTierRequestsNeedAPepeLeft() (gas: 11708397)\n[PASS] test_MaxMintGrowsWithTheBag() (gas: 4682663)\n[PASS] test_MaxPayStopsAPriceThatMoved() (gas: 10063499)\n[PASS] test_MaxRequest() (gas: 9863897)\n[PASS] test_MintMintsAtOnce_unrevealed() (gas: 770449)\n[PASS] test_MintNeedsSealedTraits() (gas: 8623021)\n[PASS] test_MintNeverBelowTheFloor() (gas: 3425403)\n[PASS] test_MintNeverSkipsTheBuyForWantOfGas() (gas: 708870)\n[PASS] test_NoMintIntoTheTreasury() (gas: 1596022)\n[PASS] test_NoRequestForNobody() (gas: 61912)\n[PASS] test_NoTierInsideAFlashLoan() (gas: 687636)\n[PASS] test_NobodyElseApprovesJobs() (gas: 435635)\n[PASS] test_OneRevealPerFren() (gas: 717718)\n[PASS] test_OnlyHoldersRecycle() (gas: 1593666)\n[PASS] test_OnlyRealFrens() (gas: 743319)\n[PASS] test_OnlyTheOwnerSetsTheValidator() (gas: 1063173)\n[PASS] test_OwnerAdjustsTheArtUntilFrozen() (gas: 1555030)\n[PASS] test_PairRules() (gas: 875556)\n[PASS] test_PartsOnlyGoForward() (gas: 1035497)\n[PASS] test_PendingEthCountsInTheMintPrice() (gas: 2181876)\n[PASS] test_PriceRisesWithMints() (gas: 38125610)\n[PASS] test_PriceTableIsTheCurve() (gas: 36977950)\n[PASS] test_QuoteCountsUnsweptImd() (gas: 3216110)\n[PASS] test_RareTraitsNeedTheTier() (gas: 1365734)\n[PASS] test_RecyclePaysTheFloorAndKeepsIt() (gas: 2915603)\n[PASS] test_RecyclePaysTheWaitingImdToo() (gas: 2749360)\n[PASS] test_ReleaseLapsedJobAfterTheReveal() (gas: 2142099)\n[PASS] test_RequestMintFor() (gas: 1084060)\n[PASS] test_ReserveOnlyLeavesThroughRecycle() (gas: 2838128)\n[PASS] test_RevealFollowsTheFren() (gas: 749220)\n[PASS] test_RevealInParts() (gas: 9747514)\nLogs:\n  gas, minting 69: 2117887\n  gas, revealing 30: 3092845\n\n[PASS] test_RevealNeedsTheRelayersVoucher() (gas: 803651)\n[PASS] test_RevealReleasesALapsedPayment() (gas: 919103)\n[PASS] test_RevealShowsTheFren() (gas: 1192018)\n[PASS] test_RevealsWhenever() (gas: 661560)\n[PASS] test_RoyaltiesPayTheFloor() (gas: 1724930)\n[PASS] test_RoyaltyBpsIsCappedAndOwnerOnly() (gas: 109412)\n[PASS] test_RulesAreFrozenOnceSealed() (gas: 69580)\n[PASS] test_SealNeedsEveryTraitAndTheSupply() (gas: 10518169)\n[PASS] test_SetMaxMint() (gas: 178238)\n[PASS] test_ShortSwapReverts() (gas: 1046881)\n[PASS] test_SwapperMayPullOnlyTheBuy() (gas: 957490)\n[PASS] test_TheOwnerKeepsTheCollection_theGovernorTheMechanics() (gas: 488664)\n[PASS] test_TierCountsWhatIsLeftAfterPaying() (gas: 920004)\n[PASS] test_TierIsTheBestOfTheBag() (gas: 633505)\n[PASS] test_TierIsTheMints() (gas: 1350710)\n[PASS] test_TierThresholdsOwnerOnlyAndOrdered() (gas: 154928)\n[PASS] test_TradesGoPastTheValidator() (gas: 1346243)\n[PASS] test_TraitCapsHold() (gas: 25778206)\n[PASS] test_TreasuryRoundTripCostsTheFloor() (gas: 3295585)\n[PASS] test_UnneededRetryFeedsTheFloor() (gas: 1026116)\n[PASS] test_UnrevealedFrenSellsAtTheFloor() (gas: 1022775)\n[PASS] test_UnrevealedFrensShareTheFloor() (gas: 1466205)\n[PASS] test_UnsweptImdCountsInTheTreasuryPrice() (gas: 3367000)\n[PASS] test_UnusedJobFeedsTheFloor() (gas: 696981)\n[PASS] test_VouchersExpire() (gas: 472492)\nSuite result: ok. 86 passed; 0 failed; 0 skipped; finished in 93.10ms (204.57ms CPU time)\n\nRan 19 tests for test/FrensLaunchFailures.t.sol:FrensLaunchFailuresTest\n[PASS] testFuzz_CheckKnowsAFren(uint24,uint8) (runs: 512, μ: 1942185, ~: 1907859)\nLogs:\n  Bound result 0\n\n[PASS] testFuzz_OtherSaltsLandElsewhere(bytes32) (runs: 256, μ: 34990, ~: 34990)\n[PASS] testFuzz_QuoteIsTheCurveBeforeAnyoneHolds(uint256) (runs: 512, μ: 329372, ~: 333140)\nLogs:\n  Bound result 52\n\n[PASS] test_AnotherOwnerMovesTheFrens() (gas: 34259)\n[PASS] test_CollectionRefusesAShortPriceTable() (gas: 3059925)\n[PASS] test_CollectionWaitsForTheTeamWallet() (gas: 5941480)\n[PASS] test_EveryPriceIsTheTables() (gas: 27580866)\n[PASS] test_FreshChainLaunchesAreIndependent() (gas: 1351085)\n[PASS] test_LaunchContractsTakeNoEth() (gas: 687657)\n[PASS] test_ModulesFailOverCorruptedFrens() (gas: 97101)\n[PASS] test_ModulesNeedTheCollection() (gas: 106270)\n[PASS] test_NoLaunchActorHoldsARole() (gas: 4580578)\n[PASS] test_NoPricePastTheTable() (gas: 41219)\n[PASS] test_OtherSaltSchemeFailsTheLaunch() (gas: 140656)\n[PASS] test_RefusingDeployerFailsTheLaunch() (gas: 103369)\n[PASS] test_ScriptRefusesAnUnplacedChain() (gas: 14786192)\n[PASS] test_SecondLaunchReusesWhatIsPlaced() (gas: 1313404)\n[PASS] test_SilentDeployerFailsTheLaunch() (gas: 93234)\n[PASS] test_WrongArtFailsTheArtLaunch() (gas: 588910)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 93.22ms (278.53ms CPU time)\n\nRan 13 tests for test/FrensPlacement.t.sol:FrensPlacementTest\n[PASS] test_Attributes() (gas: 19594305)\n[PASS] test_CodeIsWhatTheSourcesBuild() (gas: 89696)\n[PASS] test_DeploysOnAFreshChain() (gas: 51811985)\n[PASS] test_EachLaunchFitsOneTransaction() (gas: 105863223)\nLogs:\n  the collection launch: gas, all in: 12451232\n  the art launch: gas, all in: 13605058\n\n[PASS] test_LandsWhereThePlanSays() (gas: 51923654)\n[PASS] test_NewArtReadsBackExactly() (gas: 14173584)\n[PASS] test_PassesTheAdmissionScan() (gas: 88302675)\n[PASS] test_PlanFollowsFromTheCode() (gas: 80750)\n[PASS] test_PlanMatchesTheScript() (gas: 14314057)\n[PASS] test_PriceTableIsTheCurve() (gas: 53105398)\n[PASS] test_RefusesOtherArt() (gas: 12971045)\n[PASS] test_SameAddressesWhoeverRunsIt() (gas: 102774643)\n[PASS] test_TakesWhatSomeonePlacedFirst() (gas: 52491481)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 99.04ms (176.92ms CPU time)\n\nRan 1 test for test/FrensSplitGas.t.sol:FrensSplitGasTest\n[PASS] test_CollectionFitsButLegacyFourContractBundleExceedsCap() (gas: 101750011)\nLogs:\n  legacy four-contract launch: 23877683\n  two-contract collection launch: 13494921\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 105.92ms (105.80ms CPU time)\n\nRan 6 tests for test/FrensPlacedInvariants.t.sol:FrensPlacedInvariantsTest\n[PASS]\nFrensPlacedInvariantsTest invariants:\n[PASS] invariant_CapsHold\n[PASS] invariant_FloorIsAShareOfTheWorld\n[PASS] invariant_FrensAreConserved\n[PASS] invariant_ImdIsTheBooks\n[PASS] invariant_ReserveIsExactlyWhatIsHere\n FrensPlacedInvariantsTest invariants (runs: 32, calls: 1920, reverts: 0)\n\n╭--------------------+-----------------+-------+---------+----------╮\n| Contract           | Selector        | Calls | Reverts | Discards |\n+===================================================================+\n| PlacedFrensHandler | approveJob      | 111   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyFloor        | 109   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyTreasury     | 121   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donate6900      | 126   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donateImd       | 120   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | feeEth          | 124   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mint            | 219   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mintThenRecycle | 151   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | recycle         | 132   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | retryJob        | 121   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | reveal          | 135   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setParams       | 119   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setRate         | 108   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | transfer        | 107   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | warp            | 117   | 0       | 0        |\n╰--------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 592574\n  Bound result 54\n  Bound result 3\n  Bound result 371636862\n  Bound result 46\n  Bound result 1\n  Bound result 32\n  Bound result 2\n  Bound result 17595\n  Bound result 120\n  Bound result 49\n  Bound result 2400\n  Bound result 20\n  Bound result 5480\n  Bound result 40303\n  Bound result 1000000000000000\n  Bound result 1\n  Bound result 148212888003115782712107\n  Bound result 61\n  Bound result 33\n  Bound result 0\n  Bound result 12\n  Bound result 17828\n  Bound result 82\n  Bound result 732386232825818755318\n  Bound result 2599\n  Bound result 352884\n  Bound result 57\n  Bound result 56\n  Bound result 794864\n  Bound result 34\n  Bound result 20007\n  Bound result 23527\n  Bound result 2\n  Bound result 118\n  Bound result 1045\n  Bound result 192\n  Bound result 14229\n  Bound result 0\n  Bound result 161\n  Bound result 120\n  Bound result 1000000\n  Bound result 1\n  Bound result 1386\n  Bound result 58\n  Bound result 43\n  Bound result 698720108745476053509\n  Bound result 180105514843744216\n  Bound result 2\n  Bound result 3542\n  Bound result 690\n  Bound result 183408413102368114737835\n  Bound result 1\n  Bound result 0\n  Bound result 173891586404266843158\n  Bound result 15300\n  Bound result 0\n\n[PASS] testFuzz_SpentNonceRejectsOnlyItsOwnBit(uint256) (runs: 1000, μ: 1024556, ~: 1024645)\nLogs:\n  Bound result 1\n\n[PASS] test_HandlerChecksBothTreasuryCapsAfterDonation() (gas: 2589281)\nLogs:\n  Bound result 2\n  Bound result 1\n  Bound result 1000000000000000000\n  Bound result 1\n\n[PASS] test_HandlerKeepsLastFrenOutAndRejectionsAreAtomic() (gas: 2510505)\nLogs:\n  Bound result 2\n  Bound result 1\n  Bound result 1000000000000000000\n  Bound result 2\n  Bound result 2\n\n[PASS] test_HandlerSequenceExercisesPaymentsRevealsAndFailures() (gas: 5940163)\nLogs:\n  Bound result 3\n  Bound result 1\n  Bound result 1\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000\n  Bound result 5\n  Bound result 0\n  Bound result 50000000000000000000\n  Bound result 250000000000000000\n  Bound result 70000\n  Bound result 1\n  Bound result 1000000000000000\n  Bound result 1\n  Bound result 1\n  Bound result 600\n  Bound result 600\n  Bound result 601\n  Bound result 600\n  Bound result 3\n  Bound result 3\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 2\n  Bound result 1\n  Bound result 2\n  Bound result 0\n\n[PASS] test_SpentNonceBitmapBoundaries() (gas: 4404356)\nLogs:\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 2.69s (2.78s CPU time)\n\nRan 2 tests for test/frens/FrenMinterInvariants.t.sol:FrenMinterInvariantsTest\n[PASS]\nFrenMinterInvariantsTest invariants:\n[PASS] invariant_AllPaymentsRemainAccountedFor\n[PASS] invariant_RequestsAndWorkerCreditsAreConserved\n FrenMinterInvariantsTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭------------------+------------+-------+---------+----------╮\n| Contract         | Selector   | Calls | Reverts | Discards |\n+============================================================+\n| EthMinterHandler | buy        | 2747  | 0       | 0        |\n|------------------+------------+-------+---------+----------|\n| EthMinterHandler | claimMore  | 2741  | 0       | 0        |\n|------------------+------------+-------+---------+----------|\n| EthMinterHandler | mint       | 2766  | 0       | 0        |\n|------------------+------------+-------+---------+----------|\n| EthMinterHandler | openPublic | 2668  | 0       | 0        |\n|------------------+------------+-------+---------+----------|\n| EthMinterHandler | quote      | 2817  | 0       | 0        |\n|------------------+------------+-------+---------+----------|\n| EthMinterHandler | rejectMint | 2645  | 0       | 0        |\n╰------------------+------------+-------+---------+----------╯\n\nLogs:\n  Bound result 28142141643098\n  Bound result 29\n  Bound result 2\n  Bound result 1\n  Bound result 805601874966449976\n  Bound result 791550\n  Bound result 759050004875885690\n  Bound result 1\n  Bound result 3757\n  Bound result 99330647109965027702\n  Bound result 268\n  Bound result 8145\n  Bound result 273\n  Bound result 66778362\n  Bound result 1413\n  Bound result 999999999999999311\n  Bound result 30022398180907\n  Bound result 10531\n  Bound result 1\n  Bound result 1046363172\n  Bound result 1\n  Bound result 161115112724407910\n  Bound result 9700\n  Bound result 999999999999999951\n  Bound result 144\n  Bound result 80\n  Bound result 3970\n  Bound result 50\n  Bound result 50652052636897038762\n  Bound result 5366\n  Bound result 234\n  Bound result 48\n  Bound result 312\n  Bound result 232\n  Bound result 1\n  Bound result 11975\n  Bound result 14651\n  Bound result 190100000000000000\n  Bound result 270\n  Bound result 255675530110458183989\n  Bound result 241914456187021771\n  Bound result 139\n  Bound result 999999999999999776\n  Bound result 48879\n  Bound result 82\n  Bound result 300000000000000000000\n  Bound result 418454228\n  Bound result 154\n  Bound result 1\n  Bound result 1\n  Bound result 16384\n  Bound result 1000\n  Bound result 84\n  Bound result 4\n  Bound result 24576\n  Bound result 1\n  Bound result 299\n  Bound result 25\n  Bound result 20496\n\n[PASS] test_HandlerSequenceExercisesValueAndFailurePaths() (gas: 5891735)\nLogs:\n  Bound result 1\n  Bound result 0\n  Bound result 1000000000000000000\n  Bound result 100\n  Bound result 200\n  Bound result 22\n  Bound result 1000000000000000000\n  Bound result 10000000000000000000\n  Bound result 69\n  Bound result 0\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 6.38s (6.38s CPU time)\n\nRan 22 test suites in 6.38s (9.82s CPU time): 233 tests passed, 0 failed, 5 skipped (238 total tests)\n","passed":true},{"durationMs":97,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"FrenMinter.buyImd(uint256)\",\"FrenMinter.mintWithEth(uint8,uint256)\",\"FrenMinter.quoteEth(uint8)\",\"FrenMinter.quoteFloor(uint256)\",\"FrenMinter.quoteFloorEth(uint256)\",\"FrenMinter.unlockCallback(bytes)\",\"FrenSwapper.ethToImd6900(uint256,address)\",\"FrenSwapper.imdToImd6900(uint256,uint256,address)\",\"FrenSwapper.unlockCallback(bytes)\",\"FrenWorkerGate.cancelOwnershipHandover()\",\"FrenWorkerGate.claim(uint256[],address)\",\"FrenWorkerGate.claimWl(uint256,bytes32[],address)\",\"FrenWorkerGate.completeOwnershipHandover(address)\",\"FrenWorkerGate.openPublic()\",\"FrenWorkerGate.renounceOwnership()\",\"FrenWorkerGate.requestOwnershipHandover()\",\"FrenWorkerGate.setWlRoot(bytes32)\",\"FrenWorkerGate.spend(address,uint256)\",\"FrenWorkerGate.transferOwnership(address)\",\"IMD6900Frens.addPairRule((uint8,uint8,uint8,uint8,uint8))\",\"IMD6900Frens.approve(address,uint256)\",\"IMD6900Frens.approveJob(uint256,uint256,uint256,(string,bytes32,string,bytes32,bytes32,string,uint256))\",\"IMD6900Frens.buyFloor(uint256)\",\"IMD6900Frens.buyFloorWithEth(uint256,uint256)\",\"IMD6900Frens.buyTreasury(uint256,uint256,uint256)\",\"IMD6900Frens.cancelOwnershipHandover()\",\"IMD6900Frens.completeOwnershipHandover(address)\",\"IMD6900Frens.freezeArt()\",\"IMD6900Frens.receive()\",\"IMD6900Frens.recycle(uint256)\",\"IMD6900Frens.releaseLapsedJob(uint256)\",\"IMD6900Frens.renounceOwnership()\",\"IMD6900Frens.requestMint(uint8,uint256)\",\"IMD6900Frens.requestMintFor(address,uint8,uint256)\",\"IMD6900Frens.requestOwnershipHandover()\",\"IMD6900Frens.retryJob(uint256)\",\"IMD6900Frens.reveal(uint256,uint24[],string,bytes32,uint256,bytes,uint256)\",\"IMD6900Frens.safeTransferFrom(address,address,uint256)\",\"IMD6900Frens.safeTransferFrom(address,address,uint256,bytes)\",\"IMD6900Frens.sealTraits()\",\"IMD6900Frens.setApprovalForAll(address,bool)\",\"IMD6900Frens.setGovernor(address)\",\"IMD6900Frens.setMaxMint(uint8[4])\",\"IMD6900Frens.setMintOpen(bool)\",\"IMD6900Frens.setModules(address,address)\",\"IMD6900Frens.setParams(uint256,uint256,uint256)\",\"IMD6900Frens.setRenderer(address)\",\"IMD6900Frens.setRoles(address,address,address)\",\"IMD6900Frens.setRoyalty(uint96)\",\"IMD6900Frens.setTiers(uint256[3],uint256[3],uint256[3])\",\"IMD6900Frens.setTraitRules(uint8,uint16[],uint8[])\",\"IMD6900Frens.setTransferValidator(address)\",\"IMD6900Frens.transferFrom(address,address,uint256)\",\"IMD6900Frens.transferOwnership(address)\",\"IMD6900Frens.unwrapWeth()\"],\"files\":{\".gitignore\":4,\"ADAPTATION.md\":215,\"README.md\":218,\"foundry.toml\":22,\"launch.json\":16,\"script/art/chunks.py\":165,\"script/art/data/bgpal/cleanlab_blue.bin\":1,\"script/art/data/bgpal/cleanlab_green.bin\":1,\"script/art/data/bgpal/cleanlab_red.bin\":1,\"script/art/data/bgpal/messylab_blue.bin\":1,\"script/art/data/bgpal/messylab_green.bin\":1,\"script/art/data/bgpal/messylab_red.bin\":1,\"script/art/data/bgpal/tubeblue.bin\":1,\"script/art/data/bgpal/tubegreen.bin\":2,\"script/art/data/bgpal/tubered.bin\":1,\"script/art/data/bgpal/tubeyellow.bin\":1,\"script/art/data/bgpal/wireframe_green.bin\":2,\"script/art/data/bgpal/wireframe_red.bin\":1,\"script/art/data/expected.json\":53,\"script/art/data/layers/bg0.bin\":9,\"script/art/data/layers/bg1.bin\":9,\"script/art/data/layers/bg10.bin\":32,\"script/art/data/layers/bg11.bin\":32,\"script/art/data/layers/bg2.bin\":9,\"script/art/data/layers/bg3.bin\":15,\"script/art/data/layers/bg4.bin\":15,\"script/art/data/layers/bg5.bin\":15,\"script/art/data/layers/bg6.bin\":26,\"script/art/data/layers/bg7.bin\":26,\"script/art/data/layers/bg8.bin\":26,\"script/art/data/layers/bg9.bin\":26,\"script/art/data/layers/coat.bin\":20,\"script/art/data/layers/item5.bin\":2,\"script/art/data/manifest.json\":500,\"script/art/data/shared.bin\":4,\"script/art/data/tables.bin\":2,\"script/art/swarm-v2.json\":374,\"script/frens/DeployFrens.s.sol\":254,\"script/frens/FrensTimelockBatch.s.sol\":105,\"script/frens/price/prices-swarm.bin\":6,\"script/frens/price/prices.bin\":6,\"script/frens/price/prices.json\":26,\"script/frens/price/prices.py\":55,\"script/frens/price/prices_sol.py\":33,\"script/placement/addresses.json\":7,\"script/placement/gen.py\":146,\"src/FrensCode.sol\":15,\"src/FrensPlacement.sol\":129,\"src/FrensPlan.sol\":37,\"src/art/WorkerArt.sol\":671,\"src/art/WorkerArtIndex.sol\":56,\"src/frens/FrenMinter.sol\":194,\"src/frens/FrenPrices.sol\":122,\"src/frens/FrenSwapper.sol\":276,\"src/frens/FrenWorkerGate.sol\":137,\"src/frens/IMD6900Frens.sol\":1083,\"src/frens/WorkerFrensRenderer.sol\":377,\"test/FrensArtFraming.t.sol\":290,\"test/FrensLaunchAdaptation.t.sol\":331,\"test/FrensLaunchFailures.t.sol\":526,\"test/FrensLaunchReview.t.sol\":449,\"test/FrensPlacedInvariants.t.sol\":799,\"test/FrensPlacement.t.sol\":818,\"test/FrensResidualRisks.t.sol\":210,\"test/FrensRevisionRisks.t.sol\":336,\"test/FrensSplitGas.t.sol\":68,\"test/frens/DataWriter.sol\":21,\"test/frens/FrenMinter.fork.t.sol\":189,\"test/frens/FrenMinterInvariants.t.sol\":290,\"test/frens/FrenMinterOffline.t.sol\":308,\"test/frens/FrenSwapper.fork.t.sol\":134,\"test/frens/FrenSwapperAverage.t.sol\":77,\"test/frens/FrenWorkerGate.t.sol\":350,\"test/frens/FrensRules.sol\":75,\"test/frens/IMD6900Frens.t.sol\":1763,\"test/frens/IMD6900FrensPermit2.fork.t.sol\":130,\"test/frens/IMD6900FrensValidator.fork.t.sol\":146,\"test/frens/IMD6900FrensValidator.t.sol\":231,\"test/helpers/FrensEthFixture.sol\":237,\"test/test_collection_manifest.py\":56},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"1a0862ff7b61c86e05c3f40211f5de1335ee3a9f4fcabf76b79e41731162ec27","verifiedTreeHash":"e2194f587b788eff9c7e2b9e8bc1216740bdc733","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":168661,"exitCode":0,"name":"build","output":"Compiling 94 files with Solc 0.8.30\nSolc 0.8.30 finished in 168.42s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> test/FrensPlacement.t.sol:786:9:\n    |\n786 |         uint256 s = _find(j, key, 0) + key.length;\n    |         ^^^^^^^^^\nNote: The shadowed declaration is here:\n   --> test/FrensPlacement.t.sol:477:5:\n    |\n477 |     DeployFrens s;\n    |     ^^^^^^^^^^^^^\n\nWarning (2018): Function state mutability can be restricted to view\n   --> test/FrensLaunchAdaptation.t.sol:199:5:\n    |\n199 |     function test_PlacedReportsTheLaunchModules() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/frens/IMD6900Frens.sol:964:66\n    │\n964 │         return (ITransferValidator721.validateTransfer.selector, true);\n    │                                                                  ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/frens/WorkerFrensRenderer.sol:59:29\n   │\n59 │           bytes memory json = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n60 │ ┃             '{\"name\":\"Worker Fren #',\n61 │ ┃             tokenId.toString(),\n62 │ ┃             '\",\"description\":\"',\n   ‡ ┃\n68 │ ┃             \"}\"\n69 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/frens/WorkerFrensRenderer.sol:75:29\n   │\n75 │           bytes memory json = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n76 │ ┃             '{\"name\":\"Worker Fren #',\n77 │ ┃             tokenId.toString(),\n78 │ ┃             '\",\"description\":\"',\n   ‡ ┃\n82 │ ┃             '\",\"attributes\":[{\"trait_type\":\"Status\",\"value\":\"Unrevealed\"}]}'\n83 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/WorkerFrensRenderer.sol:124:21\n    │\n124 │                 if (k == i) {\n    │                     ━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:74:9\n   │\n74 │         address frens_,\n   │         ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:75:9\n   │\n75 │         address pairHook_,\n   │         ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:76:9\n   │\n76 │         address imdPoolHook_\n   │         ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:73:9\n   │\n73 │         address imd6900_,\n   │         ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:72:9\n   │\n72 │         address imd_,\n   │         ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/WorkerFrensRenderer.sol:125:54\n    │\n125 │                     bytes memory out = new bytes(j - start);\n    │                                                      ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/frens/WorkerFrensRenderer.sol:158:17\n    │\n158 │ ┏                 abi.encodePacked(\n159 │ ┃                     '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 84 84\" width=\"840\" height=\"840\">',\n160 │ ┃                     '<image width=\"84\" height=\"',\n161 │ ┃                     (N * PENDING_FRAMES).toString(),\n    ‡ ┃\n165 │ ┃                     'dur=\"1.3s\" calcMode=\"discrete\" repeatCount=\"indefinite\"/></image></svg>'\n166 │ ┃                 )\n    │ ┗━━━━━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:208:35\n    │\n208 │                 bytes1 v = bytes1(uint8(l * 3 / 5));\n    │                                   ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:221:42\n    │\n221 │         _draw(cv, _entry(Art.BG0 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                          ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:221:62\n    │\n221 │         _draw(cv, _entry(Art.BG0 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                              ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:253:74\n    │\n253 │         if (ch > 2 || face >= FACES || coat > 2 || shirt > 5 || hat > 2) revert Missing();\n    │                                                                          ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:350:31\n    │\n350 │         if (i >= Art.ENTRIES) revert Missing();\n    │                               ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:356:72\n    │\n356 │         if (p.codehash != bytes32(_hex(Art.CHUNK_HASHES, c * 64, 64))) revert BadArt();\n    │                                                                        ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:288:25\n    │\n288 │                         revert(28, 4)\n    │                         ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:232:56\n    │\n232 │         _draw(back, _entry(Art.BG0 + Art.PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                        ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:232:76\n    │\n232 │         _draw(back, _entry(Art.BG0 + Art.PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                                            ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:239:23\n    │\n239 │             _fren(cv, uint24(combo));\n    │                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:261:22\n    │\n261 │         int256 dx = -int256(CX);\n    │                      ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:262:22\n    │\n262 │         int256 dy = -int256(CY);\n    │                      ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[missing-events-access-control]: `keeper` is changed without an event but is used for access control\n     ╭▸ src/frens/IMD6900Frens.sol:1009:36\n     │\n1009 │         if (keeper_ != address(0)) keeper = keeper_;\n     │                                    ━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `governor` is changed without an event but is used for access control\n     ╭▸ src/frens/IMD6900Frens.sol:1075:9\n     │\n1075 │         governor = g;\n     │         ━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:321:56\n    │\n321 │         for (uint256 i; i < n; ++i) b[at + i] = bytes1(uint8(v >> (8 * i)));\n    │                                                        ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/frens/WorkerFrensRenderer.sol:370:49\n    │\n370 │             extcodecopy(p, add(raw, 32), add(1, mul(first, 33)), mul(frames, 33))\n    │                                                 ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenWorkerGate.sol:58:68\n   │\n58 │     constructor(address owner_, address frens_, address identity_, address strategy_) {\n   │                                                                    ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenWorkerGate.sol:58:33\n   │\n58 │     constructor(address owner_, address frens_, address identity_, address strategy_) {\n   │                                 ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[calls-loop]: external call inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:76:30\n   │\n76 │             address holder = identity.ownerOf(id);\n   │                              ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/FrensPlacement.sol:54:13\n   │\n54 │ ┏             abi.encodePacked(\n55 │ ┃                 FrensCode.FRENS,\n56 │ ┃                 abi.encode(\n57 │ ┃                     FrensPlan.OWNER,\n   ‡ ┃\n68 │ ┃             )\n   │ ┗━━━━━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[calls-loop]: external call inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:77:78\n   │\n77 │             if (holder != msg.sender && (holder != strategy || msg.sender != ISeatStrategyMin(strategy).seatOperator()))\n   │                                                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:79:17\n   │\n79 │                 revert NotYours(id);\n   │                 ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:81:30\n   │\n81 │             if (claimed[id]) revert AlreadyClaimed(id);\n   │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/FrensPlacement.sol:93:29\n   │\n93 │           bytes memory init = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n94 │ ┃             FrensCode.SWAPPER,\n95 │ ┃             abi.encode(\n96 │ ┃                 FrensPlan.POOL_MANAGER, FrensPlan.IMD, FrensPlan.IMD6900, f, FrensPlan.PAIR_HOOK, FrensPlan.POOL4_HOOK\n97 │ ┃             )\n98 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/FrensPlacement.sol:109:13\n    │\n109 │ ┏             abi.encodePacked(\n110 │ ┃                 FrensCode.MINTER, abi.encode(FrensPlan.POOL_MANAGER, f, FrensPlan.POOL4_HOOK, FrensPlan.PAIR_HOOK)\n111 │ ┃             )\n    │ ┗━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/FrensPlacement.sol:115:13\n    │\n115 │             abi.encodePacked(FrensCode.GATE, abi.encode(FrensPlan.OWNER, f, FrensPlan.IDENTITY, FrensPlan.IMD6900))\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/FrenWorkerGate.sol:105:9\n    │\n105 │         emit WlRootSet(root);\n    │         ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:177:16\n    │\n177 │         return uint160(zeroForOne ? uint256(p) * 1e18 / f : uint256(p) * f / 1e18);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:39\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                       ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:55\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                                       ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:77\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                                                             ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:118:35\n    │\n118 │             if (r.length == 68 && bytes4(r) == Quoted.selector) {\n    │                                   ━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:138:79\n    │\n138 │ …     imdKey(), SwapParams({zeroForOne: true, amountSpecified: -int256(amount), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1}), \"\"\n    │                                                                 ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:140:25\n    │\n140 │                 imdIn = uint256(uint128(e.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:140:33\n    │\n140 │                 imdIn = uint256(uint128(e.amount1()));\n    │                                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:148:39\n    │\n148 │                     amountSpecified: -int256(imdIn),\n    │                                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:153:35\n    │\n153 │             revert Quoted(amount, uint256(uint128(imdIs0 ? p.amount1() : p.amount0())));\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:153:43\n    │\n153 │             revert Quoted(amount, uint256(uint128(imdIs0 ? p.amount1() : p.amount0())));\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:158:70\n    │\n158 │ …     imdKey(), SwapParams({zeroForOne: true, amountSpecified: int256(imdOut), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1}), \"\"\n    │                                                                ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:160:25\n    │\n160 │         uint256 ethIn = uint256(uint128(-d.amount0()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:160:33\n    │\n160 │         uint256 ethIn = uint256(uint128(-d.amount0()));\n    │                                 ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:161:23\n    │\n161 │         uint256 got = uint256(uint128(d.amount1()));\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:161:31\n    │\n161 │         uint256 got = uint256(uint128(d.amount1()));\n    │                               ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenMinter.sol:165:9\n    │\n165 │         poolManager.settle{value: ethIn}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/FrenMinter.sol:183:29\n    │\n183 │             if (!withEth && cost > a) continue;\n    │                             ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:178:17\n    │\n178 │             try frens.quote(n) returns (uint256 q) {\n    │                 ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:22\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                      ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:51\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                                                   ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:84\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                                                                                    ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:184:22\n    │\n184 │             if (n <= frens.maxMint(_tier(withEth ? a : a - cost, b, c))) return n;\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:211:35\n    │\n211 │                 amountSpecified: -int256(amountIn),\n    │                                   ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:219:23\n    │\n219 │         uint256 pay = uint256(uint128(-paid));\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:219:31\n    │\n219 │         uint256 pay = uint256(uint128(-paid));\n    │                               ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:221:13\n    │\n221 │             poolManager.settle{value: pay}();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:225:13\n    │\n225 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:227:23\n    │\n227 │         uint256 out = uint256(uint128(got));\n    │                       ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:227:31\n    │\n227 │         uint256 out = uint256(uint128(got));\n    │                               ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:248:35\n    │\n248 │                 amountSpecified: -int256(ethIn),\n    │                                   ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:253:27\n    │\n253 │         uint256 ethPaid = uint256(uint128(-d1.amount0()));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:253:35\n    │\n253 │         uint256 ethPaid = uint256(uint128(-d1.amount0()));\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:254:26\n    │\n254 │         uint256 imdGot = uint256(uint128(d1.amount1()));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:254:34\n    │\n254 │         uint256 imdGot = uint256(uint128(d1.amount1()));\n    │                                  ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:261:35\n    │\n261 │                 amountSpecified: -int256(imdGot),\n    │                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:267:27\n    │\n267 │         uint256 imdPaid = uint256(uint128(-paid));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:267:35\n    │\n267 │         uint256 imdPaid = uint256(uint128(-paid));\n    │                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:268:23\n    │\n268 │         uint256 out = uint256(uint128(got));\n    │                       ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:268:31\n    │\n268 │         uint256 out = uint256(uint128(got));\n    │                               ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:270:9\n    │\n270 │         poolManager.settle{value: ethPaid}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/FrenWorkerGate.sol:135:9\n    │\n135 │         emit PublicOpened(workerMinted);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:318:9\n    │\n318 │         address imdPayTo_,\n    │         ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:312:9\n    │\n312 │         address owner_,\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:315:9\n    │\n315 │         address identity_,\n    │         ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:313:9\n    │\n313 │         address imd_,\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:317:9\n    │\n317 │         address x402Proxy_,\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:319:9\n    │\n319 │         address keeper_,\n    │         ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:316:9\n    │\n316 │         address permit2_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:314:9\n    │\n314 │         address imd6900_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:320:9\n    │\n320 │         address relayer_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:321:9\n    │\n321 │         address priceTable_\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:335:9\n    │\n335 │         emit ICreatorToken.TransferValidatorUpdated(address(0), DEFAULT_TRANSFER_VALIDATOR);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:372:16\n    │\n372 │         return uint8(table >> (8 * trait));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:377:16\n    │\n377 │         return uint8((uint256(combo) >> _shift(trait)) & ((1 << _bits(trait)) - 1));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:386:38\n    │\n386 │             if (minTiers[v] > TIERS) revert BadTraits();\n    │                                      ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/IMD6900Frens.sol:408:13\n    │\n408 │             chars += _rules[v].cap;\n    │             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:371:30\n    │\n371 │         if (trait >= TRAITS) revert BadTraits();\n    │                              ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-no-eth]: external call can be reentered before `floorImd` is updated\n    ╭▸ src/frens/IMD6900Frens.sol:849:13\n    │\n849 │         try IFrenSwapper(swapper).imdToImd6900{gas: FLOOR_BUY_GAS}(imdIn, minOut, address(this)) {}\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:985:9\n    │\n985 │         ITransferValidator721(validator).validateTransfer(msg.sender, from, to, id);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:561:9\n    │\n561 │         emit FrensMinted(requestId, minter, first, count, tier, paid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:859:9\n    │\n859 │         emit FloorBought(spent, 0, got, reserve);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:979:13\n    │\n979 │             revert LastFrenOut();\n    │             ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:557:82\n    │\n557 │         requests[requestId] = Request(minter, tier, lowTier, false, count, 0, 1, uint32(first), 0, 0);\n    │                                                                                  ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/IMD6900Frens.sol:623:38\n    │\n623 │             h = keccak256(abi.encode(h, combos[i]));\n    │                                      ━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:615:13\n    │\n615 │         if (block.timestamp > deadline) revert BadVoucher();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:643:13\n    │\n643 │             emit Revealed(requestId, tokenId, combo, jobId, outputHash);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:632:28\n    │\n632 │             if (code != 0) revert BadCombo(code);\n    │                            ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:634:90\n    │\n634 │             if (valueOf(combo, CHARACTER) == 0 && pepe.minted + openLowTier >= pepe.cap) revert BadCombo(3);\n    │                                                                                          ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:627:22\n    │\n627 │         r.revealed = uint8(upTo);\n    │                      ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:665:53\n    │\n665 │         if (!r.jobApproved || _spent(r.jobNonce) || block.timestamp <= r.jobDeadline) return false;\n    │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:689:9\n    │\n689 │         emit JobPaid(requestId, msg.sender);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:715:13\n    │\n715 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours || _spent(nonce)) revert BadJob();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:715:53\n    │\n715 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours || _spent(nonce)) revert BadJob();\n    │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:731:9\n    │\n731 │         emit JobApproved(requestId, nonce, deadline, permitDigest, quoteDigest);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:722:25\n    │\n722 │         r.jobDeadline = uint40(deadline);\n    │                         ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint40' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/frens/IMD6900Frens.sol:873:9\n    │\n873 │         IFrenSwapper(swapper).ethToImd6900{value: ethIn}(minOut, address(this));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:895:9\n    │\n895 │         emit FloorBought(imdIn, ethIn, got, reserve);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/IMD6900Frens.sol:873:9\n    │\n873 │         IFrenSwapper(swapper).ethToImd6900{value: ethIn}(minOut, address(this));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:888:9\n    │\n888 │         emit WethUnwrapped(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:911:9\n    │\n911 │         emit Recycled(tokenId, msg.sender, paid, imdPaid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:934:9\n    │\n934 │         emit TreasuryBought(tokenId, msg.sender, paid, imdPaid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:955:35\n    │\n955 │     function setTransferValidator(address validator) external onlyCurator {\n    │                                   ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:957:9\n    │\n957 │         emit ICreatorToken.TransferValidatorUpdated(getTransferValidator(), validator);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n     ╭▸ src/frens/IMD6900Frens.sol:1016:26\n     │\n1016 │     function setRenderer(address r) external onlyCurator {\n     │                          ━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1019:9\n     │\n1019 │         emit Setting(\"renderer\", 0, r);\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1025:9\n     │\n1025 │         emit Setting(\"artFrozen\", 1, renderer);\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n     ╭▸ src/frens/IMD6900Frens.sol:1041:17\n     │\n1041 │                 revert BadTraits();\n     │                 ━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n     ╭▸ src/frens/IMD6900Frens.sol:1053:84\n     │\n1053 │             if (m[i] == 0 || m[i] > MAX_PER_REQUEST || (i > 0 && m[i] < m[i - 1])) revert BadTraits();\n     │                                                                                    ━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1069:9\n     │\n1069 │         emit Setting(\"royalty\", bps, address(0));\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:530:21\n    │\n530 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n531 │         bytes memory sig = _sign(frens, relayerKey, id, c, d);\n532 │         vm.warp(d + 1);\n    │         ────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:540:17\n    │\n540 │         vm.warp(block.timestamp + 30 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:685:50\n    │\n685 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:686:17\n    │\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:699:17\n    │\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:721:50\n    │\n721 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:722:17\n    │\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:737:50\n    │\n737 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:296:21\n    │\n296 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:742:17\n    │\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:753:17\n    │\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:760:17\n    │\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/FrensPlacedInvariants.t.sol:337:17\n    │\n337 │         vm.roll(block.number + bound(blocks, 0, 2));\n    │         ────────━━━━━━━━━━━━─────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/FrensPlacedInvariants.t.sol:372:17\n    │\n372 │         vm.roll(block.number + bound(blocks, 0, 2));\n    │         ────────━━━━━━━━━━━━─────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":2935,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/FrensResidualRisks.t.sol:FrensAverageResidualRiskTest\n[PASS] test_Risk_DustFirstPinsAverageAcrossOneHundredBlocks() (gas: 9697080)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.34ms (2.15ms CPU time)\n\nRan 1 test for test/FrensPlacement.t.sol:FrensPlacementForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 3.44ms (0.00ns CPU time)\n\nRan 1 test for test/frens/FrenMinter.fork.t.sol:FrenMinterForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 3.49ms (0.00ns CPU time)\n\nRan 1 test for test/frens/IMD6900FrensValidator.fork.t.sol:IMD6900FrensValidatorForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 2.83ms (0.00ns CPU time)\n\nRan 1 test for test/frens/FrenSwapper.fork.t.sol:FrenSwapperForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 4.24ms (0.00ns CPU time)\n\nRan 1 test for test/frens/IMD6900FrensPermit2.fork.t.sol:IMD6900FrensPermit2ForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 3.59ms (0.00ns CPU time)\n\nRan 3 tests for test/FrensResidualRisks.t.sol:FrensResidualRisksTest\n[PASS] test_Risk_DuplicatePendingNonceStrandsOnePayment() (gas: 1554494)\n[PASS] test_Risk_PendingEthSpotPushDilutesExistingHolders() (gas: 5288577)\n[PASS] test_Risk_UnwiringSwapperAllowsCheapReserveCapture() (gas: 992804)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 6.69ms (4.95ms CPU time)\n\nRan 9 tests for test/frens/IMD6900FrensValidator.t.sol:IMD6900FrensValidatorTest\n[PASS] test_AuthorizationDoesNotOutliveTheSale() (gas: 193293)\n[PASS] test_AuthorizationFromTwoTopLevelCallsIsLost() (gas: 86867)\nLogs:\n  sale authorized in a separate top-level call: blocked\n\n[PASS] test_AuthorizationIsForThatFrenOnly() (gas: 394696)\n[PASS] test_AuthorizationIsForThatOperatorOnly() (gas: 479809)\n[PASS] test_ConduitAloneIsBlocked() (gas: 53267)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 79352)\n[PASS] test_HolderSendsItHerself() (gas: 87060)\n[PASS] test_OnlyTheAuthorizerAuthorizes() (gas: 74587)\n[PASS] test_ZoneAuthorizedSaleGoesThroughInOneFrame() (gas: 124594)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 10.71ms (925.02µs CPU time)\n\nRan 18 tests for test/frens/FrenWorkerGate.t.sol:FrenWorkerGateTest\n[PASS] test_ARefusedMintTakesNothing() (gas: 104954)\n[PASS] test_AnNftCountsOnce_whoeverHoldsItLater() (gas: 151129)\n[PASS] test_CreditsGoWhereTheHolderSays() (gas: 639698)\n[PASS] test_NoGateNoWindow() (gas: 442079)\n[PASS] test_OnlyFrensSpends() (gas: 32613)\n[PASS] test_OnlyTheHolderClaims() (gas: 67063)\n[PASS] test_OpenPublic() (gas: 528969)\n[PASS] test_OwnerMintsBeforeTheOpening() (gas: 2296619)\n[PASS] test_SeatOperatorClaimsTheStrategysNfts() (gas: 138514)\n[PASS] test_TheCreditIsTheMinters() (gas: 576375)\n[PASS] test_WindowClosesAfter420() (gas: 32492721)\n[PASS] test_WindowNeedsACreditPerFren() (gas: 714583)\n[PASS] test_WindowNeverOvershoots() (gas: 36198264)\n[PASS] test_WlAndWorkersShareThe420() (gas: 619410)\n[PASS] test_WlOnlyAsListed_once() (gas: 254028)\n[PASS] test_WlRaisedAmount_claimsTheDifference() (gas: 226858)\n[PASS] test_WlRootOnlyTheOwner_andNoRootNoWl() (gas: 62252)\n[PASS] test_WlWalletsMintInTheWindow_asMuchAsListed() (gas: 750975)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 37.51ms (23.44ms CPU time)\n\nRan 6 tests for test/frens/FrenSwapperAverage.t.sol:FrenSwapperAverageTest\n[PASS] test_AFullBuyMovesItOneStep() (gas: 103606)\n[PASS] test_ASmallBuyMovesItInProportion() (gas: 52842)\n[PASS] test_DustMovesItNextToNothing() (gas: 7606082)\n[PASS] test_FloorRateIsTheLowerReading() (gas: 17282)\n[PASS] test_MovesDownToo() (gas: 52658)\n[PASS] test_OnceABlock() (gas: 91443)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 73.77ms (2.77ms CPU time)\n\nRan 3 tests for test/FrensLaunchReview.t.sol:FrensFactoryReviewTest\n[PASS] test_Audit_ConstructorLeavesTheDocumentedTeamSetup() (gas: 25294534)\n[PASS] test_FreshChainCallsOnlyContractsCreatedByTheLaunch() (gas: 28696930)\n[PASS] test_ProtectedFactoryDeploysBothLaunchesInOrder() (gas: 25493481)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 80.97ms (15.58ms CPU time)\n\nRan 8 tests for test/FrensLaunchReview.t.sol:FrensAuditReviewTest\n[PASS] test_Audit_GovernorCanCloseMintAgain() (gas: 463670)\n[PASS] test_Audit_OwnerOrGovernorCanBlockAllPeerTransfers() (gas: 1379074)\n[PASS] test_Fix_LapsedJobApprovalIsReleasedByTheReveal() (gas: 1037000)\n[PASS] test_Fix_LastFrenOutStaysOutSoFeesHaveAnOwner() (gas: 1203616)\n[PASS] test_Fix_LaunchAdoptsASwapperSeededAtThePoolsPrice() (gas: 2008131)\n[PASS] test_Fix_LaunchRefusesTheWrongArt() (gas: 18811)\n[PASS] test_Fix_LaunchReplacesASwapperSeededOffASkewedPrice() (gas: 2745326)\n[PASS] test_Fix_LaunchsOwnSwapperStartsAtThePoolsPrice() (gas: 237014)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 81.54ms (4.47ms CPU time)\n\nRan 12 tests for test/FrensArtFraming.t.sol:FrensArtFramingTest\n[PASS] testFuzz_EntryPastTheIndexIsMissing(uint256) (runs: 256, μ: 13968, ~: 13943)\nLogs:\n  Bound result 32293651807887470885553672401079112064777\n\n[PASS] testFuzz_UnknownBackgroundIsMissing(uint256) (runs: 256, μ: 35607, ~: 35592)\nLogs:\n  Bound result 32293651807887470885553672401079112064777\n\n[PASS] test_ATamperedChunkDrawsNothing() (gas: 90530)\n[PASS] test_AttributesDoNotValidate() (gas: 369298)\n[PASS] test_ChunksAreFramedPushData() (gas: 2731712)\n[PASS] test_EveryValueHasAName() (gas: 15679313)\n[PASS] test_IndexHashesAreTheChunks() (gas: 788314)\n[PASS] test_IndexPointsInsideTheChunks() (gas: 525933)\n[PASS] test_LaunchLayersAreWellFormed() (gas: 46186857)\n[PASS] test_NoSuchFrenIsMissing() (gas: 4375022)\n[PASS] test_PalettesFitTogether() (gas: 13989817)\n[PASS] test_WithoutTheSwarmNothingDraws() (gas: 5947823)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 81.75ms (205.12ms CPU time)\n\nRan 16 tests for test/FrensLaunchAdaptation.t.sol:FrensLaunchAdaptationTest\n[PASS] test_BootstrapThenOpenAndResume() (gas: 2315891)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\n[PASS] test_FirstFrensBeforeFeesPreventsPublicFloorCapture() (gas: 3435830)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_FirstFrensRequiresPausedBuysAndNonzeroCount() (gas: 1785691)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Fix_SpentNonceIsRefused() (gas: 2311018)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeRefuseAnOwnerlessFloor() (gas: 1828783)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeRejectChangedWiring() (gas: 2680591)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeWarnWhenAverageLags() (gas: 4784847)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  WARNING: the swapper's average is more than 2x off the pool's price\n  WARNING: the swapper's average is more than 2x off the pool's price\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\n[PASS] test_PlacedRejectsMissingOrWrongModules() (gas: 126575)\n[PASS] test_PlacedRejectsWrongCollectionAndForeignDependencies() (gas: 65561)\n[PASS] test_PlacedReportsTheLaunchModules() (gas: 51824)\n[PASS] test_ResumeRequiresDistributorAndActualSwapperExemption() (gas: 2164855)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Risk_FirstMintCapturesExistingFloorIfRunbookIsBypassed() (gas: 3054450)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Risk_LaunchBlockPushSeedsSwapperAndSetupRejectsAfterPriceReturns() (gas: 2572174)\n[PASS] test_Risk_UnsupportedRoyaltyTokenHasNoFloorRoute() (gas: 3781565)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_SetupPausesBeforeFirstMintEvenWhenAlreadyDistributor() (gas: 1815518)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_SetupUsesTheLaunchsReplacementSwapper() (gas: 6981779)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 94.42ms (16.03ms CPU time)\n\nRan 86 tests for test/frens/IMD6900Frens.t.sol:IMD6900FrensTest\n[PASS] test_AMintNeverFailsOnTheSwap() (gas: 1003226)\n[PASS] test_BatchCannotRepeatAFren() (gas: 641744)\n[PASS] test_BatchCountIsOneTo69() (gas: 2185647)\n[PASS] test_BatchCountsTheCapsAsItGoes() (gas: 11780401)\n[PASS] test_BatchHoldsPepesForLowTiers() (gas: 12176238)\n[PASS] test_BatchPaysOneJob() (gas: 700213)\n[PASS] test_BatchRevealIsExactlyTheVoucher() (gas: 1011974)\n[PASS] test_BatchRevealsThemAll() (gas: 2256463)\n[PASS] test_BatchSoldOut() (gas: 90792483)\n[PASS] test_BuyTreasuryRespectsMaxPay() (gas: 1842004)\n[PASS] test_BuyingFromTheTreasuryRaisesTheFloor() (gas: 3101386)\n[PASS] test_CurveWhileAboveTheFloor() (gas: 1599825)\n[PASS] test_DearerImd6900RaisesTheFloorPrice() (gas: 2861964)\n[PASS] test_DustEthBuyCannotTakeTheMintsTurn() (gas: 540029)\n[PASS] test_EveryFrenIsOneOfAKind() (gas: 947080)\n[PASS] test_FailedJob_payAnother() (gas: 1173029)\n[PASS] test_FeeEthBuysTheFloor() (gas: 1708734)\n[PASS] test_FloorBuysAreAnyones_onePerBlock() (gas: 1893053)\n[PASS] test_FloorBuysAreCapped() (gas: 55694600)\n[PASS] test_FloorIsReserveOverFrensOut() (gas: 2761464)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 2145216)\n[PASS] test_FrenSentStraightToTheTreasuryCounts() (gas: 2660288)\n[PASS] test_IsACreatorToken() (gas: 41743)\n[PASS] test_JobPaymentIsPinnedToImdsPayee() (gas: 980756)\n[PASS] test_KeeperApprovesTheJobPaidFor() (gas: 657241)\n[PASS] test_KeeperCannotApproveASpentNonce() (gas: 490236)\n[PASS] test_KeeperCannotApproveLongLivedPayments() (gas: 434019)\n[PASS] test_LapsedPaymentIsUsedAgain() (gas: 719034)\n[PASS] test_LastFrenOutStaysOut() (gas: 1615217)\n[PASS] test_LaterPartsAgreeOnWhatIsRevealed() (gas: 1347339)\n[PASS] test_LowTierRequestsNeedAPepeLeft() (gas: 11708397)\n[PASS] test_MaxMintGrowsWithTheBag() (gas: 4682663)\n[PASS] test_MaxPayStopsAPriceThatMoved() (gas: 10063499)\n[PASS] test_MaxRequest() (gas: 9863897)\n[PASS] test_MintMintsAtOnce_unrevealed() (gas: 770449)\n[PASS] test_MintNeedsSealedTraits() (gas: 8623021)\n[PASS] test_MintNeverBelowTheFloor() (gas: 3425403)\n[PASS] test_MintNeverSkipsTheBuyForWantOfGas() (gas: 708870)\n[PASS] test_NoMintIntoTheTreasury() (gas: 1596022)\n[PASS] test_NoRequestForNobody() (gas: 61912)\n[PASS] test_NoTierInsideAFlashLoan() (gas: 687636)\n[PASS] test_NobodyElseApprovesJobs() (gas: 435635)\n[PASS] test_OneRevealPerFren() (gas: 717718)\n[PASS] test_OnlyHoldersRecycle() (gas: 1593666)\n[PASS] test_OnlyRealFrens() (gas: 743319)\n[PASS] test_OnlyTheOwnerSetsTheValidator() (gas: 1063173)\n[PASS] test_OwnerAdjustsTheArtUntilFrozen() (gas: 1555030)\n[PASS] test_PairRules() (gas: 875556)\n[PASS] test_PartsOnlyGoForward() (gas: 1035497)\n[PASS] test_PendingEthCountsInTheMintPrice() (gas: 2181876)\n[PASS] test_PriceRisesWithMints() (gas: 38125610)\n[PASS] test_PriceTableIsTheCurve() (gas: 36977950)\n[PASS] test_QuoteCountsUnsweptImd() (gas: 3216110)\n[PASS] test_RareTraitsNeedTheTier() (gas: 1365734)\n[PASS] test_RecyclePaysTheFloorAndKeepsIt() (gas: 2915603)\n[PASS] test_RecyclePaysTheWaitingImdToo() (gas: 2749360)\n[PASS] test_ReleaseLapsedJobAfterTheReveal() (gas: 2142099)\n[PASS] test_RequestMintFor() (gas: 1084060)\n[PASS] test_ReserveOnlyLeavesThroughRecycle() (gas: 2838128)\n[PASS] test_RevealFollowsTheFren() (gas: 749220)\n[PASS] test_RevealInParts() (gas: 9747514)\nLogs:\n  gas, minting 69: 2117887\n  gas, revealing 30: 3092845\n\n[PASS] test_RevealNeedsTheRelayersVoucher() (gas: 803651)\n[PASS] test_RevealReleasesALapsedPayment() (gas: 919103)\n[PASS] test_RevealShowsTheFren() (gas: 1192018)\n[PASS] test_RevealsWhenever() (gas: 661560)\n[PASS] test_RoyaltiesPayTheFloor() (gas: 1724930)\n[PASS] test_RoyaltyBpsIsCappedAndOwnerOnly() (gas: 109412)\n[PASS] test_RulesAreFrozenOnceSealed() (gas: 69580)\n[PASS] test_SealNeedsEveryTraitAndTheSupply() (gas: 10518169)\n[PASS] test_SetMaxMint() (gas: 178238)\n[PASS] test_ShortSwapReverts() (gas: 1046881)\n[PASS] test_SwapperMayPullOnlyTheBuy() (gas: 957490)\n[PASS] test_TheOwnerKeepsTheCollection_theGovernorTheMechanics() (gas: 488664)\n[PASS] test_TierCountsWhatIsLeftAfterPaying() (gas: 920004)\n[PASS] test_TierIsTheBestOfTheBag() (gas: 633505)\n[PASS] test_TierIsTheMints() (gas: 1350710)\n[PASS] test_TierThresholdsOwnerOnlyAndOrdered() (gas: 154928)\n[PASS] test_TradesGoPastTheValidator() (gas: 1346243)\n[PASS] test_TraitCapsHold() (gas: 25778206)\n[PASS] test_TreasuryRoundTripCostsTheFloor() (gas: 3295585)\n[PASS] test_UnneededRetryFeedsTheFloor() (gas: 1026116)\n[PASS] test_UnrevealedFrenSellsAtTheFloor() (gas: 1022775)\n[PASS] test_UnrevealedFrensShareTheFloor() (gas: 1466205)\n[PASS] test_UnsweptImdCountsInTheTreasuryPrice() (gas: 3367000)\n[PASS] test_UnusedJobFeedsTheFloor() (gas: 696981)\n[PASS] test_VouchersExpire() (gas: 472492)\nSuite result: ok. 86 passed; 0 failed; 0 skipped; finished in 93.98ms (224.52ms CPU time)\n\nRan 19 tests for test/FrensLaunchFailures.t.sol:FrensLaunchFailuresTest\n[PASS] testFuzz_CheckKnowsAFren(uint24,uint8) (runs: 512, μ: 1938293, ~: 1907475)\nLogs:\n  Bound result 0\n\n[PASS] testFuzz_OtherSaltsLandElsewhere(bytes32) (runs: 256, μ: 34990, ~: 34990)\n[PASS] testFuzz_QuoteIsTheCurveBeforeAnyoneHolds(uint256) (runs: 512, μ: 323716, ~: 321322)\nLogs:\n  Bound result 27\n\n[PASS] test_AnotherOwnerMovesTheFrens() (gas: 34259)\n[PASS] test_CollectionRefusesAShortPriceTable() (gas: 3059925)\n[PASS] test_CollectionWaitsForTheTeamWallet() (gas: 5941480)\n[PASS] test_EveryPriceIsTheTables() (gas: 27580866)\n[PASS] test_FreshChainLaunchesAreIndependent() (gas: 1351085)\n[PASS] test_LaunchContractsTakeNoEth() (gas: 687657)\n[PASS] test_ModulesFailOverCorruptedFrens() (gas: 97101)\n[PASS] test_ModulesNeedTheCollection() (gas: 106270)\n[PASS] test_NoLaunchActorHoldsARole() (gas: 4580578)\n[PASS] test_NoPricePastTheTable() (gas: 41219)\n[PASS] test_OtherSaltSchemeFailsTheLaunch() (gas: 140656)\n[PASS] test_RefusingDeployerFailsTheLaunch() (gas: 103369)\n[PASS] test_ScriptRefusesAnUnplacedChain() (gas: 14786192)\n[PASS] test_SecondLaunchReusesWhatIsPlaced() (gas: 1313404)\n[PASS] test_SilentDeployerFailsTheLaunch() (gas: 93234)\n[PASS] test_WrongArtFailsTheArtLaunch() (gas: 588910)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 94.68ms (313.49ms CPU time)\n\nRan 13 tests for test/FrensPlacement.t.sol:FrensPlacementTest\n[PASS] test_Attributes() (gas: 19594305)\n[PASS] test_CodeIsWhatTheSourcesBuild() (gas: 89696)\n[PASS] test_DeploysOnAFreshChain() (gas: 51811985)\n[PASS] test_EachLaunchFitsOneTransaction() (gas: 105863223)\nLogs:\n  the collection launch: gas, all in: 12451232\n  the art launch: gas, all in: 13605058\n\n[PASS] test_LandsWhereThePlanSays() (gas: 51923654)\n[PASS] test_NewArtReadsBackExactly() (gas: 14173584)\n[PASS] test_PassesTheAdmissionScan() (gas: 88302675)\n[PASS] test_PlanFollowsFromTheCode() (gas: 80750)\n[PASS] test_PlanMatchesTheScript() (gas: 14314057)\n[PASS] test_PriceTableIsTheCurve() (gas: 53105398)\n[PASS] test_RefusesOtherArt() (gas: 12971045)\n[PASS] test_SameAddressesWhoeverRunsIt() (gas: 102774643)\n[PASS] test_TakesWhatSomeonePlacedFirst() (gas: 52491481)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 102.30ms (190.01ms CPU time)\n\nRan 1 test for test/FrensSplitGas.t.sol:FrensSplitGasTest\n[PASS] test_CollectionFitsButLegacyFourContractBundleExceedsCap() (gas: 101750011)\nLogs:\n  legacy four-contract launch: 23877683\n  two-contract collection launch: 13494921\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 138.13ms (138.03ms CPU time)\n\nRan 1 test for test/FrensPlacedInvariants.t.sol:FrensPlacedInvariantsTest\n[PASS]\nFrensPlacedInvariantsTest invariants:\n[PASS] invariant_CapsHold\n[PASS] invariant_FloorIsAShareOfTheWorld\n[PASS] invariant_FrensAreConserved\n[PASS] invariant_ImdIsTheBooks\n[PASS] invariant_ReserveIsExactlyWhatIsHere\n FrensPlacedInvariantsTest invariants (runs: 32, calls: 1920, reverts: 4)\n\n╭--------------------+-----------------+-------+---------+----------╮\n| Contract           | Selector        | Calls | Reverts | Discards |\n+===================================================================+\n| PlacedFrensHandler | approveJob      | 124   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyFloor        | 116   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyTreasury     | 129   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donate6900      | 115   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donateImd       | 119   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | feeEth          | 115   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mint            | 233   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mintThenRecycle | 118   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | recycle         | 133   | 4       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | retryJob        | 119   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | reveal          | 132   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setParams       | 117   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setRate         | 123   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | transfer        | 108   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | warp            | 119   | 0       | 0        |\n╰--------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 24\n  Bound result 10905\n  Bound result 0\n  Bound result 1516\n  Bound result 249999999999999998\n  Bound result 0\n  Bound result 1\n  Bound result 12\n  Bound result 19\n  Bound result 1\n  Bound result 781\n  Bound result 0\n  Bound result 1\n  Bound result 2548\n  Bound result 1\n  Bound result 28\n  Bound result 11\n  Bound result 40\n  Bound result 16\n  Bound result 57\n  Bound result 2464\n  Bound result 8830\n  Bound result 1\n  Bound result 104\n  Bound result 743531\n  Bound result 141624034455026154\n  Bound result 1\n  Bound result 6\n  Bound result 12\n  Bound result 5\n  Bound result 776\n  Bound result 61638\n  Bound result 29\n  Bound result 110\n  Bound result 256\n  Bound result 16596\n  Bound result 106\n  Bound result 161\n  Bound result 3200\n  Bound result 2\n  Bound result 34\n  Bound result 3151\n  Bound result 1\n  Bound result 172451300475097274400\n  Bound result 262467845725531892\n  Bound result 3\n  Bound result 1683\n  Bound result 8892\n  Bound result 300000000000000000000\n  Bound result 21\n  Bound result 244\n  Bound result 284\n  Bound result 1\n  Bound result 24\n  Bound result 3\n  Bound result 819\n  Bound result 101\n  Bound result 1\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.81s (2.80s CPU time)\n\nRan 19 test suites in 2.81s (3.73s CPU time): 196 tests passed, 0 failed, 5 skipped (201 total tests)\n","passed":true},{"durationMs":113,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"FrenMinter.buyImd(uint256)\",\"FrenMinter.mintWithEth(uint8,uint256)\",\"FrenMinter.quoteEth(uint8)\",\"FrenMinter.quoteFloor(uint256)\",\"FrenMinter.quoteFloorEth(uint256)\",\"FrenMinter.unlockCallback(bytes)\",\"FrenSwapper.ethToImd6900(uint256,address)\",\"FrenSwapper.imdToImd6900(uint256,uint256,address)\",\"FrenSwapper.unlockCallback(bytes)\",\"FrenWorkerGate.cancelOwnershipHandover()\",\"FrenWorkerGate.claim(uint256[],address)\",\"FrenWorkerGate.claimWl(uint256,bytes32[],address)\",\"FrenWorkerGate.completeOwnershipHandover(address)\",\"FrenWorkerGate.openPublic()\",\"FrenWorkerGate.renounceOwnership()\",\"FrenWorkerGate.requestOwnershipHandover()\",\"FrenWorkerGate.setWlRoot(bytes32)\",\"FrenWorkerGate.spend(address,uint256)\",\"FrenWorkerGate.transferOwnership(address)\",\"IMD6900Frens.addPairRule((uint8,uint8,uint8,uint8,uint8))\",\"IMD6900Frens.approve(address,uint256)\",\"IMD6900Frens.approveJob(uint256,uint256,uint256,(string,bytes32,string,bytes32,bytes32,string,uint256))\",\"IMD6900Frens.buyFloor(uint256)\",\"IMD6900Frens.buyFloorWithEth(uint256,uint256)\",\"IMD6900Frens.buyTreasury(uint256,uint256,uint256)\",\"IMD6900Frens.cancelOwnershipHandover()\",\"IMD6900Frens.completeOwnershipHandover(address)\",\"IMD6900Frens.freezeArt()\",\"IMD6900Frens.receive()\",\"IMD6900Frens.recycle(uint256)\",\"IMD6900Frens.releaseLapsedJob(uint256)\",\"IMD6900Frens.renounceOwnership()\",\"IMD6900Frens.requestMint(uint8,uint256)\",\"IMD6900Frens.requestMintFor(address,uint8,uint256)\",\"IMD6900Frens.requestOwnershipHandover()\",\"IMD6900Frens.retryJob(uint256)\",\"IMD6900Frens.reveal(uint256,uint24[],string,bytes32,uint256,bytes,uint256)\",\"IMD6900Frens.safeTransferFrom(address,address,uint256)\",\"IMD6900Frens.safeTransferFrom(address,address,uint256,bytes)\",\"IMD6900Frens.sealTraits()\",\"IMD6900Frens.setApprovalForAll(address,bool)\",\"IMD6900Frens.setGovernor(address)\",\"IMD6900Frens.setMaxMint(uint8[4])\",\"IMD6900Frens.setMintOpen(bool)\",\"IMD6900Frens.setModules(address,address)\",\"IMD6900Frens.setParams(uint256,uint256,uint256)\",\"IMD6900Frens.setRenderer(address)\",\"IMD6900Frens.setRoles(address,address,address)\",\"IMD6900Frens.setRoyalty(uint96)\",\"IMD6900Frens.setTiers(uint256[3],uint256[3],uint256[3])\",\"IMD6900Frens.setTraitRules(uint8,uint16[],uint8[])\",\"IMD6900Frens.setTransferValidator(address)\",\"IMD6900Frens.transferFrom(address,address,uint256)\",\"IMD6900Frens.transferOwnership(address)\",\"IMD6900Frens.unwrapWeth()\"],\"files\":{\".gitignore\":4,\"ADAPTATION.md\":160,\"README.md\":218,\"foundry.toml\":22,\"launch.json\":16,\"script/art/chunks.py\":165,\"script/art/data/bgpal/cleanlab_blue.bin\":1,\"script/art/data/bgpal/cleanlab_green.bin\":1,\"script/art/data/bgpal/cleanlab_red.bin\":1,\"script/art/data/bgpal/messylab_blue.bin\":1,\"script/art/data/bgpal/messylab_green.bin\":1,\"script/art/data/bgpal/messylab_red.bin\":1,\"script/art/data/bgpal/tubeblue.bin\":1,\"script/art/data/bgpal/tubegreen.bin\":2,\"script/art/data/bgpal/tubered.bin\":1,\"script/art/data/bgpal/tubeyellow.bin\":1,\"script/art/data/bgpal/wireframe_green.bin\":2,\"script/art/data/bgpal/wireframe_red.bin\":1,\"script/art/data/expected.json\":53,\"script/art/data/layers/bg0.bin\":9,\"script/art/data/layers/bg1.bin\":9,\"script/art/data/layers/bg10.bin\":32,\"script/art/data/layers/bg11.bin\":32,\"script/art/data/layers/bg2.bin\":9,\"script/art/data/layers/bg3.bin\":15,\"script/art/data/layers/bg4.bin\":15,\"script/art/data/layers/bg5.bin\":15,\"script/art/data/layers/bg6.bin\":26,\"script/art/data/layers/bg7.bin\":26,\"script/art/data/layers/bg8.bin\":26,\"script/art/data/layers/bg9.bin\":26,\"script/art/data/layers/coat.bin\":20,\"script/art/data/layers/item5.bin\":2,\"script/art/data/manifest.json\":500,\"script/art/data/shared.bin\":4,\"script/art/data/tables.bin\":2,\"script/art/swarm-v2.json\":374,\"script/frens/DeployFrens.s.sol\":254,\"script/frens/FrensTimelockBatch.s.sol\":105,\"script/frens/price/prices-swarm.bin\":6,\"script/frens/price/prices.bin\":6,\"script/frens/price/prices.json\":26,\"script/frens/price/prices.py\":55,\"script/frens/price/prices_sol.py\":33,\"script/placement/addresses.json\":7,\"script/placement/gen.py\":146,\"src/FrensCode.sol\":15,\"src/FrensPlacement.sol\":129,\"src/FrensPlan.sol\":37,\"src/art/WorkerArt.sol\":671,\"src/art/WorkerArtIndex.sol\":56,\"src/frens/FrenMinter.sol\":194,\"src/frens/FrenPrices.sol\":122,\"src/frens/FrenSwapper.sol\":276,\"src/frens/FrenWorkerGate.sol\":137,\"src/frens/IMD6900Frens.sol\":1083,\"src/frens/WorkerFrensRenderer.sol\":377,\"test/FrensArtFraming.t.sol\":290,\"test/FrensLaunchAdaptation.t.sol\":331,\"test/FrensLaunchFailures.t.sol\":526,\"test/FrensLaunchReview.t.sol\":449,\"test/FrensPlacedInvariants.t.sol\":634,\"test/FrensPlacement.t.sol\":818,\"test/FrensResidualRisks.t.sol\":210,\"test/FrensSplitGas.t.sol\":68,\"test/frens/DataWriter.sol\":21,\"test/frens/FrenMinter.fork.t.sol\":189,\"test/frens/FrenSwapper.fork.t.sol\":134,\"test/frens/FrenSwapperAverage.t.sol\":77,\"test/frens/FrenWorkerGate.t.sol\":350,\"test/frens/FrensRules.sol\":75,\"test/frens/IMD6900Frens.t.sol\":1763,\"test/frens/IMD6900FrensPermit2.fork.t.sol\":130,\"test/frens/IMD6900FrensValidator.fork.t.sol\":146,\"test/frens/IMD6900FrensValidator.t.sol\":231,\"test/test_collection_manifest.py\":56},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"1d31793f341484238c8a4550e9806bbd6006da687ba57704a779aa3aa254e7ca","verifiedTreeHash":"633f0f9abe666e1e3b2b0990e7f5e5401a5e5507","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":201234,"exitCode":0,"name":"build","output":"Compiling 95 files with Solc 0.8.30\nSolc 0.8.30 finished in 200.99s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> test/FrensPlacement.t.sol:786:9:\n    |\n786 |         uint256 s = _find(j, key, 0) + key.length;\n    |         ^^^^^^^^^\nNote: The shadowed declaration is here:\n   --> test/FrensPlacement.t.sol:477:5:\n    |\n477 |     DeployFrens s;\n    |     ^^^^^^^^^^^^^\n\nWarning (2018): Function state mutability can be restricted to view\n   --> test/FrensLaunchAdaptation.t.sol:199:5:\n    |\n199 |     function test_PlacedReportsTheLaunchModules() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/frens/IMD6900Frens.sol:964:66\n    │\n964 │         return (ITransferValidator721.validateTransfer.selector, true);\n    │                                                                  ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:39\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                       ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:55\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                                       ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:77\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                                                             ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:75:9\n   │\n75 │         address pairHook_,\n   │         ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:74:9\n   │\n74 │         address frens_,\n   │         ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:72:9\n   │\n72 │         address imd_,\n   │         ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:73:9\n   │\n73 │         address imd6900_,\n   │         ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:76:9\n   │\n76 │         address imdPoolHook_\n   │         ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-events-access-control]: `keeper` is changed without an event but is used for access control\n     ╭▸ src/frens/IMD6900Frens.sol:1009:36\n     │\n1009 │         if (keeper_ != address(0)) keeper = keeper_;\n     │                                    ━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `governor` is changed without an event but is used for access control\n     ╭▸ src/frens/IMD6900Frens.sol:1075:9\n     │\n1075 │         governor = g;\n     │         ━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/FrensPlacement.sol:54:13\n   │\n54 │ ┏             abi.encodePacked(\n55 │ ┃                 FrensCode.FRENS,\n56 │ ┃                 abi.encode(\n57 │ ┃                     FrensPlan.OWNER,\n   ‡ ┃\n68 │ ┃             )\n   │ ┗━━━━━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/FrensPlacement.sol:93:29\n   │\n93 │           bytes memory init = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n94 │ ┃             FrensCode.SWAPPER,\n95 │ ┃             abi.encode(\n96 │ ┃                 FrensPlan.POOL_MANAGER, FrensPlan.IMD, FrensPlan.IMD6900, f, FrensPlan.PAIR_HOOK, FrensPlan.POOL4_HOOK\n97 │ ┃             )\n98 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/FrensPlacement.sol:109:13\n    │\n109 │ ┏             abi.encodePacked(\n110 │ ┃                 FrensCode.MINTER, abi.encode(FrensPlan.POOL_MANAGER, f, FrensPlan.POOL4_HOOK, FrensPlan.PAIR_HOOK)\n111 │ ┃             )\n    │ ┗━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/FrensPlacement.sol:115:13\n    │\n115 │             abi.encodePacked(FrensCode.GATE, abi.encode(FrensPlan.OWNER, f, FrensPlan.IDENTITY, FrensPlan.IMD6900))\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:118:35\n    │\n118 │             if (r.length == 68 && bytes4(r) == Quoted.selector) {\n    │                                   ━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:177:16\n    │\n177 │         return uint160(zeroForOne ? uint256(p) * 1e18 / f : uint256(p) * f / 1e18);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:138:79\n    │\n138 │ …     imdKey(), SwapParams({zeroForOne: true, amountSpecified: -int256(amount), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1}), \"\"\n    │                                                                 ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:140:25\n    │\n140 │                 imdIn = uint256(uint128(e.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:140:33\n    │\n140 │                 imdIn = uint256(uint128(e.amount1()));\n    │                                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:148:39\n    │\n148 │                     amountSpecified: -int256(imdIn),\n    │                                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:153:35\n    │\n153 │             revert Quoted(amount, uint256(uint128(imdIs0 ? p.amount1() : p.amount0())));\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:153:43\n    │\n153 │             revert Quoted(amount, uint256(uint128(imdIs0 ? p.amount1() : p.amount0())));\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:158:70\n    │\n158 │ …     imdKey(), SwapParams({zeroForOne: true, amountSpecified: int256(imdOut), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1}), \"\"\n    │                                                                ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:160:25\n    │\n160 │         uint256 ethIn = uint256(uint128(-d.amount0()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:160:33\n    │\n160 │         uint256 ethIn = uint256(uint128(-d.amount0()));\n    │                                 ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:161:23\n    │\n161 │         uint256 got = uint256(uint128(d.amount1()));\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:161:31\n    │\n161 │         uint256 got = uint256(uint128(d.amount1()));\n    │                               ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenMinter.sol:165:9\n    │\n165 │         poolManager.settle{value: ethIn}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/FrenMinter.sol:183:29\n    │\n183 │             if (!withEth && cost > a) continue;\n    │                             ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:178:17\n    │\n178 │             try frens.quote(n) returns (uint256 q) {\n    │                 ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:22\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                      ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:51\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                                                   ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:84\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                                                                                    ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:184:22\n    │\n184 │             if (n <= frens.maxMint(_tier(withEth ? a : a - cost, b, c))) return n;\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/frens/WorkerFrensRenderer.sol:59:29\n   │\n59 │           bytes memory json = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n60 │ ┃             '{\"name\":\"Worker Fren #',\n61 │ ┃             tokenId.toString(),\n62 │ ┃             '\",\"description\":\"',\n   ‡ ┃\n68 │ ┃             \"}\"\n69 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/frens/WorkerFrensRenderer.sol:75:29\n   │\n75 │           bytes memory json = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n76 │ ┃             '{\"name\":\"Worker Fren #',\n77 │ ┃             tokenId.toString(),\n78 │ ┃             '\",\"description\":\"',\n   ‡ ┃\n82 │ ┃             '\",\"attributes\":[{\"trait_type\":\"Status\",\"value\":\"Unrevealed\"}]}'\n83 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/WorkerFrensRenderer.sol:124:21\n    │\n124 │                 if (k == i) {\n    │                     ━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/WorkerFrensRenderer.sol:125:54\n    │\n125 │                     bytes memory out = new bytes(j - start);\n    │                                                      ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/frens/WorkerFrensRenderer.sol:158:17\n    │\n158 │ ┏                 abi.encodePacked(\n159 │ ┃                     '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 84 84\" width=\"840\" height=\"840\">',\n160 │ ┃                     '<image width=\"84\" height=\"',\n161 │ ┃                     (N * PENDING_FRAMES).toString(),\n    ‡ ┃\n165 │ ┃                     'dur=\"1.3s\" calcMode=\"discrete\" repeatCount=\"indefinite\"/></image></svg>'\n166 │ ┃                 )\n    │ ┗━━━━━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:208:35\n    │\n208 │                 bytes1 v = bytes1(uint8(l * 3 / 5));\n    │                                   ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:221:42\n    │\n221 │         _draw(cv, _entry(Art.BG0 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                          ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:221:62\n    │\n221 │         _draw(cv, _entry(Art.BG0 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                              ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:253:74\n    │\n253 │         if (ch > 2 || face >= FACES || coat > 2 || shirt > 5 || hat > 2) revert Missing();\n    │                                                                          ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:350:31\n    │\n350 │         if (i >= Art.ENTRIES) revert Missing();\n    │                               ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:356:72\n    │\n356 │         if (p.codehash != bytes32(_hex(Art.CHUNK_HASHES, c * 64, 64))) revert BadArt();\n    │                                                                        ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:288:25\n    │\n288 │                         revert(28, 4)\n    │                         ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:232:56\n    │\n232 │         _draw(back, _entry(Art.BG0 + Art.PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                        ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:232:76\n    │\n232 │         _draw(back, _entry(Art.BG0 + Art.PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                                            ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:239:23\n    │\n239 │             _fren(cv, uint24(combo));\n    │                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:261:22\n    │\n261 │         int256 dx = -int256(CX);\n    │                      ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:262:22\n    │\n262 │         int256 dy = -int256(CY);\n    │                      ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:321:56\n    │\n321 │         for (uint256 i; i < n; ++i) b[at + i] = bytes1(uint8(v >> (8 * i)));\n    │                                                        ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/frens/WorkerFrensRenderer.sol:370:49\n    │\n370 │             extcodecopy(p, add(raw, 32), add(1, mul(first, 33)), mul(frames, 33))\n    │                                                 ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:211:35\n    │\n211 │                 amountSpecified: -int256(amountIn),\n    │                                   ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:219:23\n    │\n219 │         uint256 pay = uint256(uint128(-paid));\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:219:31\n    │\n219 │         uint256 pay = uint256(uint128(-paid));\n    │                               ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:221:13\n    │\n221 │             poolManager.settle{value: pay}();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:225:13\n    │\n225 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:227:23\n    │\n227 │         uint256 out = uint256(uint128(got));\n    │                       ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:227:31\n    │\n227 │         uint256 out = uint256(uint128(got));\n    │                               ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:248:35\n    │\n248 │                 amountSpecified: -int256(ethIn),\n    │                                   ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:253:27\n    │\n253 │         uint256 ethPaid = uint256(uint128(-d1.amount0()));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:253:35\n    │\n253 │         uint256 ethPaid = uint256(uint128(-d1.amount0()));\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:254:26\n    │\n254 │         uint256 imdGot = uint256(uint128(d1.amount1()));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:254:34\n    │\n254 │         uint256 imdGot = uint256(uint128(d1.amount1()));\n    │                                  ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:261:35\n    │\n261 │                 amountSpecified: -int256(imdGot),\n    │                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:267:27\n    │\n267 │         uint256 imdPaid = uint256(uint128(-paid));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:267:35\n    │\n267 │         uint256 imdPaid = uint256(uint128(-paid));\n    │                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:268:23\n    │\n268 │         uint256 out = uint256(uint128(got));\n    │                       ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:268:31\n    │\n268 │         uint256 out = uint256(uint128(got));\n    │                               ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:270:9\n    │\n270 │         poolManager.settle{value: ethPaid}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenWorkerGate.sol:58:33\n   │\n58 │     constructor(address owner_, address frens_, address identity_, address strategy_) {\n   │                                 ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenWorkerGate.sol:58:68\n   │\n58 │     constructor(address owner_, address frens_, address identity_, address strategy_) {\n   │                                                                    ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[calls-loop]: external call inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:76:30\n   │\n76 │             address holder = identity.ownerOf(id);\n   │                              ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:77:78\n   │\n77 │             if (holder != msg.sender && (holder != strategy || msg.sender != ISeatStrategyMin(strategy).seatOperator()))\n   │                                                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:79:17\n   │\n79 │                 revert NotYours(id);\n   │                 ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:81:30\n   │\n81 │             if (claimed[id]) revert AlreadyClaimed(id);\n   │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/FrenWorkerGate.sol:105:9\n    │\n105 │         emit WlRootSet(root);\n    │         ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:312:9\n    │\n312 │         address owner_,\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:319:9\n    │\n319 │         address keeper_,\n    │         ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:320:9\n    │\n320 │         address relayer_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:318:9\n    │\n318 │         address imdPayTo_,\n    │         ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:321:9\n    │\n321 │         address priceTable_\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:314:9\n    │\n314 │         address imd6900_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:317:9\n    │\n317 │         address x402Proxy_,\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:316:9\n    │\n316 │         address permit2_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:313:9\n    │\n313 │         address imd_,\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:315:9\n    │\n315 │         address identity_,\n    │         ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:335:9\n    │\n335 │         emit ICreatorToken.TransferValidatorUpdated(address(0), DEFAULT_TRANSFER_VALIDATOR);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:372:16\n    │\n372 │         return uint8(table >> (8 * trait));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:377:16\n    │\n377 │         return uint8((uint256(combo) >> _shift(trait)) & ((1 << _bits(trait)) - 1));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:386:38\n    │\n386 │             if (minTiers[v] > TIERS) revert BadTraits();\n    │                                      ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/IMD6900Frens.sol:408:13\n    │\n408 │             chars += _rules[v].cap;\n    │             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:371:30\n    │\n371 │         if (trait >= TRAITS) revert BadTraits();\n    │                              ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-no-eth]: external call can be reentered before `floorImd` is updated\n    ╭▸ src/frens/IMD6900Frens.sol:849:13\n    │\n849 │         try IFrenSwapper(swapper).imdToImd6900{gas: FLOOR_BUY_GAS}(imdIn, minOut, address(this)) {}\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:985:9\n    │\n985 │         ITransferValidator721(validator).validateTransfer(msg.sender, from, to, id);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:561:9\n    │\n561 │         emit FrensMinted(requestId, minter, first, count, tier, paid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:859:9\n    │\n859 │         emit FloorBought(spent, 0, got, reserve);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:979:13\n    │\n979 │             revert LastFrenOut();\n    │             ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:557:82\n    │\n557 │         requests[requestId] = Request(minter, tier, lowTier, false, count, 0, 1, uint32(first), 0, 0);\n    │                                                                                  ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/FrenWorkerGate.sol:135:9\n    │\n135 │         emit PublicOpened(workerMinted);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/IMD6900Frens.sol:623:38\n    │\n623 │             h = keccak256(abi.encode(h, combos[i]));\n    │                                      ━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:615:13\n    │\n615 │         if (block.timestamp > deadline) revert BadVoucher();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:643:13\n    │\n643 │             emit Revealed(requestId, tokenId, combo, jobId, outputHash);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:632:28\n    │\n632 │             if (code != 0) revert BadCombo(code);\n    │                            ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:634:90\n    │\n634 │             if (valueOf(combo, CHARACTER) == 0 && pepe.minted + openLowTier >= pepe.cap) revert BadCombo(3);\n    │                                                                                          ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:627:22\n    │\n627 │         r.revealed = uint8(upTo);\n    │                      ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:665:53\n    │\n665 │         if (!r.jobApproved || _spent(r.jobNonce) || block.timestamp <= r.jobDeadline) return false;\n    │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:689:9\n    │\n689 │         emit JobPaid(requestId, msg.sender);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:715:13\n    │\n715 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours || _spent(nonce)) revert BadJob();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:715:53\n    │\n715 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours || _spent(nonce)) revert BadJob();\n    │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:731:9\n    │\n731 │         emit JobApproved(requestId, nonce, deadline, permitDigest, quoteDigest);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:722:25\n    │\n722 │         r.jobDeadline = uint40(deadline);\n    │                         ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint40' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/frens/IMD6900Frens.sol:873:9\n    │\n873 │         IFrenSwapper(swapper).ethToImd6900{value: ethIn}(minOut, address(this));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:895:9\n    │\n895 │         emit FloorBought(imdIn, ethIn, got, reserve);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/IMD6900Frens.sol:873:9\n    │\n873 │         IFrenSwapper(swapper).ethToImd6900{value: ethIn}(minOut, address(this));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:888:9\n    │\n888 │         emit WethUnwrapped(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:911:9\n    │\n911 │         emit Recycled(tokenId, msg.sender, paid, imdPaid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:934:9\n    │\n934 │         emit TreasuryBought(tokenId, msg.sender, paid, imdPaid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:955:35\n    │\n955 │     function setTransferValidator(address validator) external onlyCurator {\n    │                                   ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:957:9\n    │\n957 │         emit ICreatorToken.TransferValidatorUpdated(getTransferValidator(), validator);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n     ╭▸ src/frens/IMD6900Frens.sol:1016:26\n     │\n1016 │     function setRenderer(address r) external onlyCurator {\n     │                          ━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1019:9\n     │\n1019 │         emit Setting(\"renderer\", 0, r);\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1025:9\n     │\n1025 │         emit Setting(\"artFrozen\", 1, renderer);\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n     ╭▸ src/frens/IMD6900Frens.sol:1041:17\n     │\n1041 │                 revert BadTraits();\n     │                 ━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n     ╭▸ src/frens/IMD6900Frens.sol:1053:84\n     │\n1053 │             if (m[i] == 0 || m[i] > MAX_PER_REQUEST || (i > 0 && m[i] < m[i - 1])) revert BadTraits();\n     │                                                                                    ━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1069:9\n     │\n1069 │         emit Setting(\"royalty\", bps, address(0));\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:530:21\n    │\n530 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n531 │         bytes memory sig = _sign(frens, relayerKey, id, c, d);\n532 │         vm.warp(d + 1);\n    │         ────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:540:17\n    │\n540 │         vm.warp(block.timestamp + 30 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:685:50\n    │\n685 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:686:17\n    │\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:699:17\n    │\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:721:50\n    │\n721 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:722:17\n    │\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:737:50\n    │\n737 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:296:21\n    │\n296 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:742:17\n    │\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:753:17\n    │\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:760:17\n    │\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/FrensPlacedInvariants.t.sol:337:17\n    │\n337 │         vm.roll(block.number + bound(blocks, 0, 2));\n    │         ────────━━━━━━━━━━━━─────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/FrensPlacedInvariants.t.sol:372:17\n    │\n372 │         vm.roll(block.number + bound(blocks, 0, 2));\n    │         ────────━━━━━━━━━━━━─────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":3335,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/frens/IMD6900FrensValidator.fork.t.sol:IMD6900FrensValidatorForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 338.08µs (0.00ns CPU time)\n\nRan 1 test for test/frens/FrenSwapper.fork.t.sol:FrenSwapperForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 485.80µs (0.00ns CPU time)\n\nRan 1 test for test/frens/IMD6900FrensPermit2.fork.t.sol:IMD6900FrensPermit2ForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 497.12µs (0.00ns CPU time)\n\nRan 1 test for test/frens/FrenMinter.fork.t.sol:FrenMinterForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 880.12µs (0.00ns CPU time)\n\nRan 1 test for test/FrensPlacement.t.sol:FrensPlacementForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 2.71ms (0.00ns CPU time)\n\nRan 1 test for test/FrensResidualRisks.t.sol:FrensAverageResidualRiskTest\n[PASS] test_Risk_DustFirstPinsAverageAcrossOneHundredBlocks() (gas: 9697080)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.73ms (3.52ms CPU time)\n\nRan 8 tests for test/FrensLaunchReview.t.sol:FrensAuditReviewTest\n[PASS] test_Audit_GovernorCanCloseMintAgain() (gas: 463670)\n[PASS] test_Audit_OwnerOrGovernorCanBlockAllPeerTransfers() (gas: 1379074)\n[PASS] test_Fix_LapsedJobApprovalIsReleasedByTheReveal() (gas: 1037000)\n[PASS] test_Fix_LastFrenOutStaysOutSoFeesHaveAnOwner() (gas: 1203616)\n[PASS] test_Fix_LaunchAdoptsASwapperSeededAtThePoolsPrice() (gas: 2008131)\n[PASS] test_Fix_LaunchRefusesTheWrongArt() (gas: 18811)\n[PASS] test_Fix_LaunchReplacesASwapperSeededOffASkewedPrice() (gas: 2745326)\n[PASS] test_Fix_LaunchsOwnSwapperStartsAtThePoolsPrice() (gas: 237014)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 5.06ms (5.12ms CPU time)\n\nRan 9 tests for test/frens/IMD6900FrensValidator.t.sol:IMD6900FrensValidatorTest\n[PASS] test_AuthorizationDoesNotOutliveTheSale() (gas: 193293)\n[PASS] test_AuthorizationFromTwoTopLevelCallsIsLost() (gas: 86867)\nLogs:\n  sale authorized in a separate top-level call: blocked\n\n[PASS] test_AuthorizationIsForThatFrenOnly() (gas: 394696)\n[PASS] test_AuthorizationIsForThatOperatorOnly() (gas: 479809)\n[PASS] test_ConduitAloneIsBlocked() (gas: 53267)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 79352)\n[PASS] test_HolderSendsItHerself() (gas: 87060)\n[PASS] test_OnlyTheAuthorizerAuthorizes() (gas: 74587)\n[PASS] test_ZoneAuthorizedSaleGoesThroughInOneFrame() (gas: 124594)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 6.23ms (942.26µs CPU time)\n\nRan 18 tests for test/frens/FrenWorkerGate.t.sol:FrenWorkerGateTest\n[PASS] test_ARefusedMintTakesNothing() (gas: 104954)\n[PASS] test_AnNftCountsOnce_whoeverHoldsItLater() (gas: 151129)\n[PASS] test_CreditsGoWhereTheHolderSays() (gas: 639698)\n[PASS] test_NoGateNoWindow() (gas: 442079)\n[PASS] test_OnlyFrensSpends() (gas: 32613)\n[PASS] test_OnlyTheHolderClaims() (gas: 67063)\n[PASS] test_OpenPublic() (gas: 528969)\n[PASS] test_OwnerMintsBeforeTheOpening() (gas: 2296619)\n[PASS] test_SeatOperatorClaimsTheStrategysNfts() (gas: 138514)\n[PASS] test_TheCreditIsTheMinters() (gas: 576375)\n[PASS] test_WindowClosesAfter420() (gas: 32492721)\n[PASS] test_WindowNeedsACreditPerFren() (gas: 714583)\n[PASS] test_WindowNeverOvershoots() (gas: 36198264)\n[PASS] test_WlAndWorkersShareThe420() (gas: 619410)\n[PASS] test_WlOnlyAsListed_once() (gas: 254028)\n[PASS] test_WlRaisedAmount_claimsTheDifference() (gas: 226858)\n[PASS] test_WlRootOnlyTheOwner_andNoRootNoWl() (gas: 62252)\n[PASS] test_WlWalletsMintInTheWindow_asMuchAsListed() (gas: 750975)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 13.93ms (10.16ms CPU time)\n\nRan 6 tests for test/frens/FrenSwapperAverage.t.sol:FrenSwapperAverageTest\n[PASS] test_AFullBuyMovesItOneStep() (gas: 103606)\n[PASS] test_ASmallBuyMovesItInProportion() (gas: 52842)\n[PASS] test_DustMovesItNextToNothing() (gas: 7606082)\n[PASS] test_FloorRateIsTheLowerReading() (gas: 17282)\n[PASS] test_MovesDownToo() (gas: 52658)\n[PASS] test_OnceABlock() (gas: 91443)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 54.72ms (3.67ms CPU time)\n\nRan 3 tests for test/FrensLaunchReview.t.sol:FrensFactoryReviewTest\n[PASS] test_Audit_ConstructorLeavesTheDocumentedTeamSetup() (gas: 25294534)\n[PASS] test_FreshChainCallsOnlyContractsCreatedByTheLaunch() (gas: 28696930)\n[PASS] test_ProtectedFactoryDeploysBothLaunchesInOrder() (gas: 25493481)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 61.15ms (14.47ms CPU time)\n\nRan 13 tests for test/FrensRevisionRisks.t.sol:FrensRevisionRisksTest\n[PASS] test_Risk_DuplicatePendingNonceStrandsOnePayment() (gas: 1554719)\n[PASS] test_Risk_DustEthTakesFiftyBlockTurns() (gas: 6374674)\n[PASS] test_Risk_EthMintPushesItsOwnPendingEthPrice() (gas: 5152892)\nLogs:\n  shown IMD: 1587628858579375267300\n  paid IMD: 979046693648650397080\n  refunded IMD: 608582164930724870220\n\n[PASS] test_Risk_FloorBoundJobCostIsSocialised() (gas: 3564025)\n[PASS] test_Risk_FloorQuotesUseExtremeLimits() (gas: 6173436)\n[PASS] test_Risk_FloorViewOmitsJobRefund() (gas: 857288)\n[PASS] test_Risk_PayeeChangeLeavesExpiredPermitDigest() (gas: 742744)\n[PASS] test_Risk_PendingEthSpotPushDilutesExistingHolders() (gas: 5288831)\n[PASS] test_Risk_PushMintUnpushHasPositiveMarkedValue() (gas: 6578235)\nLogs:\n  round trip ETH cost: 135188802651085725\n  marked IMD surplus: 106904119598384566859\n\n[PASS] test_Risk_RecycleOmitsPendingEthPricedByTreasury() (gas: 1093066)\n[PASS] test_Risk_StrangerSpendsWorkersCredit() (gas: 1493190)\n[PASS] test_Risk_UnwiringSwapperAllowsCheapReserveCapture() (gas: 992877)\n[PASS] test_Risk_ZeroOrOneFeeLimitEqualsCurrentPrice() (gas: 6176076)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 65.66ms (10.05ms CPU time)\n\nRan 3 tests for test/FrensResidualRisks.t.sol:FrensResidualRisksTest\n[PASS] test_Risk_DuplicatePendingNonceStrandsOnePayment() (gas: 1554494)\n[PASS] test_Risk_PendingEthSpotPushDilutesExistingHolders() (gas: 5288577)\n[PASS] test_Risk_UnwiringSwapperAllowsCheapReserveCapture() (gas: 992804)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 74.59ms (2.79ms CPU time)\n\nRan 12 tests for test/FrensArtFraming.t.sol:FrensArtFramingTest\n[PASS] testFuzz_EntryPastTheIndexIsMissing(uint256) (runs: 256, μ: 13984, ~: 13943)\nLogs:\n  Bound result 2365124025249459686598956871796181862060570807612\n\n[PASS] testFuzz_UnknownBackgroundIsMissing(uint256) (runs: 256, μ: 35610, ~: 35592)\nLogs:\n  Bound result 257255416849912704561242\n\n[PASS] test_ATamperedChunkDrawsNothing() (gas: 90530)\n[PASS] test_AttributesDoNotValidate() (gas: 369298)\n[PASS] test_ChunksAreFramedPushData() (gas: 2731712)\n[PASS] test_EveryValueHasAName() (gas: 15679313)\n[PASS] test_IndexHashesAreTheChunks() (gas: 788314)\n[PASS] test_IndexPointsInsideTheChunks() (gas: 525933)\n[PASS] test_LaunchLayersAreWellFormed() (gas: 46186857)\n[PASS] test_NoSuchFrenIsMissing() (gas: 4375022)\n[PASS] test_PalettesFitTogether() (gas: 13989817)\n[PASS] test_WithoutTheSwarmNothingDraws() (gas: 5947823)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 74.61ms (262.68ms CPU time)\n\nRan 16 tests for test/FrensLaunchAdaptation.t.sol:FrensLaunchAdaptationTest\n[PASS] test_BootstrapThenOpenAndResume() (gas: 2315891)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\n[PASS] test_FirstFrensBeforeFeesPreventsPublicFloorCapture() (gas: 3435830)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_FirstFrensRequiresPausedBuysAndNonzeroCount() (gas: 1785691)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Fix_SpentNonceIsRefused() (gas: 2311018)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeRefuseAnOwnerlessFloor() (gas: 1828783)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeRejectChangedWiring() (gas: 2680591)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeWarnWhenAverageLags() (gas: 4784847)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  WARNING: the swapper's average is more than 2x off the pool's price\n  WARNING: the swapper's average is more than 2x off the pool's price\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\n[PASS] test_PlacedRejectsMissingOrWrongModules() (gas: 126575)\n[PASS] test_PlacedRejectsWrongCollectionAndForeignDependencies() (gas: 65561)\n[PASS] test_PlacedReportsTheLaunchModules() (gas: 51824)\n[PASS] test_ResumeRequiresDistributorAndActualSwapperExemption() (gas: 2164855)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Risk_FirstMintCapturesExistingFloorIfRunbookIsBypassed() (gas: 3054450)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Risk_LaunchBlockPushSeedsSwapperAndSetupRejectsAfterPriceReturns() (gas: 2572174)\n[PASS] test_Risk_UnsupportedRoyaltyTokenHasNoFloorRoute() (gas: 3781565)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_SetupPausesBeforeFirstMintEvenWhenAlreadyDistributor() (gas: 1815518)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_SetupUsesTheLaunchsReplacementSwapper() (gas: 6981779)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 76.03ms (18.79ms CPU time)\n\nRan 86 tests for test/frens/IMD6900Frens.t.sol:IMD6900FrensTest\n[PASS] test_AMintNeverFailsOnTheSwap() (gas: 1003226)\n[PASS] test_BatchCannotRepeatAFren() (gas: 641744)\n[PASS] test_BatchCountIsOneTo69() (gas: 2185647)\n[PASS] test_BatchCountsTheCapsAsItGoes() (gas: 11780401)\n[PASS] test_BatchHoldsPepesForLowTiers() (gas: 12176238)\n[PASS] test_BatchPaysOneJob() (gas: 700213)\n[PASS] test_BatchRevealIsExactlyTheVoucher() (gas: 1011974)\n[PASS] test_BatchRevealsThemAll() (gas: 2256463)\n[PASS] test_BatchSoldOut() (gas: 90792483)\n[PASS] test_BuyTreasuryRespectsMaxPay() (gas: 1842004)\n[PASS] test_BuyingFromTheTreasuryRaisesTheFloor() (gas: 3101386)\n[PASS] test_CurveWhileAboveTheFloor() (gas: 1599825)\n[PASS] test_DearerImd6900RaisesTheFloorPrice() (gas: 2861964)\n[PASS] test_DustEthBuyCannotTakeTheMintsTurn() (gas: 540029)\n[PASS] test_EveryFrenIsOneOfAKind() (gas: 947080)\n[PASS] test_FailedJob_payAnother() (gas: 1173029)\n[PASS] test_FeeEthBuysTheFloor() (gas: 1708734)\n[PASS] test_FloorBuysAreAnyones_onePerBlock() (gas: 1893053)\n[PASS] test_FloorBuysAreCapped() (gas: 55694600)\n[PASS] test_FloorIsReserveOverFrensOut() (gas: 2761464)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 2145216)\n[PASS] test_FrenSentStraightToTheTreasuryCounts() (gas: 2660288)\n[PASS] test_IsACreatorToken() (gas: 41743)\n[PASS] test_JobPaymentIsPinnedToImdsPayee() (gas: 980756)\n[PASS] test_KeeperApprovesTheJobPaidFor() (gas: 657241)\n[PASS] test_KeeperCannotApproveASpentNonce() (gas: 490236)\n[PASS] test_KeeperCannotApproveLongLivedPayments() (gas: 434019)\n[PASS] test_LapsedPaymentIsUsedAgain() (gas: 719034)\n[PASS] test_LastFrenOutStaysOut() (gas: 1615217)\n[PASS] test_LaterPartsAgreeOnWhatIsRevealed() (gas: 1347339)\n[PASS] test_LowTierRequestsNeedAPepeLeft() (gas: 11708397)\n[PASS] test_MaxMintGrowsWithTheBag() (gas: 4682663)\n[PASS] test_MaxPayStopsAPriceThatMoved() (gas: 10063499)\n[PASS] test_MaxRequest() (gas: 9863897)\n[PASS] test_MintMintsAtOnce_unrevealed() (gas: 770449)\n[PASS] test_MintNeedsSealedTraits() (gas: 8623021)\n[PASS] test_MintNeverBelowTheFloor() (gas: 3425403)\n[PASS] test_MintNeverSkipsTheBuyForWantOfGas() (gas: 708870)\n[PASS] test_NoMintIntoTheTreasury() (gas: 1596022)\n[PASS] test_NoRequestForNobody() (gas: 61912)\n[PASS] test_NoTierInsideAFlashLoan() (gas: 687636)\n[PASS] test_NobodyElseApprovesJobs() (gas: 435635)\n[PASS] test_OneRevealPerFren() (gas: 717718)\n[PASS] test_OnlyHoldersRecycle() (gas: 1593666)\n[PASS] test_OnlyRealFrens() (gas: 743319)\n[PASS] test_OnlyTheOwnerSetsTheValidator() (gas: 1063173)\n[PASS] test_OwnerAdjustsTheArtUntilFrozen() (gas: 1555030)\n[PASS] test_PairRules() (gas: 875556)\n[PASS] test_PartsOnlyGoForward() (gas: 1035497)\n[PASS] test_PendingEthCountsInTheMintPrice() (gas: 2181876)\n[PASS] test_PriceRisesWithMints() (gas: 38125610)\n[PASS] test_PriceTableIsTheCurve() (gas: 36977950)\n[PASS] test_QuoteCountsUnsweptImd() (gas: 3216110)\n[PASS] test_RareTraitsNeedTheTier() (gas: 1365734)\n[PASS] test_RecyclePaysTheFloorAndKeepsIt() (gas: 2915603)\n[PASS] test_RecyclePaysTheWaitingImdToo() (gas: 2749360)\n[PASS] test_ReleaseLapsedJobAfterTheReveal() (gas: 2142099)\n[PASS] test_RequestMintFor() (gas: 1084060)\n[PASS] test_ReserveOnlyLeavesThroughRecycle() (gas: 2838128)\n[PASS] test_RevealFollowsTheFren() (gas: 749220)\n[PASS] test_RevealInParts() (gas: 9747514)\nLogs:\n  gas, minting 69: 2117887\n  gas, revealing 30: 3092845\n\n[PASS] test_RevealNeedsTheRelayersVoucher() (gas: 803651)\n[PASS] test_RevealReleasesALapsedPayment() (gas: 919103)\n[PASS] test_RevealShowsTheFren() (gas: 1192018)\n[PASS] test_RevealsWhenever() (gas: 661560)\n[PASS] test_RoyaltiesPayTheFloor() (gas: 1724930)\n[PASS] test_RoyaltyBpsIsCappedAndOwnerOnly() (gas: 109412)\n[PASS] test_RulesAreFrozenOnceSealed() (gas: 69580)\n[PASS] test_SealNeedsEveryTraitAndTheSupply() (gas: 10518169)\n[PASS] test_SetMaxMint() (gas: 178238)\n[PASS] test_ShortSwapReverts() (gas: 1046881)\n[PASS] test_SwapperMayPullOnlyTheBuy() (gas: 957490)\n[PASS] test_TheOwnerKeepsTheCollection_theGovernorTheMechanics() (gas: 488664)\n[PASS] test_TierCountsWhatIsLeftAfterPaying() (gas: 920004)\n[PASS] test_TierIsTheBestOfTheBag() (gas: 633505)\n[PASS] test_TierIsTheMints() (gas: 1350710)\n[PASS] test_TierThresholdsOwnerOnlyAndOrdered() (gas: 154928)\n[PASS] test_TradesGoPastTheValidator() (gas: 1346243)\n[PASS] test_TraitCapsHold() (gas: 25778206)\n[PASS] test_TreasuryRoundTripCostsTheFloor() (gas: 3295585)\n[PASS] test_UnneededRetryFeedsTheFloor() (gas: 1026116)\n[PASS] test_UnrevealedFrenSellsAtTheFloor() (gas: 1022775)\n[PASS] test_UnrevealedFrensShareTheFloor() (gas: 1466205)\n[PASS] test_UnsweptImdCountsInTheTreasuryPrice() (gas: 3367000)\n[PASS] test_UnusedJobFeedsTheFloor() (gas: 696981)\n[PASS] test_VouchersExpire() (gas: 472492)\nSuite result: ok. 86 passed; 0 failed; 0 skipped; finished in 90.31ms (236.73ms CPU time)\n\nRan 19 tests for test/FrensLaunchFailures.t.sol:FrensLaunchFailuresTest\n[PASS] testFuzz_CheckKnowsAFren(uint24,uint8) (runs: 512, μ: 1942139, ~: 1907913)\nLogs:\n  Bound result 0\n\n[PASS] testFuzz_OtherSaltsLandElsewhere(bytes32) (runs: 256, μ: 34990, ~: 34990)\n[PASS] testFuzz_QuoteIsTheCurveBeforeAnyoneHolds(uint256) (runs: 512, μ: 318182, ~: 317675)\nLogs:\n  Bound result 13\n\n[PASS] test_AnotherOwnerMovesTheFrens() (gas: 34259)\n[PASS] test_CollectionRefusesAShortPriceTable() (gas: 3059925)\n[PASS] test_CollectionWaitsForTheTeamWallet() (gas: 5941480)\n[PASS] test_EveryPriceIsTheTables() (gas: 27580866)\n[PASS] test_FreshChainLaunchesAreIndependent() (gas: 1351085)\n[PASS] test_LaunchContractsTakeNoEth() (gas: 687657)\n[PASS] test_ModulesFailOverCorruptedFrens() (gas: 97101)\n[PASS] test_ModulesNeedTheCollection() (gas: 106270)\n[PASS] test_NoLaunchActorHoldsARole() (gas: 4580578)\n[PASS] test_NoPricePastTheTable() (gas: 41219)\n[PASS] test_OtherSaltSchemeFailsTheLaunch() (gas: 140656)\n[PASS] test_RefusingDeployerFailsTheLaunch() (gas: 103369)\n[PASS] test_ScriptRefusesAnUnplacedChain() (gas: 14786192)\n[PASS] test_SecondLaunchReusesWhatIsPlaced() (gas: 1313404)\n[PASS] test_SilentDeployerFailsTheLaunch() (gas: 93234)\n[PASS] test_WrongArtFailsTheArtLaunch() (gas: 588910)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 90.34ms (259.50ms CPU time)\n\nRan 13 tests for test/FrensPlacement.t.sol:FrensPlacementTest\n[PASS] test_Attributes() (gas: 19594305)\n[PASS] test_CodeIsWhatTheSourcesBuild() (gas: 89696)\n[PASS] test_DeploysOnAFreshChain() (gas: 51811985)\n[PASS] test_EachLaunchFitsOneTransaction() (gas: 105863223)\nLogs:\n  the collection launch: gas, all in: 12451232\n  the art launch: gas, all in: 13605058\n\n[PASS] test_LandsWhereThePlanSays() (gas: 51923654)\n[PASS] test_NewArtReadsBackExactly() (gas: 14173584)\n[PASS] test_PassesTheAdmissionScan() (gas: 88302675)\n[PASS] test_PlanFollowsFromTheCode() (gas: 80750)\n[PASS] test_PlanMatchesTheScript() (gas: 14314057)\n[PASS] test_PriceTableIsTheCurve() (gas: 53105398)\n[PASS] test_RefusesOtherArt() (gas: 12971045)\n[PASS] test_SameAddressesWhoeverRunsIt() (gas: 102774643)\n[PASS] test_TakesWhatSomeonePlacedFirst() (gas: 52491481)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 166.95ms (307.25ms CPU time)\n\nRan 1 test for test/FrensSplitGas.t.sol:FrensSplitGasTest\n[PASS] test_CollectionFitsButLegacyFourContractBundleExceedsCap() (gas: 101750011)\nLogs:\n  legacy four-contract launch: 23877683\n  two-contract collection launch: 13494921\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 171.60ms (171.47ms CPU time)\n\nRan 1 test for test/FrensPlacedInvariants.t.sol:FrensPlacedInvariantsTest\n[PASS]\nFrensPlacedInvariantsTest invariants:\n[PASS] invariant_CapsHold\n[PASS] invariant_FloorIsAShareOfTheWorld\n[PASS] invariant_FrensAreConserved\n[PASS] invariant_ImdIsTheBooks\n[PASS] invariant_ReserveIsExactlyWhatIsHere\n FrensPlacedInvariantsTest invariants (runs: 32, calls: 1920, reverts: 5)\n\n╭--------------------+-----------------+-------+---------+----------╮\n| Contract           | Selector        | Calls | Reverts | Discards |\n+===================================================================+\n| PlacedFrensHandler | approveJob      | 116   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyFloor        | 128   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyTreasury     | 117   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donate6900      | 133   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donateImd       | 128   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | feeEth          | 121   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mint            | 215   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mintThenRecycle | 110   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | recycle         | 129   | 5       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | retryJob        | 105   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | reveal          | 129   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setParams       | 129   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setRate         | 102   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | transfer        | 124   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | warp            | 134   | 0       | 0        |\n╰--------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 2\n  Bound result 1\n  Bound result 166666666666666666\n  Bound result 2\n  Bound result 32\n  Bound result 1\n  Bound result 9405\n  Bound result 108173672871127110\n  Bound result 1\n  Bound result 1\n  Bound result 82\n  Bound result 0\n  Bound result 3\n  Bound result 560\n  Bound result 7667\n  Bound result 19\n  Bound result 2454\n  Bound result 62\n  Bound result 48\n  Bound result 39\n  Bound result 1\n  Bound result 12658395828604242698\n  Bound result 3523\n  Bound result 867461\n  Bound result 0\n  Bound result 197269688662682920129\n  Bound result 16605\n  Bound result 892\n  Bound result 100\n  Bound result 5600\n  Bound result 0\n  Bound result 1125\n  Bound result 4796\n  Bound result 1\n  Bound result 13352582426266607304\n  Bound result 241954406270753264\n  Bound result 11\n  Bound result 56\n  Bound result 2287\n  Bound result 937\n  Bound result 0\n  Bound result 17\n  Bound result 63\n  Bound result 716689171253286393260\n  Bound result 32\n  Bound result 1\n  Bound result 244\n  Bound result 68\n  Bound result 12\n  Bound result 1\n  Bound result 100\n  Bound result 760000000000000000\n  Bound result 3596\n  Bound result 448\n  Bound result 11741\n  Bound result 120900153520848454\n  Bound result 1\n  Bound result 1520\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.21s (3.20s CPU time)\n\nRan 20 test suites in 3.21s (4.17s CPU time): 209 tests passed, 0 failed, 5 skipped (214 total tests)\n","passed":true},{"durationMs":133,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"FrenMinter.buyImd(uint256)\",\"FrenMinter.mintWithEth(uint8,uint256)\",\"FrenMinter.quoteEth(uint8)\",\"FrenMinter.quoteFloor(uint256)\",\"FrenMinter.quoteFloorEth(uint256)\",\"FrenMinter.unlockCallback(bytes)\",\"FrenSwapper.ethToImd6900(uint256,address)\",\"FrenSwapper.imdToImd6900(uint256,uint256,address)\",\"FrenSwapper.unlockCallback(bytes)\",\"FrenWorkerGate.cancelOwnershipHandover()\",\"FrenWorkerGate.claim(uint256[],address)\",\"FrenWorkerGate.claimWl(uint256,bytes32[],address)\",\"FrenWorkerGate.completeOwnershipHandover(address)\",\"FrenWorkerGate.openPublic()\",\"FrenWorkerGate.renounceOwnership()\",\"FrenWorkerGate.requestOwnershipHandover()\",\"FrenWorkerGate.setWlRoot(bytes32)\",\"FrenWorkerGate.spend(address,uint256)\",\"FrenWorkerGate.transferOwnership(address)\",\"IMD6900Frens.addPairRule((uint8,uint8,uint8,uint8,uint8))\",\"IMD6900Frens.approve(address,uint256)\",\"IMD6900Frens.approveJob(uint256,uint256,uint256,(string,bytes32,string,bytes32,bytes32,string,uint256))\",\"IMD6900Frens.buyFloor(uint256)\",\"IMD6900Frens.buyFloorWithEth(uint256,uint256)\",\"IMD6900Frens.buyTreasury(uint256,uint256,uint256)\",\"IMD6900Frens.cancelOwnershipHandover()\",\"IMD6900Frens.completeOwnershipHandover(address)\",\"IMD6900Frens.freezeArt()\",\"IMD6900Frens.receive()\",\"IMD6900Frens.recycle(uint256)\",\"IMD6900Frens.releaseLapsedJob(uint256)\",\"IMD6900Frens.renounceOwnership()\",\"IMD6900Frens.requestMint(uint8,uint256)\",\"IMD6900Frens.requestMintFor(address,uint8,uint256)\",\"IMD6900Frens.requestOwnershipHandover()\",\"IMD6900Frens.retryJob(uint256)\",\"IMD6900Frens.reveal(uint256,uint24[],string,bytes32,uint256,bytes,uint256)\",\"IMD6900Frens.safeTransferFrom(address,address,uint256)\",\"IMD6900Frens.safeTransferFrom(address,address,uint256,bytes)\",\"IMD6900Frens.sealTraits()\",\"IMD6900Frens.setApprovalForAll(address,bool)\",\"IMD6900Frens.setGovernor(address)\",\"IMD6900Frens.setMaxMint(uint8[4])\",\"IMD6900Frens.setMintOpen(bool)\",\"IMD6900Frens.setModules(address,address)\",\"IMD6900Frens.setParams(uint256,uint256,uint256)\",\"IMD6900Frens.setRenderer(address)\",\"IMD6900Frens.setRoles(address,address,address)\",\"IMD6900Frens.setRoyalty(uint96)\",\"IMD6900Frens.setTiers(uint256[3],uint256[3],uint256[3])\",\"IMD6900Frens.setTraitRules(uint8,uint16[],uint8[])\",\"IMD6900Frens.setTransferValidator(address)\",\"IMD6900Frens.transferFrom(address,address,uint256)\",\"IMD6900Frens.transferOwnership(address)\",\"IMD6900Frens.unwrapWeth()\"],\"files\":{\".gitignore\":4,\"ADAPTATION.md\":215,\"README.md\":218,\"foundry.toml\":22,\"launch.json\":16,\"script/art/chunks.py\":165,\"script/art/data/bgpal/cleanlab_blue.bin\":1,\"script/art/data/bgpal/cleanlab_green.bin\":1,\"script/art/data/bgpal/cleanlab_red.bin\":1,\"script/art/data/bgpal/messylab_blue.bin\":1,\"script/art/data/bgpal/messylab_green.bin\":1,\"script/art/data/bgpal/messylab_red.bin\":1,\"script/art/data/bgpal/tubeblue.bin\":1,\"script/art/data/bgpal/tubegreen.bin\":2,\"script/art/data/bgpal/tubered.bin\":1,\"script/art/data/bgpal/tubeyellow.bin\":1,\"script/art/data/bgpal/wireframe_green.bin\":2,\"script/art/data/bgpal/wireframe_red.bin\":1,\"script/art/data/expected.json\":53,\"script/art/data/layers/bg0.bin\":9,\"script/art/data/layers/bg1.bin\":9,\"script/art/data/layers/bg10.bin\":32,\"script/art/data/layers/bg11.bin\":32,\"script/art/data/layers/bg2.bin\":9,\"script/art/data/layers/bg3.bin\":15,\"script/art/data/layers/bg4.bin\":15,\"script/art/data/layers/bg5.bin\":15,\"script/art/data/layers/bg6.bin\":26,\"script/art/data/layers/bg7.bin\":26,\"script/art/data/layers/bg8.bin\":26,\"script/art/data/layers/bg9.bin\":26,\"script/art/data/layers/coat.bin\":20,\"script/art/data/layers/item5.bin\":2,\"script/art/data/manifest.json\":500,\"script/art/data/shared.bin\":4,\"script/art/data/tables.bin\":2,\"script/art/swarm-v2.json\":374,\"script/frens/DeployFrens.s.sol\":254,\"script/frens/FrensTimelockBatch.s.sol\":105,\"script/frens/price/prices-swarm.bin\":6,\"script/frens/price/prices.bin\":6,\"script/frens/price/prices.json\":26,\"script/frens/price/prices.py\":55,\"script/frens/price/prices_sol.py\":33,\"script/placement/addresses.json\":7,\"script/placement/gen.py\":146,\"src/FrensCode.sol\":15,\"src/FrensPlacement.sol\":129,\"src/FrensPlan.sol\":37,\"src/art/WorkerArt.sol\":671,\"src/art/WorkerArtIndex.sol\":56,\"src/frens/FrenMinter.sol\":194,\"src/frens/FrenPrices.sol\":122,\"src/frens/FrenSwapper.sol\":276,\"src/frens/FrenWorkerGate.sol\":137,\"src/frens/IMD6900Frens.sol\":1083,\"src/frens/WorkerFrensRenderer.sol\":377,\"test/FrensArtFraming.t.sol\":290,\"test/FrensLaunchAdaptation.t.sol\":331,\"test/FrensLaunchFailures.t.sol\":526,\"test/FrensLaunchReview.t.sol\":449,\"test/FrensPlacedInvariants.t.sol\":634,\"test/FrensPlacement.t.sol\":818,\"test/FrensResidualRisks.t.sol\":210,\"test/FrensRevisionRisks.t.sol\":336,\"test/FrensSplitGas.t.sol\":68,\"test/frens/DataWriter.sol\":21,\"test/frens/FrenMinter.fork.t.sol\":189,\"test/frens/FrenSwapper.fork.t.sol\":134,\"test/frens/FrenSwapperAverage.t.sol\":77,\"test/frens/FrenWorkerGate.t.sol\":350,\"test/frens/FrensRules.sol\":75,\"test/frens/IMD6900Frens.t.sol\":1763,\"test/frens/IMD6900FrensPermit2.fork.t.sol\":130,\"test/frens/IMD6900FrensValidator.fork.t.sol\":146,\"test/frens/IMD6900FrensValidator.t.sol\":231,\"test/test_collection_manifest.py\":56},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"4cf041cb4d34bd5b4a4f4680c21f4985275db48ec080fc6ddc71335b3f87716d","verifiedTreeHash":"206fc78e6c7b16667ff66114ceb55a4dcd444506","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":153301,"exitCode":0,"name":"build","output":"Compiling 97 files with Solc 0.8.30\nSolc 0.8.30 finished in 153.10s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> test/FrensPlacement.t.sol:786:9:\n    |\n786 |         uint256 s = _find(j, key, 0) + key.length;\n    |         ^^^^^^^^^\nNote: The shadowed declaration is here:\n   --> test/FrensPlacement.t.sol:477:5:\n    |\n477 |     DeployFrens s;\n    |     ^^^^^^^^^^^^^\n\nWarning (2018): Function state mutability can be restricted to view\n   --> test/FrensLaunchAdaptation.t.sol:199:5:\n    |\n199 |     function test_PlacedReportsTheLaunchModules() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:72:9\n   │\n72 │         address imd_,\n   │         ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:75:9\n   │\n75 │         address pairHook_,\n   │         ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/frens/WorkerFrensRenderer.sol:59:29\n   │\n59 │           bytes memory json = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n60 │ ┃             '{\"name\":\"Worker Fren #',\n61 │ ┃             tokenId.toString(),\n62 │ ┃             '\",\"description\":\"',\n   ‡ ┃\n68 │ ┃             \"}\"\n69 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:74:9\n   │\n74 │         address frens_,\n   │         ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:73:9\n   │\n73 │         address imd6900_,\n   │         ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:76:9\n   │\n76 │         address imdPoolHook_\n   │         ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/frens/WorkerFrensRenderer.sol:75:29\n   │\n75 │           bytes memory json = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n76 │ ┃             '{\"name\":\"Worker Fren #',\n77 │ ┃             tokenId.toString(),\n78 │ ┃             '\",\"description\":\"',\n   ‡ ┃\n82 │ ┃             '\",\"attributes\":[{\"trait_type\":\"Status\",\"value\":\"Unrevealed\"}]}'\n83 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/WorkerFrensRenderer.sol:125:54\n    │\n125 │                     bytes memory out = new bytes(j - start);\n    │                                                      ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/WorkerFrensRenderer.sol:124:21\n    │\n124 │                 if (k == i) {\n    │                     ━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/frens/WorkerFrensRenderer.sol:158:17\n    │\n158 │ ┏                 abi.encodePacked(\n159 │ ┃                     '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 84 84\" width=\"840\" height=\"840\">',\n160 │ ┃                     '<image width=\"84\" height=\"',\n161 │ ┃                     (N * PENDING_FRAMES).toString(),\n    ‡ ┃\n165 │ ┃                     'dur=\"1.3s\" calcMode=\"discrete\" repeatCount=\"indefinite\"/></image></svg>'\n166 │ ┃                 )\n    │ ┗━━━━━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:208:35\n    │\n208 │                 bytes1 v = bytes1(uint8(l * 3 / 5));\n    │                                   ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:221:42\n    │\n221 │         _draw(cv, _entry(Art.BG0 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                          ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:221:62\n    │\n221 │         _draw(cv, _entry(Art.BG0 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                              ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:253:74\n    │\n253 │         if (ch > 2 || face >= FACES || coat > 2 || shirt > 5 || hat > 2) revert Missing();\n    │                                                                          ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:350:31\n    │\n350 │         if (i >= Art.ENTRIES) revert Missing();\n    │                               ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:356:72\n    │\n356 │         if (p.codehash != bytes32(_hex(Art.CHUNK_HASHES, c * 64, 64))) revert BadArt();\n    │                                                                        ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:288:25\n    │\n288 │                         revert(28, 4)\n    │                         ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:232:56\n    │\n232 │         _draw(back, _entry(Art.BG0 + Art.PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                        ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:232:76\n    │\n232 │         _draw(back, _entry(Art.BG0 + Art.PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                                            ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:239:23\n    │\n239 │             _fren(cv, uint24(combo));\n    │                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:261:22\n    │\n261 │         int256 dx = -int256(CX);\n    │                      ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:262:22\n    │\n262 │         int256 dy = -int256(CY);\n    │                      ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:321:56\n    │\n321 │         for (uint256 i; i < n; ++i) b[at + i] = bytes1(uint8(v >> (8 * i)));\n    │                                                        ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/frens/WorkerFrensRenderer.sol:370:49\n    │\n370 │             extcodecopy(p, add(raw, 32), add(1, mul(first, 33)), mul(frames, 33))\n    │                                                 ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/frens/IMD6900Frens.sol:964:66\n    │\n964 │         return (ITransferValidator721.validateTransfer.selector, true);\n    │                                                                  ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/FrensPlacement.sol:54:13\n   │\n54 │ ┏             abi.encodePacked(\n55 │ ┃                 FrensCode.FRENS,\n56 │ ┃                 abi.encode(\n57 │ ┃                     FrensPlan.OWNER,\n   ‡ ┃\n68 │ ┃             )\n   │ ┗━━━━━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/FrensPlacement.sol:93:29\n   │\n93 │           bytes memory init = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n94 │ ┃             FrensCode.SWAPPER,\n95 │ ┃             abi.encode(\n96 │ ┃                 FrensPlan.POOL_MANAGER, FrensPlan.IMD, FrensPlan.IMD6900, f, FrensPlan.PAIR_HOOK, FrensPlan.POOL4_HOOK\n97 │ ┃             )\n98 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/FrensPlacement.sol:109:13\n    │\n109 │ ┏             abi.encodePacked(\n110 │ ┃                 FrensCode.MINTER, abi.encode(FrensPlan.POOL_MANAGER, f, FrensPlan.POOL4_HOOK, FrensPlan.PAIR_HOOK)\n111 │ ┃             )\n    │ ┗━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/FrensPlacement.sol:115:13\n    │\n115 │             abi.encodePacked(FrensCode.GATE, abi.encode(FrensPlan.OWNER, f, FrensPlan.IDENTITY, FrensPlan.IMD6900))\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:177:16\n    │\n177 │         return uint160(zeroForOne ? uint256(p) * 1e18 / f : uint256(p) * f / 1e18);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:55\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                                       ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:77\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                                                             ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:39\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                       ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:118:35\n    │\n118 │             if (r.length == 68 && bytes4(r) == Quoted.selector) {\n    │                                   ━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[missing-events-access-control]: `keeper` is changed without an event but is used for access control\n     ╭▸ src/frens/IMD6900Frens.sol:1009:36\n     │\n1009 │         if (keeper_ != address(0)) keeper = keeper_;\n     │                                    ━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `governor` is changed without an event but is used for access control\n     ╭▸ src/frens/IMD6900Frens.sol:1075:9\n     │\n1075 │         governor = g;\n     │         ━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:138:79\n    │\n138 │ …     imdKey(), SwapParams({zeroForOne: true, amountSpecified: -int256(amount), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1}), \"\"\n    │                                                                 ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:140:25\n    │\n140 │                 imdIn = uint256(uint128(e.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:140:33\n    │\n140 │                 imdIn = uint256(uint128(e.amount1()));\n    │                                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:148:39\n    │\n148 │                     amountSpecified: -int256(imdIn),\n    │                                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:153:35\n    │\n153 │             revert Quoted(amount, uint256(uint128(imdIs0 ? p.amount1() : p.amount0())));\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:153:43\n    │\n153 │             revert Quoted(amount, uint256(uint128(imdIs0 ? p.amount1() : p.amount0())));\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:158:70\n    │\n158 │ …     imdKey(), SwapParams({zeroForOne: true, amountSpecified: int256(imdOut), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1}), \"\"\n    │                                                                ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:160:25\n    │\n160 │         uint256 ethIn = uint256(uint128(-d.amount0()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:160:33\n    │\n160 │         uint256 ethIn = uint256(uint128(-d.amount0()));\n    │                                 ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:161:23\n    │\n161 │         uint256 got = uint256(uint128(d.amount1()));\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:161:31\n    │\n161 │         uint256 got = uint256(uint128(d.amount1()));\n    │                               ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenMinter.sol:165:9\n    │\n165 │         poolManager.settle{value: ethIn}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/FrenMinter.sol:183:29\n    │\n183 │             if (!withEth && cost > a) continue;\n    │                             ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:178:17\n    │\n178 │             try frens.quote(n) returns (uint256 q) {\n    │                 ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:22\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                      ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:51\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                                                   ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:84\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                                                                                    ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:184:22\n    │\n184 │             if (n <= frens.maxMint(_tier(withEth ? a : a - cost, b, c))) return n;\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:211:35\n    │\n211 │                 amountSpecified: -int256(amountIn),\n    │                                   ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:219:23\n    │\n219 │         uint256 pay = uint256(uint128(-paid));\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:219:31\n    │\n219 │         uint256 pay = uint256(uint128(-paid));\n    │                               ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:221:13\n    │\n221 │             poolManager.settle{value: pay}();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:225:13\n    │\n225 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:227:23\n    │\n227 │         uint256 out = uint256(uint128(got));\n    │                       ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:227:31\n    │\n227 │         uint256 out = uint256(uint128(got));\n    │                               ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:248:35\n    │\n248 │                 amountSpecified: -int256(ethIn),\n    │                                   ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:253:27\n    │\n253 │         uint256 ethPaid = uint256(uint128(-d1.amount0()));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:253:35\n    │\n253 │         uint256 ethPaid = uint256(uint128(-d1.amount0()));\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:254:26\n    │\n254 │         uint256 imdGot = uint256(uint128(d1.amount1()));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:254:34\n    │\n254 │         uint256 imdGot = uint256(uint128(d1.amount1()));\n    │                                  ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:261:35\n    │\n261 │                 amountSpecified: -int256(imdGot),\n    │                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:267:27\n    │\n267 │         uint256 imdPaid = uint256(uint128(-paid));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:267:35\n    │\n267 │         uint256 imdPaid = uint256(uint128(-paid));\n    │                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:268:23\n    │\n268 │         uint256 out = uint256(uint128(got));\n    │                       ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:268:31\n    │\n268 │         uint256 out = uint256(uint128(got));\n    │                               ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:270:9\n    │\n270 │         poolManager.settle{value: ethPaid}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenWorkerGate.sol:58:68\n   │\n58 │     constructor(address owner_, address frens_, address identity_, address strategy_) {\n   │                                                                    ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenWorkerGate.sol:58:33\n   │\n58 │     constructor(address owner_, address frens_, address identity_, address strategy_) {\n   │                                 ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[calls-loop]: external call inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:76:30\n   │\n76 │             address holder = identity.ownerOf(id);\n   │                              ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:77:78\n   │\n77 │             if (holder != msg.sender && (holder != strategy || msg.sender != ISeatStrategyMin(strategy).seatOperator()))\n   │                                                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:79:17\n   │\n79 │                 revert NotYours(id);\n   │                 ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:81:30\n   │\n81 │             if (claimed[id]) revert AlreadyClaimed(id);\n   │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/FrenWorkerGate.sol:105:9\n    │\n105 │         emit WlRootSet(root);\n    │         ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/FrenWorkerGate.sol:135:9\n    │\n135 │         emit PublicOpened(workerMinted);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:321:9\n    │\n321 │         address priceTable_\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:312:9\n    │\n312 │         address owner_,\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:316:9\n    │\n316 │         address permit2_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:318:9\n    │\n318 │         address imdPayTo_,\n    │         ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:313:9\n    │\n313 │         address imd_,\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:315:9\n    │\n315 │         address identity_,\n    │         ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:317:9\n    │\n317 │         address x402Proxy_,\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:319:9\n    │\n319 │         address keeper_,\n    │         ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:320:9\n    │\n320 │         address relayer_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:314:9\n    │\n314 │         address imd6900_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:335:9\n    │\n335 │         emit ICreatorToken.TransferValidatorUpdated(address(0), DEFAULT_TRANSFER_VALIDATOR);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:372:16\n    │\n372 │         return uint8(table >> (8 * trait));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:377:16\n    │\n377 │         return uint8((uint256(combo) >> _shift(trait)) & ((1 << _bits(trait)) - 1));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:386:38\n    │\n386 │             if (minTiers[v] > TIERS) revert BadTraits();\n    │                                      ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/IMD6900Frens.sol:408:13\n    │\n408 │             chars += _rules[v].cap;\n    │             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:371:30\n    │\n371 │         if (trait >= TRAITS) revert BadTraits();\n    │                              ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-no-eth]: external call can be reentered before `floorImd` is updated\n    ╭▸ src/frens/IMD6900Frens.sol:849:13\n    │\n849 │         try IFrenSwapper(swapper).imdToImd6900{gas: FLOOR_BUY_GAS}(imdIn, minOut, address(this)) {}\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:985:9\n    │\n985 │         ITransferValidator721(validator).validateTransfer(msg.sender, from, to, id);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:561:9\n    │\n561 │         emit FrensMinted(requestId, minter, first, count, tier, paid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:859:9\n    │\n859 │         emit FloorBought(spent, 0, got, reserve);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:979:13\n    │\n979 │             revert LastFrenOut();\n    │             ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:557:82\n    │\n557 │         requests[requestId] = Request(minter, tier, lowTier, false, count, 0, 1, uint32(first), 0, 0);\n    │                                                                                  ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/IMD6900Frens.sol:623:38\n    │\n623 │             h = keccak256(abi.encode(h, combos[i]));\n    │                                      ━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:615:13\n    │\n615 │         if (block.timestamp > deadline) revert BadVoucher();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:643:13\n    │\n643 │             emit Revealed(requestId, tokenId, combo, jobId, outputHash);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:632:28\n    │\n632 │             if (code != 0) revert BadCombo(code);\n    │                            ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:634:90\n    │\n634 │             if (valueOf(combo, CHARACTER) == 0 && pepe.minted + openLowTier >= pepe.cap) revert BadCombo(3);\n    │                                                                                          ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:627:22\n    │\n627 │         r.revealed = uint8(upTo);\n    │                      ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:665:53\n    │\n665 │         if (!r.jobApproved || _spent(r.jobNonce) || block.timestamp <= r.jobDeadline) return false;\n    │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:689:9\n    │\n689 │         emit JobPaid(requestId, msg.sender);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:715:13\n    │\n715 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours || _spent(nonce)) revert BadJob();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:715:53\n    │\n715 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours || _spent(nonce)) revert BadJob();\n    │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:731:9\n    │\n731 │         emit JobApproved(requestId, nonce, deadline, permitDigest, quoteDigest);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:722:25\n    │\n722 │         r.jobDeadline = uint40(deadline);\n    │                         ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint40' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/frens/IMD6900Frens.sol:873:9\n    │\n873 │         IFrenSwapper(swapper).ethToImd6900{value: ethIn}(minOut, address(this));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:895:9\n    │\n895 │         emit FloorBought(imdIn, ethIn, got, reserve);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/IMD6900Frens.sol:873:9\n    │\n873 │         IFrenSwapper(swapper).ethToImd6900{value: ethIn}(minOut, address(this));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:888:9\n    │\n888 │         emit WethUnwrapped(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:911:9\n    │\n911 │         emit Recycled(tokenId, msg.sender, paid, imdPaid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:934:9\n    │\n934 │         emit TreasuryBought(tokenId, msg.sender, paid, imdPaid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:955:35\n    │\n955 │     function setTransferValidator(address validator) external onlyCurator {\n    │                                   ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:957:9\n    │\n957 │         emit ICreatorToken.TransferValidatorUpdated(getTransferValidator(), validator);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n     ╭▸ src/frens/IMD6900Frens.sol:1016:26\n     │\n1016 │     function setRenderer(address r) external onlyCurator {\n     │                          ━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1019:9\n     │\n1019 │         emit Setting(\"renderer\", 0, r);\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1025:9\n     │\n1025 │         emit Setting(\"artFrozen\", 1, renderer);\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n     ╭▸ src/frens/IMD6900Frens.sol:1041:17\n     │\n1041 │                 revert BadTraits();\n     │                 ━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n     ╭▸ src/frens/IMD6900Frens.sol:1053:84\n     │\n1053 │             if (m[i] == 0 || m[i] > MAX_PER_REQUEST || (i > 0 && m[i] < m[i - 1])) revert BadTraits();\n     │                                                                                    ━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1069:9\n     │\n1069 │         emit Setting(\"royalty\", bps, address(0));\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:530:21\n    │\n530 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n531 │         bytes memory sig = _sign(frens, relayerKey, id, c, d);\n532 │         vm.warp(d + 1);\n    │         ────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:540:17\n    │\n540 │         vm.warp(block.timestamp + 30 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:685:50\n    │\n685 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:686:17\n    │\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:699:17\n    │\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:721:50\n    │\n721 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:722:17\n    │\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:737:50\n    │\n737 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:296:21\n    │\n296 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:742:17\n    │\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:753:17\n    │\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:760:17\n    │\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/FrensPlacedInvariants.t.sol:337:17\n    │\n337 │         vm.roll(block.number + bound(blocks, 0, 2));\n    │         ────────━━━━━━━━━━━━─────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/FrensPlacedInvariants.t.sol:372:17\n    │\n372 │         vm.roll(block.number + bound(blocks, 0, 2));\n    │         ────────━━━━━━━━━━━━─────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":6545,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/frens/IMD6900FrensValidator.fork.t.sol:IMD6900FrensValidatorForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 288.02µs (0.00ns CPU time)\n\nRan 1 test for test/frens/IMD6900FrensPermit2.fork.t.sol:IMD6900FrensPermit2ForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 414.90µs (0.00ns CPU time)\n\nRan 1 test for test/frens/FrenMinter.fork.t.sol:FrenMinterForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 587.09µs (0.00ns CPU time)\n\nRan 1 test for test/frens/FrenSwapper.fork.t.sol:FrenSwapperForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 690.09µs (0.00ns CPU time)\n\nRan 1 test for test/FrensPlacement.t.sol:FrensPlacementForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 2.63ms (0.00ns CPU time)\n\nRan 1 test for test/FrensResidualRisks.t.sol:FrensAverageResidualRiskTest\n[PASS] test_Risk_DustFirstPinsAverageAcrossOneHundredBlocks() (gas: 9697080)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.17ms (4.01ms CPU time)\n\nRan 8 tests for test/FrensLaunchReview.t.sol:FrensAuditReviewTest\n[PASS] test_Audit_GovernorCanCloseMintAgain() (gas: 463670)\n[PASS] test_Audit_OwnerOrGovernorCanBlockAllPeerTransfers() (gas: 1379074)\n[PASS] test_Fix_LapsedJobApprovalIsReleasedByTheReveal() (gas: 1037000)\n[PASS] test_Fix_LastFrenOutStaysOutSoFeesHaveAnOwner() (gas: 1203616)\n[PASS] test_Fix_LaunchAdoptsASwapperSeededAtThePoolsPrice() (gas: 2008131)\n[PASS] test_Fix_LaunchRefusesTheWrongArt() (gas: 18811)\n[PASS] test_Fix_LaunchReplacesASwapperSeededOffASkewedPrice() (gas: 2745326)\n[PASS] test_Fix_LaunchsOwnSwapperStartsAtThePoolsPrice() (gas: 237014)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 5.77ms (3.78ms CPU time)\n\nRan 9 tests for test/frens/IMD6900FrensValidator.t.sol:IMD6900FrensValidatorTest\n[PASS] test_AuthorizationDoesNotOutliveTheSale() (gas: 193293)\n[PASS] test_AuthorizationFromTwoTopLevelCallsIsLost() (gas: 86867)\nLogs:\n  sale authorized in a separate top-level call: blocked\n\n[PASS] test_AuthorizationIsForThatFrenOnly() (gas: 394696)\n[PASS] test_AuthorizationIsForThatOperatorOnly() (gas: 479809)\n[PASS] test_ConduitAloneIsBlocked() (gas: 53267)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 79352)\n[PASS] test_HolderSendsItHerself() (gas: 87060)\n[PASS] test_OnlyTheAuthorizerAuthorizes() (gas: 74587)\n[PASS] test_ZoneAuthorizedSaleGoesThroughInOneFrame() (gas: 124594)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 6.03ms (827.03µs CPU time)\n\nRan 18 tests for test/frens/FrenWorkerGate.t.sol:FrenWorkerGateTest\n[PASS] test_ARefusedMintTakesNothing() (gas: 104954)\n[PASS] test_AnNftCountsOnce_whoeverHoldsItLater() (gas: 151129)\n[PASS] test_CreditsGoWhereTheHolderSays() (gas: 639698)\n[PASS] test_NoGateNoWindow() (gas: 442079)\n[PASS] test_OnlyFrensSpends() (gas: 32613)\n[PASS] test_OnlyTheHolderClaims() (gas: 67063)\n[PASS] test_OpenPublic() (gas: 528969)\n[PASS] test_OwnerMintsBeforeTheOpening() (gas: 2296619)\n[PASS] test_SeatOperatorClaimsTheStrategysNfts() (gas: 138514)\n[PASS] test_TheCreditIsTheMinters() (gas: 576375)\n[PASS] test_WindowClosesAfter420() (gas: 32492721)\n[PASS] test_WindowNeedsACreditPerFren() (gas: 714583)\n[PASS] test_WindowNeverOvershoots() (gas: 36198264)\n[PASS] test_WlAndWorkersShareThe420() (gas: 619410)\n[PASS] test_WlOnlyAsListed_once() (gas: 254028)\n[PASS] test_WlRaisedAmount_claimsTheDifference() (gas: 226858)\n[PASS] test_WlRootOnlyTheOwner_andNoRootNoWl() (gas: 62252)\n[PASS] test_WlWalletsMintInTheWindow_asMuchAsListed() (gas: 750975)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 21.28ms (8.63ms CPU time)\n\nRan 6 tests for test/frens/FrenSwapperAverage.t.sol:FrenSwapperAverageTest\n[PASS] test_AFullBuyMovesItOneStep() (gas: 103606)\n[PASS] test_ASmallBuyMovesItInProportion() (gas: 52842)\n[PASS] test_DustMovesItNextToNothing() (gas: 7606082)\n[PASS] test_FloorRateIsTheLowerReading() (gas: 17282)\n[PASS] test_MovesDownToo() (gas: 52658)\n[PASS] test_OnceABlock() (gas: 91443)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 60.80ms (2.01ms CPU time)\n\nRan 3 tests for test/FrensLaunchReview.t.sol:FrensFactoryReviewTest\n[PASS] test_Audit_ConstructorLeavesTheDocumentedTeamSetup() (gas: 25294534)\n[PASS] test_FreshChainCallsOnlyContractsCreatedByTheLaunch() (gas: 28696930)\n[PASS] test_ProtectedFactoryDeploysBothLaunchesInOrder() (gas: 25493481)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 62.36ms (16.41ms CPU time)\n\nRan 3 tests for test/FrensResidualRisks.t.sol:FrensResidualRisksTest\n[PASS] test_Risk_DuplicatePendingNonceStrandsOnePayment() (gas: 1554494)\n[PASS] test_Risk_PendingEthSpotPushDilutesExistingHolders() (gas: 5288577)\n[PASS] test_Risk_UnwiringSwapperAllowsCheapReserveCapture() (gas: 992804)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 76.82ms (1.86ms CPU time)\n\nRan 86 tests for test/frens/IMD6900Frens.t.sol:IMD6900FrensTest\n[PASS] test_AMintNeverFailsOnTheSwap() (gas: 1003226)\n[PASS] test_BatchCannotRepeatAFren() (gas: 641744)\n[PASS] test_BatchCountIsOneTo69() (gas: 2185647)\n[PASS] test_BatchCountsTheCapsAsItGoes() (gas: 11780401)\n[PASS] test_BatchHoldsPepesForLowTiers() (gas: 12176238)\n[PASS] test_BatchPaysOneJob() (gas: 700213)\n[PASS] test_BatchRevealIsExactlyTheVoucher() (gas: 1011974)\n[PASS] test_BatchRevealsThemAll() (gas: 2256463)\n[PASS] test_BatchSoldOut() (gas: 90792483)\n[PASS] test_BuyTreasuryRespectsMaxPay() (gas: 1842004)\n[PASS] test_BuyingFromTheTreasuryRaisesTheFloor() (gas: 3101386)\n[PASS] test_CurveWhileAboveTheFloor() (gas: 1599825)\n[PASS] test_DearerImd6900RaisesTheFloorPrice() (gas: 2861964)\n[PASS] test_DustEthBuyCannotTakeTheMintsTurn() (gas: 540029)\n[PASS] test_EveryFrenIsOneOfAKind() (gas: 947080)\n[PASS] test_FailedJob_payAnother() (gas: 1173029)\n[PASS] test_FeeEthBuysTheFloor() (gas: 1708734)\n[PASS] test_FloorBuysAreAnyones_onePerBlock() (gas: 1893053)\n[PASS] test_FloorBuysAreCapped() (gas: 55694600)\n[PASS] test_FloorIsReserveOverFrensOut() (gas: 2761464)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 2145216)\n[PASS] test_FrenSentStraightToTheTreasuryCounts() (gas: 2660288)\n[PASS] test_IsACreatorToken() (gas: 41743)\n[PASS] test_JobPaymentIsPinnedToImdsPayee() (gas: 980756)\n[PASS] test_KeeperApprovesTheJobPaidFor() (gas: 657241)\n[PASS] test_KeeperCannotApproveASpentNonce() (gas: 490236)\n[PASS] test_KeeperCannotApproveLongLivedPayments() (gas: 434019)\n[PASS] test_LapsedPaymentIsUsedAgain() (gas: 719034)\n[PASS] test_LastFrenOutStaysOut() (gas: 1615217)\n[PASS] test_LaterPartsAgreeOnWhatIsRevealed() (gas: 1347339)\n[PASS] test_LowTierRequestsNeedAPepeLeft() (gas: 11708397)\n[PASS] test_MaxMintGrowsWithTheBag() (gas: 4682663)\n[PASS] test_MaxPayStopsAPriceThatMoved() (gas: 10063499)\n[PASS] test_MaxRequest() (gas: 9863897)\n[PASS] test_MintMintsAtOnce_unrevealed() (gas: 770449)\n[PASS] test_MintNeedsSealedTraits() (gas: 8623021)\n[PASS] test_MintNeverBelowTheFloor() (gas: 3425403)\n[PASS] test_MintNeverSkipsTheBuyForWantOfGas() (gas: 708870)\n[PASS] test_NoMintIntoTheTreasury() (gas: 1596022)\n[PASS] test_NoRequestForNobody() (gas: 61912)\n[PASS] test_NoTierInsideAFlashLoan() (gas: 687636)\n[PASS] test_NobodyElseApprovesJobs() (gas: 435635)\n[PASS] test_OneRevealPerFren() (gas: 717718)\n[PASS] test_OnlyHoldersRecycle() (gas: 1593666)\n[PASS] test_OnlyRealFrens() (gas: 743319)\n[PASS] test_OnlyTheOwnerSetsTheValidator() (gas: 1063173)\n[PASS] test_OwnerAdjustsTheArtUntilFrozen() (gas: 1555030)\n[PASS] test_PairRules() (gas: 875556)\n[PASS] test_PartsOnlyGoForward() (gas: 1035497)\n[PASS] test_PendingEthCountsInTheMintPrice() (gas: 2181876)\n[PASS] test_PriceRisesWithMints() (gas: 38125610)\n[PASS] test_PriceTableIsTheCurve() (gas: 36977950)\n[PASS] test_QuoteCountsUnsweptImd() (gas: 3216110)\n[PASS] test_RareTraitsNeedTheTier() (gas: 1365734)\n[PASS] test_RecyclePaysTheFloorAndKeepsIt() (gas: 2915603)\n[PASS] test_RecyclePaysTheWaitingImdToo() (gas: 2749360)\n[PASS] test_ReleaseLapsedJobAfterTheReveal() (gas: 2142099)\n[PASS] test_RequestMintFor() (gas: 1084060)\n[PASS] test_ReserveOnlyLeavesThroughRecycle() (gas: 2838128)\n[PASS] test_RevealFollowsTheFren() (gas: 749220)\n[PASS] test_RevealInParts() (gas: 9747514)\nLogs:\n  gas, minting 69: 2117887\n  gas, revealing 30: 3092845\n\n[PASS] test_RevealNeedsTheRelayersVoucher() (gas: 803651)\n[PASS] test_RevealReleasesALapsedPayment() (gas: 919103)\n[PASS] test_RevealShowsTheFren() (gas: 1192018)\n[PASS] test_RevealsWhenever() (gas: 661560)\n[PASS] test_RoyaltiesPayTheFloor() (gas: 1724930)\n[PASS] test_RoyaltyBpsIsCappedAndOwnerOnly() (gas: 109412)\n[PASS] test_RulesAreFrozenOnceSealed() (gas: 69580)\n[PASS] test_SealNeedsEveryTraitAndTheSupply() (gas: 10518169)\n[PASS] test_SetMaxMint() (gas: 178238)\n[PASS] test_ShortSwapReverts() (gas: 1046881)\n[PASS] test_SwapperMayPullOnlyTheBuy() (gas: 957490)\n[PASS] test_TheOwnerKeepsTheCollection_theGovernorTheMechanics() (gas: 488664)\n[PASS] test_TierCountsWhatIsLeftAfterPaying() (gas: 920004)\n[PASS] test_TierIsTheBestOfTheBag() (gas: 633505)\n[PASS] test_TierIsTheMints() (gas: 1350710)\n[PASS] test_TierThresholdsOwnerOnlyAndOrdered() (gas: 154928)\n[PASS] test_TradesGoPastTheValidator() (gas: 1346243)\n[PASS] test_TraitCapsHold() (gas: 25778206)\n[PASS] test_TreasuryRoundTripCostsTheFloor() (gas: 3295585)\n[PASS] test_UnneededRetryFeedsTheFloor() (gas: 1026116)\n[PASS] test_UnrevealedFrenSellsAtTheFloor() (gas: 1022775)\n[PASS] test_UnrevealedFrensShareTheFloor() (gas: 1466205)\n[PASS] test_UnsweptImdCountsInTheTreasuryPrice() (gas: 3367000)\n[PASS] test_UnusedJobFeedsTheFloor() (gas: 696981)\n[PASS] test_VouchersExpire() (gas: 472492)\nSuite result: ok. 86 passed; 0 failed; 0 skipped; finished in 96.15ms (158.67ms CPU time)\n\nRan 13 tests for test/FrensPlacement.t.sol:FrensPlacementTest\n[PASS] test_Attributes() (gas: 19594305)\n[PASS] test_CodeIsWhatTheSourcesBuild() (gas: 89696)\n[PASS] test_DeploysOnAFreshChain() (gas: 51811985)\n[PASS] test_EachLaunchFitsOneTransaction() (gas: 105863223)\nLogs:\n  the collection launch: gas, all in: 12451232\n  the art launch: gas, all in: 13605058\n\n[PASS] test_LandsWhereThePlanSays() (gas: 51923654)\n[PASS] test_NewArtReadsBackExactly() (gas: 14173584)\n[PASS] test_PassesTheAdmissionScan() (gas: 88302675)\n[PASS] test_PlanFollowsFromTheCode() (gas: 80750)\n[PASS] test_PlanMatchesTheScript() (gas: 14314057)\n[PASS] test_PriceTableIsTheCurve() (gas: 53105398)\n[PASS] test_RefusesOtherArt() (gas: 12971045)\n[PASS] test_SameAddressesWhoeverRunsIt() (gas: 102774643)\n[PASS] test_TakesWhatSomeonePlacedFirst() (gas: 52491481)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 97.07ms (194.14ms CPU time)\n\nRan 16 tests for test/FrensLaunchAdaptation.t.sol:FrensLaunchAdaptationTest\n[PASS] test_BootstrapThenOpenAndResume() (gas: 2315891)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\n[PASS] test_FirstFrensBeforeFeesPreventsPublicFloorCapture() (gas: 3435830)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_FirstFrensRequiresPausedBuysAndNonzeroCount() (gas: 1785691)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Fix_SpentNonceIsRefused() (gas: 2311018)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeRefuseAnOwnerlessFloor() (gas: 1828783)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeRejectChangedWiring() (gas: 2680591)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeWarnWhenAverageLags() (gas: 4784847)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  WARNING: the swapper's average is more than 2x off the pool's price\n  WARNING: the swapper's average is more than 2x off the pool's price\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\n[PASS] test_PlacedRejectsMissingOrWrongModules() (gas: 126575)\n[PASS] test_PlacedRejectsWrongCollectionAndForeignDependencies() (gas: 65561)\n[PASS] test_PlacedReportsTheLaunchModules() (gas: 51824)\n[PASS] test_ResumeRequiresDistributorAndActualSwapperExemption() (gas: 2164855)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Risk_FirstMintCapturesExistingFloorIfRunbookIsBypassed() (gas: 3054450)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Risk_LaunchBlockPushSeedsSwapperAndSetupRejectsAfterPriceReturns() (gas: 2572174)\n[PASS] test_Risk_UnsupportedRoyaltyTokenHasNoFloorRoute() (gas: 3781565)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_SetupPausesBeforeFirstMintEvenWhenAlreadyDistributor() (gas: 1815518)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_SetupUsesTheLaunchsReplacementSwapper() (gas: 6981779)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 98.55ms (22.12ms CPU time)\n\nRan 12 tests for test/FrensArtFraming.t.sol:FrensArtFramingTest\n[PASS] testFuzz_EntryPastTheIndexIsMissing(uint256) (runs: 256, μ: 13965, ~: 13943)\nLogs:\n  Bound result 8288\n\n[PASS] testFuzz_UnknownBackgroundIsMissing(uint256) (runs: 256, μ: 35605, ~: 35592)\nLogs:\n  Bound result 12\n\n[PASS] test_ATamperedChunkDrawsNothing() (gas: 90530)\n[PASS] test_AttributesDoNotValidate() (gas: 369298)\n[PASS] test_ChunksAreFramedPushData() (gas: 2731712)\n[PASS] test_EveryValueHasAName() (gas: 15679313)\n[PASS] test_IndexHashesAreTheChunks() (gas: 788314)\n[PASS] test_IndexPointsInsideTheChunks() (gas: 525933)\n[PASS] test_LaunchLayersAreWellFormed() (gas: 46186857)\n[PASS] test_NoSuchFrenIsMissing() (gas: 4375022)\n[PASS] test_PalettesFitTogether() (gas: 13989817)\n[PASS] test_WithoutTheSwarmNothingDraws() (gas: 5947823)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 98.61ms (191.86ms CPU time)\n\nRan 19 tests for test/FrensLaunchFailures.t.sol:FrensLaunchFailuresTest\n[PASS] testFuzz_CheckKnowsAFren(uint24,uint8) (runs: 512, μ: 1940859, ~: 1907849)\nLogs:\n  Bound result 0\n\n[PASS] testFuzz_OtherSaltsLandElsewhere(bytes32) (runs: 256, μ: 34990, ~: 34990)\n[PASS] testFuzz_QuoteIsTheCurveBeforeAnyoneHolds(uint256) (runs: 512, μ: 321810, ~: 309934)\nLogs:\n  Bound result 9\n\n[PASS] test_AnotherOwnerMovesTheFrens() (gas: 34259)\n[PASS] test_CollectionRefusesAShortPriceTable() (gas: 3059925)\n[PASS] test_CollectionWaitsForTheTeamWallet() (gas: 5941480)\n[PASS] test_EveryPriceIsTheTables() (gas: 27580866)\n[PASS] test_FreshChainLaunchesAreIndependent() (gas: 1351085)\n[PASS] test_LaunchContractsTakeNoEth() (gas: 687657)\n[PASS] test_ModulesFailOverCorruptedFrens() (gas: 97101)\n[PASS] test_ModulesNeedTheCollection() (gas: 106270)\n[PASS] test_NoLaunchActorHoldsARole() (gas: 4580578)\n[PASS] test_NoPricePastTheTable() (gas: 41219)\n[PASS] test_OtherSaltSchemeFailsTheLaunch() (gas: 140656)\n[PASS] test_RefusingDeployerFailsTheLaunch() (gas: 103369)\n[PASS] test_ScriptRefusesAnUnplacedChain() (gas: 14786192)\n[PASS] test_SecondLaunchReusesWhatIsPlaced() (gas: 1313404)\n[PASS] test_SilentDeployerFailsTheLaunch() (gas: 93234)\n[PASS] test_WrongArtFailsTheArtLaunch() (gas: 588910)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 98.64ms (244.18ms CPU time)\n\nRan 17 tests for test/frens/FrenMinterOffline.t.sol:FrenMinterOfflineTest\n[PASS] testFuzz_BuyImdConservesPaymentAndRefund(uint256,uint256) (runs: 1000, μ: 330847, ~: 330921)\nLogs:\n  Bound result 3\n  Bound result 639167032288844278\n\n[PASS] test_AllQuotesUnwindPoolAccountingAndTokenTransfers() (gas: 763581)\n[PASS] test_ClosedMintRollsBackAnAlreadySettledSwap() (gas: 641895)\n[PASS] test_EthMintSpendsCallersCreditAndKeepsTheirTier() (gas: 1038475)\n[PASS] test_ExcessOutputGoesBackToThePayer() (gas: 866146)\n[PASS] test_InvalidCountsDoNotLeavePaidSwapsBehind() (gas: 982596)\n[PASS] test_LastFrenAndSoldOutDoNotAcceptMoreEth() (gas: 62017818)\n[PASS] test_MinterCannotBorrowAnotherWalletsGateCredit() (gas: 703774)\n[PASS] test_OneWeiUnderfundingRollsBackAndExactPaymentSucceeds() (gas: 1190355)\n[PASS] test_OnlyPoolManagerCanEnterAnyCallbackMode() (gas: 396447)\n[PASS] test_PartialExactOutputCannotMintOrBuy() (gas: 716999)\n[PASS] test_QuoteAndMintMaxPayDifferByOneWei() (gas: 940830)\n[PASS] test_QuotesBubblePoolErrorsInsteadOfReturningZero() (gas: 719702)\n[PASS] test_RefundCallbackCannotReenterEitherValueEntryPoint() (gas: 1404208)\n[PASS] test_RefundRejectionRevertsMintAndBuyAtomically() (gas: 2302902)\n[PASS] test_TierFailureRestoresCreditsSwapAndPayment() (gas: 778989)\n[PASS] test_ZeroBuyRevertsAndOneWeiBuySettles() (gas: 657108)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 98.70ms (113.79ms CPU time)\n\nRan 1 test for test/FrensSplitGas.t.sol:FrensSplitGasTest\n[PASS] test_CollectionFitsButLegacyFourContractBundleExceedsCap() (gas: 101750011)\nLogs:\n  legacy four-contract launch: 23877683\n  two-contract collection launch: 13494921\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 133.33ms (133.15ms CPU time)\n\nRan 1 test for test/FrensPlacedInvariants.t.sol:FrensPlacedInvariantsTest\n[PASS]\nFrensPlacedInvariantsTest invariants:\n[PASS] invariant_CapsHold\n[PASS] invariant_FloorIsAShareOfTheWorld\n[PASS] invariant_FrensAreConserved\n[PASS] invariant_ImdIsTheBooks\n[PASS] invariant_ReserveIsExactlyWhatIsHere\n FrensPlacedInvariantsTest invariants (runs: 32, calls: 1920, reverts: 6)\n\n╭--------------------+-----------------+-------+---------+----------╮\n| Contract           | Selector        | Calls | Reverts | Discards |\n+===================================================================+\n| PlacedFrensHandler | approveJob      | 123   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyFloor        | 130   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyTreasury     | 110   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donate6900      | 99    | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donateImd       | 119   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | feeEth          | 121   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mint            | 245   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mintThenRecycle | 134   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | recycle         | 133   | 6       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | retryJob        | 115   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | reveal          | 121   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setParams       | 125   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setRate         | 107   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | transfer        | 111   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | warp            | 127   | 0       | 0        |\n╰--------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 4294967295\n  Bound result 1\n  Bound result 1598\n  Bound result 380538\n  Bound result 7\n  Bound result 21710\n  Bound result 867927511654341368602\n  Bound result 0\n  Bound result 0\n  Bound result 345387797898327550\n  Bound result 50007522378767661\n  Bound result 16683\n  Bound result 86\n  Bound result 1\n  Bound result 1300\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 199999999999999999998\n  Bound result 499999999999999998\n  Bound result 2\n  Bound result 1\n  Bound result 731687303715884105727\n  Bound result 4782\n  Bound result 1\n  Bound result 105567313527488399148\n  Bound result 18517\n  Bound result 1\n  Bound result 3102\n  Bound result 1\n  Bound result 1000000000000000034\n  Bound result 206573537333698336\n  Bound result 2\n  Bound result 2\n  Bound result 2\n  Bound result 0\n  Bound result 398\n  Bound result 1\n  Bound result 1\n  Bound result 2\n  Bound result 2734\n  Bound result 3\n  Bound result 1\n  Bound result 54\n  Bound result 50000000000000000\n  Bound result 1\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.56s (2.56s CPU time)\n\nRan 2 tests for test/frens/FrenMinterInvariants.t.sol:FrenMinterInvariantsTest\n[PASS]\nFrenMinterInvariantsTest invariants:\n[PASS] invariant_AllPaymentsRemainAccountedFor\n[PASS] invariant_RequestsAndWorkerCreditsAreConserved\n FrenMinterInvariantsTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭------------------+------------+-------+---------+----------╮\n| Contract         | Selector   | Calls | Reverts | Discards |\n+============================================================+\n| EthMinterHandler | buy        | 2786  | 0       | 0        |\n|------------------+------------+-------+---------+----------|\n| EthMinterHandler | claimMore  | 2716  | 0       | 0        |\n|------------------+------------+-------+---------+----------|\n| EthMinterHandler | mint       | 2714  | 0       | 0        |\n|------------------+------------+-------+---------+----------|\n| EthMinterHandler | openPublic | 2787  | 0       | 0        |\n|------------------+------------+-------+---------+----------|\n| EthMinterHandler | quote      | 2722  | 0       | 0        |\n|------------------+------------+-------+---------+----------|\n| EthMinterHandler | rejectMint | 2659  | 0       | 0        |\n╰------------------+------------+-------+---------+----------╯\n\nLogs:\n  Bound result 53\n  Bound result 500000000000000000\n  Bound result 342\n  Bound result 4\n  Bound result 157220852171426166\n  Bound result 32\n  Bound result 347788416570869627\n  Bound result 1174\n  Bound result 289358488000398338\n  Bound result 171\n  Bound result 3798\n  Bound result 87\n  Bound result 4482\n  Bound result 198\n  Bound result 29900647273645944\n  Bound result 746163980702720938\n  Bound result 169\n  Bound result 1\n  Bound result 565249999999999999\n  Bound result 202134175282355742673\n  Bound result 315107\n  Bound result 100000000000000000000\n  Bound result 162\n  Bound result 11\n  Bound result 974\n  Bound result 17\n  Bound result 1\n  Bound result 216\n  Bound result 2600\n  Bound result 80424406219177558080\n  Bound result 5000\n  Bound result 58\n  Bound result 886\n  Bound result 34\n  Bound result 200000000000000000000\n  Bound result 11773\n  Bound result 40\n  Bound result 156\n  Bound result 388\n  Bound result 14\n  Bound result 639\n  Bound result 18908\n  Bound result 41768846324179990159\n  Bound result 11556\n  Bound result 5872\n  Bound result 16631\n  Bound result 806\n  Bound result 60\n  Bound result 158\n  Bound result 343\n  Bound result 312\n  Bound result 50\n  Bound result 7\n  Bound result 82\n  Bound result 18\n  Bound result 16330\n  Bound result 284783122685678194286\n  Bound result 686120271303140462\n  Bound result 144\n  Bound result 3360\n  Bound result 3137\n  Bound result 22\n  Bound result 200000000000000000\n  Bound result 23431\n  Bound result 408\n  Bound result 3000\n\n[PASS] test_HandlerSequenceExercisesValueAndFailurePaths() (gas: 5891735)\nLogs:\n  Bound result 1\n  Bound result 0\n  Bound result 1000000000000000000\n  Bound result 100\n  Bound result 200\n  Bound result 22\n  Bound result 1000000000000000000\n  Bound result 10000000000000000000\n  Bound result 69\n  Bound result 0\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 6.44s (6.44s CPU time)\n\nRan 21 test suites in 6.45s (9.97s CPU time): 215 tests passed, 0 failed, 5 skipped (220 total tests)\n","passed":true},{"durationMs":85,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"FrenMinter.buyImd(uint256)\",\"FrenMinter.mintWithEth(uint8,uint256)\",\"FrenMinter.quoteEth(uint8)\",\"FrenMinter.quoteFloor(uint256)\",\"FrenMinter.quoteFloorEth(uint256)\",\"FrenMinter.unlockCallback(bytes)\",\"FrenSwapper.ethToImd6900(uint256,address)\",\"FrenSwapper.imdToImd6900(uint256,uint256,address)\",\"FrenSwapper.unlockCallback(bytes)\",\"FrenWorkerGate.cancelOwnershipHandover()\",\"FrenWorkerGate.claim(uint256[],address)\",\"FrenWorkerGate.claimWl(uint256,bytes32[],address)\",\"FrenWorkerGate.completeOwnershipHandover(address)\",\"FrenWorkerGate.openPublic()\",\"FrenWorkerGate.renounceOwnership()\",\"FrenWorkerGate.requestOwnershipHandover()\",\"FrenWorkerGate.setWlRoot(bytes32)\",\"FrenWorkerGate.spend(address,uint256)\",\"FrenWorkerGate.transferOwnership(address)\",\"IMD6900Frens.addPairRule((uint8,uint8,uint8,uint8,uint8))\",\"IMD6900Frens.approve(address,uint256)\",\"IMD6900Frens.approveJob(uint256,uint256,uint256,(string,bytes32,string,bytes32,bytes32,string,uint256))\",\"IMD6900Frens.buyFloor(uint256)\",\"IMD6900Frens.buyFloorWithEth(uint256,uint256)\",\"IMD6900Frens.buyTreasury(uint256,uint256,uint256)\",\"IMD6900Frens.cancelOwnershipHandover()\",\"IMD6900Frens.completeOwnershipHandover(address)\",\"IMD6900Frens.freezeArt()\",\"IMD6900Frens.receive()\",\"IMD6900Frens.recycle(uint256)\",\"IMD6900Frens.releaseLapsedJob(uint256)\",\"IMD6900Frens.renounceOwnership()\",\"IMD6900Frens.requestMint(uint8,uint256)\",\"IMD6900Frens.requestMintFor(address,uint8,uint256)\",\"IMD6900Frens.requestOwnershipHandover()\",\"IMD6900Frens.retryJob(uint256)\",\"IMD6900Frens.reveal(uint256,uint24[],string,bytes32,uint256,bytes,uint256)\",\"IMD6900Frens.safeTransferFrom(address,address,uint256)\",\"IMD6900Frens.safeTransferFrom(address,address,uint256,bytes)\",\"IMD6900Frens.sealTraits()\",\"IMD6900Frens.setApprovalForAll(address,bool)\",\"IMD6900Frens.setGovernor(address)\",\"IMD6900Frens.setMaxMint(uint8[4])\",\"IMD6900Frens.setMintOpen(bool)\",\"IMD6900Frens.setModules(address,address)\",\"IMD6900Frens.setParams(uint256,uint256,uint256)\",\"IMD6900Frens.setRenderer(address)\",\"IMD6900Frens.setRoles(address,address,address)\",\"IMD6900Frens.setRoyalty(uint96)\",\"IMD6900Frens.setTiers(uint256[3],uint256[3],uint256[3])\",\"IMD6900Frens.setTraitRules(uint8,uint16[],uint8[])\",\"IMD6900Frens.setTransferValidator(address)\",\"IMD6900Frens.transferFrom(address,address,uint256)\",\"IMD6900Frens.transferOwnership(address)\",\"IMD6900Frens.unwrapWeth()\"],\"files\":{\".gitignore\":4,\"ADAPTATION.md\":160,\"README.md\":218,\"foundry.toml\":22,\"launch.json\":16,\"script/art/chunks.py\":165,\"script/art/data/bgpal/cleanlab_blue.bin\":1,\"script/art/data/bgpal/cleanlab_green.bin\":1,\"script/art/data/bgpal/cleanlab_red.bin\":1,\"script/art/data/bgpal/messylab_blue.bin\":1,\"script/art/data/bgpal/messylab_green.bin\":1,\"script/art/data/bgpal/messylab_red.bin\":1,\"script/art/data/bgpal/tubeblue.bin\":1,\"script/art/data/bgpal/tubegreen.bin\":2,\"script/art/data/bgpal/tubered.bin\":1,\"script/art/data/bgpal/tubeyellow.bin\":1,\"script/art/data/bgpal/wireframe_green.bin\":2,\"script/art/data/bgpal/wireframe_red.bin\":1,\"script/art/data/expected.json\":53,\"script/art/data/layers/bg0.bin\":9,\"script/art/data/layers/bg1.bin\":9,\"script/art/data/layers/bg10.bin\":32,\"script/art/data/layers/bg11.bin\":32,\"script/art/data/layers/bg2.bin\":9,\"script/art/data/layers/bg3.bin\":15,\"script/art/data/layers/bg4.bin\":15,\"script/art/data/layers/bg5.bin\":15,\"script/art/data/layers/bg6.bin\":26,\"script/art/data/layers/bg7.bin\":26,\"script/art/data/layers/bg8.bin\":26,\"script/art/data/layers/bg9.bin\":26,\"script/art/data/layers/coat.bin\":20,\"script/art/data/layers/item5.bin\":2,\"script/art/data/manifest.json\":500,\"script/art/data/shared.bin\":4,\"script/art/data/tables.bin\":2,\"script/art/swarm-v2.json\":374,\"script/frens/DeployFrens.s.sol\":254,\"script/frens/FrensTimelockBatch.s.sol\":105,\"script/frens/price/prices-swarm.bin\":6,\"script/frens/price/prices.bin\":6,\"script/frens/price/prices.json\":26,\"script/frens/price/prices.py\":55,\"script/frens/price/prices_sol.py\":33,\"script/placement/addresses.json\":7,\"script/placement/gen.py\":146,\"src/FrensCode.sol\":15,\"src/FrensPlacement.sol\":129,\"src/FrensPlan.sol\":37,\"src/art/WorkerArt.sol\":671,\"src/art/WorkerArtIndex.sol\":56,\"src/frens/FrenMinter.sol\":194,\"src/frens/FrenPrices.sol\":122,\"src/frens/FrenSwapper.sol\":276,\"src/frens/FrenWorkerGate.sol\":137,\"src/frens/IMD6900Frens.sol\":1083,\"src/frens/WorkerFrensRenderer.sol\":377,\"test/FrensArtFraming.t.sol\":290,\"test/FrensLaunchAdaptation.t.sol\":331,\"test/FrensLaunchFailures.t.sol\":526,\"test/FrensLaunchReview.t.sol\":449,\"test/FrensPlacedInvariants.t.sol\":634,\"test/FrensPlacement.t.sol\":818,\"test/FrensResidualRisks.t.sol\":210,\"test/FrensSplitGas.t.sol\":68,\"test/frens/DataWriter.sol\":21,\"test/frens/FrenMinter.fork.t.sol\":189,\"test/frens/FrenMinterInvariants.t.sol\":290,\"test/frens/FrenMinterOffline.t.sol\":308,\"test/frens/FrenSwapper.fork.t.sol\":134,\"test/frens/FrenSwapperAverage.t.sol\":77,\"test/frens/FrenWorkerGate.t.sol\":350,\"test/frens/FrensRules.sol\":75,\"test/frens/IMD6900Frens.t.sol\":1763,\"test/frens/IMD6900FrensPermit2.fork.t.sol\":130,\"test/frens/IMD6900FrensValidator.fork.t.sol\":146,\"test/frens/IMD6900FrensValidator.t.sol\":231,\"test/helpers/FrensEthFixture.sol\":237,\"test/test_collection_manifest.py\":56},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"6a70a837efd1bdfeb9dc03664b8f59b1506226dba886905528b9cfe7d7b11d08","verifiedTreeHash":"9b3800b65e42bb0802b3684315d299a8163daa8d","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":149198,"exitCode":0,"name":"build","output":"Compiling 95 files with Solc 0.8.30\nSolc 0.8.30 finished in 149.01s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> test/FrensPlacement.t.sol:786:9:\n    |\n786 |         uint256 s = _find(j, key, 0) + key.length;\n    |         ^^^^^^^^^\nNote: The shadowed declaration is here:\n   --> test/FrensPlacement.t.sol:477:5:\n    |\n477 |     DeployFrens s;\n    |     ^^^^^^^^^^^^^\n\nWarning (2018): Function state mutability can be restricted to view\n   --> test/FrensLaunchAdaptation.t.sol:199:5:\n    |\n199 |     function test_PlacedReportsTheLaunchModules() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:77\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                                                             ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:55\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                                       ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:39\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                       ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/frens/WorkerFrensRenderer.sol:59:29\n   │\n59 │           bytes memory json = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n60 │ ┃             '{\"name\":\"Worker Fren #',\n61 │ ┃             tokenId.toString(),\n62 │ ┃             '\",\"description\":\"',\n   ‡ ┃\n68 │ ┃             \"}\"\n69 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:74:9\n   │\n74 │         address frens_,\n   │         ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:76:9\n   │\n76 │         address imdPoolHook_\n   │         ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:72:9\n   │\n72 │         address imd_,\n   │         ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:73:9\n   │\n73 │         address imd6900_,\n   │         ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:75:9\n   │\n75 │         address pairHook_,\n   │         ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/frens/WorkerFrensRenderer.sol:75:29\n   │\n75 │           bytes memory json = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n76 │ ┃             '{\"name\":\"Worker Fren #',\n77 │ ┃             tokenId.toString(),\n78 │ ┃             '\",\"description\":\"',\n   ‡ ┃\n82 │ ┃             '\",\"attributes\":[{\"trait_type\":\"Status\",\"value\":\"Unrevealed\"}]}'\n83 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/WorkerFrensRenderer.sol:124:21\n    │\n124 │                 if (k == i) {\n    │                     ━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/WorkerFrensRenderer.sol:125:54\n    │\n125 │                     bytes memory out = new bytes(j - start);\n    │                                                      ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/frens/WorkerFrensRenderer.sol:158:17\n    │\n158 │ ┏                 abi.encodePacked(\n159 │ ┃                     '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 84 84\" width=\"840\" height=\"840\">',\n160 │ ┃                     '<image width=\"84\" height=\"',\n161 │ ┃                     (N * PENDING_FRAMES).toString(),\n    ‡ ┃\n165 │ ┃                     'dur=\"1.3s\" calcMode=\"discrete\" repeatCount=\"indefinite\"/></image></svg>'\n166 │ ┃                 )\n    │ ┗━━━━━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:208:35\n    │\n208 │                 bytes1 v = bytes1(uint8(l * 3 / 5));\n    │                                   ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:221:42\n    │\n221 │         _draw(cv, _entry(Art.BG0 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                          ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:221:62\n    │\n221 │         _draw(cv, _entry(Art.BG0 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                              ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:253:74\n    │\n253 │         if (ch > 2 || face >= FACES || coat > 2 || shirt > 5 || hat > 2) revert Missing();\n    │                                                                          ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:350:31\n    │\n350 │         if (i >= Art.ENTRIES) revert Missing();\n    │                               ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:356:72\n    │\n356 │         if (p.codehash != bytes32(_hex(Art.CHUNK_HASHES, c * 64, 64))) revert BadArt();\n    │                                                                        ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:288:25\n    │\n288 │                         revert(28, 4)\n    │                         ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:232:56\n    │\n232 │         _draw(back, _entry(Art.BG0 + Art.PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                        ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:232:76\n    │\n232 │         _draw(back, _entry(Art.BG0 + Art.PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                                            ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:239:23\n    │\n239 │             _fren(cv, uint24(combo));\n    │                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/FrensPlacement.sol:54:13\n   │\n54 │ ┏             abi.encodePacked(\n55 │ ┃                 FrensCode.FRENS,\n56 │ ┃                 abi.encode(\n57 │ ┃                     FrensPlan.OWNER,\n   ‡ ┃\n68 │ ┃             )\n   │ ┗━━━━━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:261:22\n    │\n261 │         int256 dx = -int256(CX);\n    │                      ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:262:22\n    │\n262 │         int256 dy = -int256(CY);\n    │                      ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/FrensPlacement.sol:93:29\n   │\n93 │           bytes memory init = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n94 │ ┃             FrensCode.SWAPPER,\n95 │ ┃             abi.encode(\n96 │ ┃                 FrensPlan.POOL_MANAGER, FrensPlan.IMD, FrensPlan.IMD6900, f, FrensPlan.PAIR_HOOK, FrensPlan.POOL4_HOOK\n97 │ ┃             )\n98 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/FrensPlacement.sol:109:13\n    │\n109 │ ┏             abi.encodePacked(\n110 │ ┃                 FrensCode.MINTER, abi.encode(FrensPlan.POOL_MANAGER, f, FrensPlan.POOL4_HOOK, FrensPlan.PAIR_HOOK)\n111 │ ┃             )\n    │ ┗━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/FrensPlacement.sol:115:13\n    │\n115 │             abi.encodePacked(FrensCode.GATE, abi.encode(FrensPlan.OWNER, f, FrensPlan.IDENTITY, FrensPlan.IMD6900))\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:321:56\n    │\n321 │         for (uint256 i; i < n; ++i) b[at + i] = bytes1(uint8(v >> (8 * i)));\n    │                                                        ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/frens/WorkerFrensRenderer.sol:370:49\n    │\n370 │             extcodecopy(p, add(raw, 32), add(1, mul(first, 33)), mul(frames, 33))\n    │                                                 ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:118:35\n    │\n118 │             if (r.length == 68 && bytes4(r) == Quoted.selector) {\n    │                                   ━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/frens/IMD6900Frens.sol:964:66\n    │\n964 │         return (ITransferValidator721.validateTransfer.selector, true);\n    │                                                                  ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:138:79\n    │\n138 │ …     imdKey(), SwapParams({zeroForOne: true, amountSpecified: -int256(amount), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1}), \"\"\n    │                                                                 ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:140:25\n    │\n140 │                 imdIn = uint256(uint128(e.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:140:33\n    │\n140 │                 imdIn = uint256(uint128(e.amount1()));\n    │                                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:148:39\n    │\n148 │                     amountSpecified: -int256(imdIn),\n    │                                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:153:35\n    │\n153 │             revert Quoted(amount, uint256(uint128(imdIs0 ? p.amount1() : p.amount0())));\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:153:43\n    │\n153 │             revert Quoted(amount, uint256(uint128(imdIs0 ? p.amount1() : p.amount0())));\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:158:70\n    │\n158 │ …     imdKey(), SwapParams({zeroForOne: true, amountSpecified: int256(imdOut), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1}), \"\"\n    │                                                                ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:160:25\n    │\n160 │         uint256 ethIn = uint256(uint128(-d.amount0()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:160:33\n    │\n160 │         uint256 ethIn = uint256(uint128(-d.amount0()));\n    │                                 ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:161:23\n    │\n161 │         uint256 got = uint256(uint128(d.amount1()));\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:161:31\n    │\n161 │         uint256 got = uint256(uint128(d.amount1()));\n    │                               ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenMinter.sol:165:9\n    │\n165 │         poolManager.settle{value: ethIn}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/FrenMinter.sol:183:29\n    │\n183 │             if (!withEth && cost > a) continue;\n    │                             ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:178:17\n    │\n178 │             try frens.quote(n) returns (uint256 q) {\n    │                 ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:22\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                      ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:51\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                                                   ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:84\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                                                                                    ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:184:22\n    │\n184 │             if (n <= frens.maxMint(_tier(withEth ? a : a - cost, b, c))) return n;\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[missing-events-access-control]: `keeper` is changed without an event but is used for access control\n     ╭▸ src/frens/IMD6900Frens.sol:1009:36\n     │\n1009 │         if (keeper_ != address(0)) keeper = keeper_;\n     │                                    ━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `governor` is changed without an event but is used for access control\n     ╭▸ src/frens/IMD6900Frens.sol:1075:9\n     │\n1075 │         governor = g;\n     │         ━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:177:16\n    │\n177 │         return uint160(zeroForOne ? uint256(p) * 1e18 / f : uint256(p) * f / 1e18);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:211:35\n    │\n211 │                 amountSpecified: -int256(amountIn),\n    │                                   ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:219:23\n    │\n219 │         uint256 pay = uint256(uint128(-paid));\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:219:31\n    │\n219 │         uint256 pay = uint256(uint128(-paid));\n    │                               ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:221:13\n    │\n221 │             poolManager.settle{value: pay}();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:225:13\n    │\n225 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:227:23\n    │\n227 │         uint256 out = uint256(uint128(got));\n    │                       ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:227:31\n    │\n227 │         uint256 out = uint256(uint128(got));\n    │                               ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:248:35\n    │\n248 │                 amountSpecified: -int256(ethIn),\n    │                                   ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:253:27\n    │\n253 │         uint256 ethPaid = uint256(uint128(-d1.amount0()));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:253:35\n    │\n253 │         uint256 ethPaid = uint256(uint128(-d1.amount0()));\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:254:26\n    │\n254 │         uint256 imdGot = uint256(uint128(d1.amount1()));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:254:34\n    │\n254 │         uint256 imdGot = uint256(uint128(d1.amount1()));\n    │                                  ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:261:35\n    │\n261 │                 amountSpecified: -int256(imdGot),\n    │                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:267:27\n    │\n267 │         uint256 imdPaid = uint256(uint128(-paid));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:267:35\n    │\n267 │         uint256 imdPaid = uint256(uint128(-paid));\n    │                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:268:23\n    │\n268 │         uint256 out = uint256(uint128(got));\n    │                       ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:268:31\n    │\n268 │         uint256 out = uint256(uint128(got));\n    │                               ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:270:9\n    │\n270 │         poolManager.settle{value: ethPaid}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenWorkerGate.sol:58:33\n   │\n58 │     constructor(address owner_, address frens_, address identity_, address strategy_) {\n   │                                 ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenWorkerGate.sol:58:68\n   │\n58 │     constructor(address owner_, address frens_, address identity_, address strategy_) {\n   │                                                                    ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[calls-loop]: external call inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:76:30\n   │\n76 │             address holder = identity.ownerOf(id);\n   │                              ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:77:78\n   │\n77 │             if (holder != msg.sender && (holder != strategy || msg.sender != ISeatStrategyMin(strategy).seatOperator()))\n   │                                                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:79:17\n   │\n79 │                 revert NotYours(id);\n   │                 ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:81:30\n   │\n81 │             if (claimed[id]) revert AlreadyClaimed(id);\n   │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/FrenWorkerGate.sol:105:9\n    │\n105 │         emit WlRootSet(root);\n    │         ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:318:9\n    │\n318 │         address imdPayTo_,\n    │         ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:320:9\n    │\n320 │         address relayer_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:315:9\n    │\n315 │         address identity_,\n    │         ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:316:9\n    │\n316 │         address permit2_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:321:9\n    │\n321 │         address priceTable_\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:312:9\n    │\n312 │         address owner_,\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:314:9\n    │\n314 │         address imd6900_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:313:9\n    │\n313 │         address imd_,\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:317:9\n    │\n317 │         address x402Proxy_,\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:319:9\n    │\n319 │         address keeper_,\n    │         ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:335:9\n    │\n335 │         emit ICreatorToken.TransferValidatorUpdated(address(0), DEFAULT_TRANSFER_VALIDATOR);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:372:16\n    │\n372 │         return uint8(table >> (8 * trait));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:377:16\n    │\n377 │         return uint8((uint256(combo) >> _shift(trait)) & ((1 << _bits(trait)) - 1));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:386:38\n    │\n386 │             if (minTiers[v] > TIERS) revert BadTraits();\n    │                                      ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/IMD6900Frens.sol:408:13\n    │\n408 │             chars += _rules[v].cap;\n    │             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:371:30\n    │\n371 │         if (trait >= TRAITS) revert BadTraits();\n    │                              ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-no-eth]: external call can be reentered before `floorImd` is updated\n    ╭▸ src/frens/IMD6900Frens.sol:849:13\n    │\n849 │         try IFrenSwapper(swapper).imdToImd6900{gas: FLOOR_BUY_GAS}(imdIn, minOut, address(this)) {}\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:985:9\n    │\n985 │         ITransferValidator721(validator).validateTransfer(msg.sender, from, to, id);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:561:9\n    │\n561 │         emit FrensMinted(requestId, minter, first, count, tier, paid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:859:9\n    │\n859 │         emit FloorBought(spent, 0, got, reserve);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:979:13\n    │\n979 │             revert LastFrenOut();\n    │             ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:557:82\n    │\n557 │         requests[requestId] = Request(minter, tier, lowTier, false, count, 0, 1, uint32(first), 0, 0);\n    │                                                                                  ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/FrenWorkerGate.sol:135:9\n    │\n135 │         emit PublicOpened(workerMinted);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/IMD6900Frens.sol:623:38\n    │\n623 │             h = keccak256(abi.encode(h, combos[i]));\n    │                                      ━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:615:13\n    │\n615 │         if (block.timestamp > deadline) revert BadVoucher();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:643:13\n    │\n643 │             emit Revealed(requestId, tokenId, combo, jobId, outputHash);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:632:28\n    │\n632 │             if (code != 0) revert BadCombo(code);\n    │                            ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:634:90\n    │\n634 │             if (valueOf(combo, CHARACTER) == 0 && pepe.minted + openLowTier >= pepe.cap) revert BadCombo(3);\n    │                                                                                          ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:627:22\n    │\n627 │         r.revealed = uint8(upTo);\n    │                      ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:665:53\n    │\n665 │         if (!r.jobApproved || _spent(r.jobNonce) || block.timestamp <= r.jobDeadline) return false;\n    │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:689:9\n    │\n689 │         emit JobPaid(requestId, msg.sender);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:715:13\n    │\n715 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours || _spent(nonce)) revert BadJob();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:715:53\n    │\n715 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours || _spent(nonce)) revert BadJob();\n    │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:731:9\n    │\n731 │         emit JobApproved(requestId, nonce, deadline, permitDigest, quoteDigest);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:722:25\n    │\n722 │         r.jobDeadline = uint40(deadline);\n    │                         ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint40' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/frens/IMD6900Frens.sol:873:9\n    │\n873 │         IFrenSwapper(swapper).ethToImd6900{value: ethIn}(minOut, address(this));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:895:9\n    │\n895 │         emit FloorBought(imdIn, ethIn, got, reserve);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/IMD6900Frens.sol:873:9\n    │\n873 │         IFrenSwapper(swapper).ethToImd6900{value: ethIn}(minOut, address(this));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:888:9\n    │\n888 │         emit WethUnwrapped(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:911:9\n    │\n911 │         emit Recycled(tokenId, msg.sender, paid, imdPaid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:934:9\n    │\n934 │         emit TreasuryBought(tokenId, msg.sender, paid, imdPaid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:955:35\n    │\n955 │     function setTransferValidator(address validator) external onlyCurator {\n    │                                   ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:957:9\n    │\n957 │         emit ICreatorToken.TransferValidatorUpdated(getTransferValidator(), validator);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n     ╭▸ src/frens/IMD6900Frens.sol:1016:26\n     │\n1016 │     function setRenderer(address r) external onlyCurator {\n     │                          ━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1019:9\n     │\n1019 │         emit Setting(\"renderer\", 0, r);\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1025:9\n     │\n1025 │         emit Setting(\"artFrozen\", 1, renderer);\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n     ╭▸ src/frens/IMD6900Frens.sol:1041:17\n     │\n1041 │                 revert BadTraits();\n     │                 ━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n     ╭▸ src/frens/IMD6900Frens.sol:1053:84\n     │\n1053 │             if (m[i] == 0 || m[i] > MAX_PER_REQUEST || (i > 0 && m[i] < m[i - 1])) revert BadTraits();\n     │                                                                                    ━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1069:9\n     │\n1069 │         emit Setting(\"royalty\", bps, address(0));\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:530:21\n    │\n530 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n531 │         bytes memory sig = _sign(frens, relayerKey, id, c, d);\n532 │         vm.warp(d + 1);\n    │         ────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:540:17\n    │\n540 │         vm.warp(block.timestamp + 30 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:685:50\n    │\n685 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:686:17\n    │\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:699:17\n    │\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:721:50\n    │\n721 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:722:17\n    │\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:737:50\n    │\n737 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:296:21\n    │\n296 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:742:17\n    │\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:753:17\n    │\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:760:17\n    │\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/FrensPlacedInvariants.t.sol:337:17\n    │\n337 │         vm.roll(block.number + bound(blocks, 0, 2));\n    │         ────────━━━━━━━━━━━━─────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/FrensPlacedInvariants.t.sol:372:17\n    │\n372 │         vm.roll(block.number + bound(blocks, 0, 2));\n    │         ────────━━━━━━━━━━━━─────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":2581,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/frens/IMD6900FrensValidator.fork.t.sol:IMD6900FrensValidatorForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 386.32µs (0.00ns CPU time)\n\nRan 1 test for test/frens/FrenSwapper.fork.t.sol:FrenSwapperForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 477.08µs (0.00ns CPU time)\n\nRan 1 test for test/frens/FrenMinter.fork.t.sol:FrenMinterForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 672.66µs (0.00ns CPU time)\n\nRan 1 test for test/frens/IMD6900FrensPermit2.fork.t.sol:IMD6900FrensPermit2ForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 692.33µs (0.00ns CPU time)\n\nRan 1 test for test/FrensPlacement.t.sol:FrensPlacementForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.69ms (0.00ns CPU time)\n\nRan 1 test for test/FrensResidualRisks.t.sol:FrensAverageResidualRiskTest\n[PASS] test_Risk_DustFirstPinsAverageAcrossOneHundredBlocks() (gas: 9697080)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.20ms (3.02ms CPU time)\n\nRan 9 tests for test/frens/IMD6900FrensValidator.t.sol:IMD6900FrensValidatorTest\n[PASS] test_AuthorizationDoesNotOutliveTheSale() (gas: 193293)\n[PASS] test_AuthorizationFromTwoTopLevelCallsIsLost() (gas: 86867)\nLogs:\n  sale authorized in a separate top-level call: blocked\n\n[PASS] test_AuthorizationIsForThatFrenOnly() (gas: 394696)\n[PASS] test_AuthorizationIsForThatOperatorOnly() (gas: 479809)\n[PASS] test_ConduitAloneIsBlocked() (gas: 53267)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 79352)\n[PASS] test_HolderSendsItHerself() (gas: 87060)\n[PASS] test_OnlyTheAuthorizerAuthorizes() (gas: 74587)\n[PASS] test_ZoneAuthorizedSaleGoesThroughInOneFrame() (gas: 124594)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 7.10ms (1.13ms CPU time)\n\nRan 18 tests for test/frens/FrenWorkerGate.t.sol:FrenWorkerGateTest\n[PASS] test_ARefusedMintTakesNothing() (gas: 104954)\n[PASS] test_AnNftCountsOnce_whoeverHoldsItLater() (gas: 151129)\n[PASS] test_CreditsGoWhereTheHolderSays() (gas: 639698)\n[PASS] test_NoGateNoWindow() (gas: 442079)\n[PASS] test_OnlyFrensSpends() (gas: 32613)\n[PASS] test_OnlyTheHolderClaims() (gas: 67063)\n[PASS] test_OpenPublic() (gas: 528969)\n[PASS] test_OwnerMintsBeforeTheOpening() (gas: 2296619)\n[PASS] test_SeatOperatorClaimsTheStrategysNfts() (gas: 138514)\n[PASS] test_TheCreditIsTheMinters() (gas: 576375)\n[PASS] test_WindowClosesAfter420() (gas: 32492721)\n[PASS] test_WindowNeedsACreditPerFren() (gas: 714583)\n[PASS] test_WindowNeverOvershoots() (gas: 36198264)\n[PASS] test_WlAndWorkersShareThe420() (gas: 619410)\n[PASS] test_WlOnlyAsListed_once() (gas: 254028)\n[PASS] test_WlRaisedAmount_claimsTheDifference() (gas: 226858)\n[PASS] test_WlRootOnlyTheOwner_andNoRootNoWl() (gas: 62252)\n[PASS] test_WlWalletsMintInTheWindow_asMuchAsListed() (gas: 750975)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 10.44ms (8.35ms CPU time)\n\nRan 13 tests for test/FrensRevisionRisks.t.sol:FrensRevisionRisksTest\n[PASS] test_Risk_DuplicatePendingNonceStrandsOnePayment() (gas: 1554719)\n[PASS] test_Risk_DustEthTakesFiftyBlockTurns() (gas: 6374674)\n[PASS] test_Risk_EthMintPushesItsOwnPendingEthPrice() (gas: 5152892)\nLogs:\n  shown IMD: 1587628858579375267300\n  paid IMD: 979046693648650397080\n  refunded IMD: 608582164930724870220\n\n[PASS] test_Risk_FloorBoundJobCostIsSocialised() (gas: 3564025)\n[PASS] test_Risk_FloorQuotesUseExtremeLimits() (gas: 6173436)\n[PASS] test_Risk_FloorViewOmitsJobRefund() (gas: 857288)\n[PASS] test_Risk_PayeeChangeLeavesExpiredPermitDigest() (gas: 742744)\n[PASS] test_Risk_PendingEthSpotPushDilutesExistingHolders() (gas: 5288831)\n[PASS] test_Risk_PushMintUnpushHasPositiveMarkedValue() (gas: 6578235)\nLogs:\n  round trip ETH cost: 135188802651085725\n  marked IMD surplus: 106904119598384566859\n\n[PASS] test_Risk_RecycleOmitsPendingEthPricedByTreasury() (gas: 1093066)\n[PASS] test_Risk_StrangerSpendsWorkersCredit() (gas: 1493190)\n[PASS] test_Risk_UnwiringSwapperAllowsCheapReserveCapture() (gas: 992877)\n[PASS] test_Risk_ZeroOrOneFeeLimitEqualsCurrentPrice() (gas: 6176076)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 15.56ms (12.63ms CPU time)\n\nRan 8 tests for test/FrensLaunchReview.t.sol:FrensAuditReviewTest\n[PASS] test_Audit_GovernorCanCloseMintAgain() (gas: 463670)\n[PASS] test_Audit_OwnerOrGovernorCanBlockAllPeerTransfers() (gas: 1379074)\n[PASS] test_Fix_LapsedJobApprovalIsReleasedByTheReveal() (gas: 1037000)\n[PASS] test_Fix_LastFrenOutStaysOutSoFeesHaveAnOwner() (gas: 1203616)\n[PASS] test_Fix_LaunchAdoptsASwapperSeededAtThePoolsPrice() (gas: 2008131)\n[PASS] test_Fix_LaunchRefusesTheWrongArt() (gas: 18811)\n[PASS] test_Fix_LaunchReplacesASwapperSeededOffASkewedPrice() (gas: 2745326)\n[PASS] test_Fix_LaunchsOwnSwapperStartsAtThePoolsPrice() (gas: 237014)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 45.40ms (5.54ms CPU time)\n\nRan 16 tests for test/FrensLaunchAdaptation.t.sol:FrensLaunchAdaptationTest\n[PASS] test_BootstrapThenOpenAndResume() (gas: 2315891)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\n[PASS] test_FirstFrensBeforeFeesPreventsPublicFloorCapture() (gas: 3435830)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_FirstFrensRequiresPausedBuysAndNonzeroCount() (gas: 1785691)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Fix_SpentNonceIsRefused() (gas: 2311018)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeRefuseAnOwnerlessFloor() (gas: 1828783)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeRejectChangedWiring() (gas: 2680591)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeWarnWhenAverageLags() (gas: 4784847)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  WARNING: the swapper's average is more than 2x off the pool's price\n  WARNING: the swapper's average is more than 2x off the pool's price\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\n[PASS] test_PlacedRejectsMissingOrWrongModules() (gas: 126575)\n[PASS] test_PlacedRejectsWrongCollectionAndForeignDependencies() (gas: 65561)\n[PASS] test_PlacedReportsTheLaunchModules() (gas: 51824)\n[PASS] test_ResumeRequiresDistributorAndActualSwapperExemption() (gas: 2164855)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Risk_FirstMintCapturesExistingFloorIfRunbookIsBypassed() (gas: 3054450)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Risk_LaunchBlockPushSeedsSwapperAndSetupRejectsAfterPriceReturns() (gas: 2572174)\n[PASS] test_Risk_UnsupportedRoyaltyTokenHasNoFloorRoute() (gas: 3781565)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_SetupPausesBeforeFirstMintEvenWhenAlreadyDistributor() (gas: 1815518)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_SetupUsesTheLaunchsReplacementSwapper() (gas: 6981779)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 45.36ms (18.16ms CPU time)\n\nRan 3 tests for test/FrensLaunchReview.t.sol:FrensFactoryReviewTest\n[PASS] test_Audit_ConstructorLeavesTheDocumentedTeamSetup() (gas: 25294534)\n[PASS] test_FreshChainCallsOnlyContractsCreatedByTheLaunch() (gas: 28696930)\n[PASS] test_ProtectedFactoryDeploysBothLaunchesInOrder() (gas: 25493481)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 56.80ms (15.88ms CPU time)\n\nRan 3 tests for test/FrensResidualRisks.t.sol:FrensResidualRisksTest\n[PASS] test_Risk_DuplicatePendingNonceStrandsOnePayment() (gas: 1554494)\n[PASS] test_Risk_PendingEthSpotPushDilutesExistingHolders() (gas: 5288577)\n[PASS] test_Risk_UnwiringSwapperAllowsCheapReserveCapture() (gas: 992804)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 60.56ms (2.10ms CPU time)\n\nRan 6 tests for test/frens/FrenSwapperAverage.t.sol:FrenSwapperAverageTest\n[PASS] test_AFullBuyMovesItOneStep() (gas: 103606)\n[PASS] test_ASmallBuyMovesItInProportion() (gas: 52842)\n[PASS] test_DustMovesItNextToNothing() (gas: 7606082)\n[PASS] test_FloorRateIsTheLowerReading() (gas: 17282)\n[PASS] test_MovesDownToo() (gas: 52658)\n[PASS] test_OnceABlock() (gas: 91443)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 61.55ms (9.20ms CPU time)\n\nRan 86 tests for test/frens/IMD6900Frens.t.sol:IMD6900FrensTest\n[PASS] test_AMintNeverFailsOnTheSwap() (gas: 1003226)\n[PASS] test_BatchCannotRepeatAFren() (gas: 641744)\n[PASS] test_BatchCountIsOneTo69() (gas: 2185647)\n[PASS] test_BatchCountsTheCapsAsItGoes() (gas: 11780401)\n[PASS] test_BatchHoldsPepesForLowTiers() (gas: 12176238)\n[PASS] test_BatchPaysOneJob() (gas: 700213)\n[PASS] test_BatchRevealIsExactlyTheVoucher() (gas: 1011974)\n[PASS] test_BatchRevealsThemAll() (gas: 2256463)\n[PASS] test_BatchSoldOut() (gas: 90792483)\n[PASS] test_BuyTreasuryRespectsMaxPay() (gas: 1842004)\n[PASS] test_BuyingFromTheTreasuryRaisesTheFloor() (gas: 3101386)\n[PASS] test_CurveWhileAboveTheFloor() (gas: 1599825)\n[PASS] test_DearerImd6900RaisesTheFloorPrice() (gas: 2861964)\n[PASS] test_DustEthBuyCannotTakeTheMintsTurn() (gas: 540029)\n[PASS] test_EveryFrenIsOneOfAKind() (gas: 947080)\n[PASS] test_FailedJob_payAnother() (gas: 1173029)\n[PASS] test_FeeEthBuysTheFloor() (gas: 1708734)\n[PASS] test_FloorBuysAreAnyones_onePerBlock() (gas: 1893053)\n[PASS] test_FloorBuysAreCapped() (gas: 55694600)\n[PASS] test_FloorIsReserveOverFrensOut() (gas: 2761464)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 2145216)\n[PASS] test_FrenSentStraightToTheTreasuryCounts() (gas: 2660288)\n[PASS] test_IsACreatorToken() (gas: 41743)\n[PASS] test_JobPaymentIsPinnedToImdsPayee() (gas: 980756)\n[PASS] test_KeeperApprovesTheJobPaidFor() (gas: 657241)\n[PASS] test_KeeperCannotApproveASpentNonce() (gas: 490236)\n[PASS] test_KeeperCannotApproveLongLivedPayments() (gas: 434019)\n[PASS] test_LapsedPaymentIsUsedAgain() (gas: 719034)\n[PASS] test_LastFrenOutStaysOut() (gas: 1615217)\n[PASS] test_LaterPartsAgreeOnWhatIsRevealed() (gas: 1347339)\n[PASS] test_LowTierRequestsNeedAPepeLeft() (gas: 11708397)\n[PASS] test_MaxMintGrowsWithTheBag() (gas: 4682663)\n[PASS] test_MaxPayStopsAPriceThatMoved() (gas: 10063499)\n[PASS] test_MaxRequest() (gas: 9863897)\n[PASS] test_MintMintsAtOnce_unrevealed() (gas: 770449)\n[PASS] test_MintNeedsSealedTraits() (gas: 8623021)\n[PASS] test_MintNeverBelowTheFloor() (gas: 3425403)\n[PASS] test_MintNeverSkipsTheBuyForWantOfGas() (gas: 708870)\n[PASS] test_NoMintIntoTheTreasury() (gas: 1596022)\n[PASS] test_NoRequestForNobody() (gas: 61912)\n[PASS] test_NoTierInsideAFlashLoan() (gas: 687636)\n[PASS] test_NobodyElseApprovesJobs() (gas: 435635)\n[PASS] test_OneRevealPerFren() (gas: 717718)\n[PASS] test_OnlyHoldersRecycle() (gas: 1593666)\n[PASS] test_OnlyRealFrens() (gas: 743319)\n[PASS] test_OnlyTheOwnerSetsTheValidator() (gas: 1063173)\n[PASS] test_OwnerAdjustsTheArtUntilFrozen() (gas: 1555030)\n[PASS] test_PairRules() (gas: 875556)\n[PASS] test_PartsOnlyGoForward() (gas: 1035497)\n[PASS] test_PendingEthCountsInTheMintPrice() (gas: 2181876)\n[PASS] test_PriceRisesWithMints() (gas: 38125610)\n[PASS] test_PriceTableIsTheCurve() (gas: 36977950)\n[PASS] test_QuoteCountsUnsweptImd() (gas: 3216110)\n[PASS] test_RareTraitsNeedTheTier() (gas: 1365734)\n[PASS] test_RecyclePaysTheFloorAndKeepsIt() (gas: 2915603)\n[PASS] test_RecyclePaysTheWaitingImdToo() (gas: 2749360)\n[PASS] test_ReleaseLapsedJobAfterTheReveal() (gas: 2142099)\n[PASS] test_RequestMintFor() (gas: 1084060)\n[PASS] test_ReserveOnlyLeavesThroughRecycle() (gas: 2838128)\n[PASS] test_RevealFollowsTheFren() (gas: 749220)\n[PASS] test_RevealInParts() (gas: 9747514)\nLogs:\n  gas, minting 69: 2117887\n  gas, revealing 30: 3092845\n\n[PASS] test_RevealNeedsTheRelayersVoucher() (gas: 803651)\n[PASS] test_RevealReleasesALapsedPayment() (gas: 919103)\n[PASS] test_RevealShowsTheFren() (gas: 1192018)\n[PASS] test_RevealsWhenever() (gas: 661560)\n[PASS] test_RoyaltiesPayTheFloor() (gas: 1724930)\n[PASS] test_RoyaltyBpsIsCappedAndOwnerOnly() (gas: 109412)\n[PASS] test_RulesAreFrozenOnceSealed() (gas: 69580)\n[PASS] test_SealNeedsEveryTraitAndTheSupply() (gas: 10518169)\n[PASS] test_SetMaxMint() (gas: 178238)\n[PASS] test_ShortSwapReverts() (gas: 1046881)\n[PASS] test_SwapperMayPullOnlyTheBuy() (gas: 957490)\n[PASS] test_TheOwnerKeepsTheCollection_theGovernorTheMechanics() (gas: 488664)\n[PASS] test_TierCountsWhatIsLeftAfterPaying() (gas: 920004)\n[PASS] test_TierIsTheBestOfTheBag() (gas: 633505)\n[PASS] test_TierIsTheMints() (gas: 1350710)\n[PASS] test_TierThresholdsOwnerOnlyAndOrdered() (gas: 154928)\n[PASS] test_TradesGoPastTheValidator() (gas: 1346243)\n[PASS] test_TraitCapsHold() (gas: 25778206)\n[PASS] test_TreasuryRoundTripCostsTheFloor() (gas: 3295585)\n[PASS] test_UnneededRetryFeedsTheFloor() (gas: 1026116)\n[PASS] test_UnrevealedFrenSellsAtTheFloor() (gas: 1022775)\n[PASS] test_UnrevealedFrensShareTheFloor() (gas: 1466205)\n[PASS] test_UnsweptImdCountsInTheTreasuryPrice() (gas: 3367000)\n[PASS] test_UnusedJobFeedsTheFloor() (gas: 696981)\n[PASS] test_VouchersExpire() (gas: 472492)\nSuite result: ok. 86 passed; 0 failed; 0 skipped; finished in 68.33ms (182.99ms CPU time)\n\nRan 12 tests for test/FrensArtFraming.t.sol:FrensArtFramingTest\n[PASS] testFuzz_EntryPastTheIndexIsMissing(uint256) (runs: 256, μ: 13986, ~: 13943)\nLogs:\n  Bound result 955939965724\n\n[PASS] testFuzz_UnknownBackgroundIsMissing(uint256) (runs: 256, μ: 35608, ~: 35592)\nLogs:\n  Bound result 955939965724\n\n[PASS] test_ATamperedChunkDrawsNothing() (gas: 90530)\n[PASS] test_AttributesDoNotValidate() (gas: 369298)\n[PASS] test_ChunksAreFramedPushData() (gas: 2731712)\n[PASS] test_EveryValueHasAName() (gas: 15679313)\n[PASS] test_IndexHashesAreTheChunks() (gas: 788314)\n[PASS] test_IndexPointsInsideTheChunks() (gas: 525933)\n[PASS] test_LaunchLayersAreWellFormed() (gas: 46186857)\n[PASS] test_NoSuchFrenIsMissing() (gas: 4375022)\n[PASS] test_PalettesFitTogether() (gas: 13989817)\n[PASS] test_WithoutTheSwarmNothingDraws() (gas: 5947823)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 73.20ms (257.36ms CPU time)\n\nRan 19 tests for test/FrensLaunchFailures.t.sol:FrensLaunchFailuresTest\n[PASS] testFuzz_CheckKnowsAFren(uint24,uint8) (runs: 512, μ: 1937016, ~: 1907374)\nLogs:\n  Bound result 2\n\n[PASS] testFuzz_OtherSaltsLandElsewhere(bytes32) (runs: 256, μ: 34990, ~: 34990)\n[PASS] testFuzz_QuoteIsTheCurveBeforeAnyoneHolds(uint256) (runs: 512, μ: 319965, ~: 309934)\nLogs:\n  Bound result 68\n\n[PASS] test_AnotherOwnerMovesTheFrens() (gas: 34259)\n[PASS] test_CollectionRefusesAShortPriceTable() (gas: 3059925)\n[PASS] test_CollectionWaitsForTheTeamWallet() (gas: 5941480)\n[PASS] test_EveryPriceIsTheTables() (gas: 27580866)\n[PASS] test_FreshChainLaunchesAreIndependent() (gas: 1351085)\n[PASS] test_LaunchContractsTakeNoEth() (gas: 687657)\n[PASS] test_ModulesFailOverCorruptedFrens() (gas: 97101)\n[PASS] test_ModulesNeedTheCollection() (gas: 106270)\n[PASS] test_NoLaunchActorHoldsARole() (gas: 4580578)\n[PASS] test_NoPricePastTheTable() (gas: 41219)\n[PASS] test_OtherSaltSchemeFailsTheLaunch() (gas: 140656)\n[PASS] test_RefusingDeployerFailsTheLaunch() (gas: 103369)\n[PASS] test_ScriptRefusesAnUnplacedChain() (gas: 14786192)\n[PASS] test_SecondLaunchReusesWhatIsPlaced() (gas: 1313404)\n[PASS] test_SilentDeployerFailsTheLaunch() (gas: 93234)\n[PASS] test_WrongArtFailsTheArtLaunch() (gas: 588910)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 76.32ms (246.56ms CPU time)\n\nRan 13 tests for test/FrensPlacement.t.sol:FrensPlacementTest\n[PASS] test_Attributes() (gas: 19594305)\n[PASS] test_CodeIsWhatTheSourcesBuild() (gas: 89696)\n[PASS] test_DeploysOnAFreshChain() (gas: 51811985)\n[PASS] test_EachLaunchFitsOneTransaction() (gas: 105863223)\nLogs:\n  the collection launch: gas, all in: 12451232\n  the art launch: gas, all in: 13605058\n\n[PASS] test_LandsWhereThePlanSays() (gas: 51923654)\n[PASS] test_NewArtReadsBackExactly() (gas: 14173584)\n[PASS] test_PassesTheAdmissionScan() (gas: 88302675)\n[PASS] test_PlanFollowsFromTheCode() (gas: 80750)\n[PASS] test_PlanMatchesTheScript() (gas: 14314057)\n[PASS] test_PriceTableIsTheCurve() (gas: 53105398)\n[PASS] test_RefusesOtherArt() (gas: 12971045)\n[PASS] test_SameAddressesWhoeverRunsIt() (gas: 102774643)\n[PASS] test_TakesWhatSomeonePlacedFirst() (gas: 52491481)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 99.01ms (198.56ms CPU time)\n\nRan 1 test for test/FrensSplitGas.t.sol:FrensSplitGasTest\n[PASS] test_CollectionFitsButLegacyFourContractBundleExceedsCap() (gas: 101750011)\nLogs:\n  legacy four-contract launch: 23877683\n  two-contract collection launch: 13494921\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 126.07ms (125.96ms CPU time)\n\nRan 1 test for test/FrensPlacedInvariants.t.sol:FrensPlacedInvariantsTest\n[PASS]\nFrensPlacedInvariantsTest invariants:\n[PASS] invariant_CapsHold\n[PASS] invariant_FloorIsAShareOfTheWorld\n[PASS] invariant_FrensAreConserved\n[PASS] invariant_ImdIsTheBooks\n[PASS] invariant_ReserveIsExactlyWhatIsHere\n FrensPlacedInvariantsTest invariants (runs: 32, calls: 1920, reverts: 2)\n\n╭--------------------+-----------------+-------+---------+----------╮\n| Contract           | Selector        | Calls | Reverts | Discards |\n+===================================================================+\n| PlacedFrensHandler | approveJob      | 118   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyFloor        | 102   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyTreasury     | 107   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donate6900      | 108   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donateImd       | 128   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | feeEth          | 138   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mint            | 242   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mintThenRecycle | 119   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | recycle         | 124   | 2       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | retryJob        | 129   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | reveal          | 119   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setParams       | 135   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setRate         | 123   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | transfer        | 123   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | warp            | 105   | 0       | 0        |\n╰--------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 13196\n  Bound result 37\n  Bound result 50000\n  Bound result 2\n  Bound result 12172715070786\n  Bound result 499999999999999997\n  Bound result 1000000000000000000000\n  Bound result 0\n  Bound result 17892\n  Bound result 380499999999999399\n  Bound result 806\n  Bound result 12\n  Bound result 3\n  Bound result 9\n  Bound result 499999999993000001\n  Bound result 35\n  Bound result 12\n  Bound result 7585\n  Bound result 5\n  Bound result 37\n  Bound result 47\n  Bound result 2786\n  Bound result 2\n  Bound result 13199\n  Bound result 80\n  Bound result 425804797564849584310\n  Bound result 2\n  Bound result 50\n  Bound result 37\n  Bound result 47\n  Bound result 28802\n  Bound result 21326\n  Bound result 14500\n  Bound result 4\n  Bound result 25\n  Bound result 7\n  Bound result 1\n  Bound result 103816423492441564052\n  Bound result 615598494\n  Bound result 96\n  Bound result 53\n  Bound result 2\n  Bound result 62\n  Bound result 68\n  Bound result 60000000000000000000\n  Bound result 3\n  Bound result 82\n  Bound result 9805\n  Bound result 0\n  Bound result 55\n  Bound result 500\n  Bound result 2\n  Bound result 58\n  Bound result 10125\n  Bound result 10\n  Bound result 0\n  Bound result 0\n  Bound result 69\n  Bound result 58\n  Bound result 1\n  Bound result 19419\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.48s (2.47s CPU time)\n\nRan 20 test suites in 2.49s (3.24s CPU time): 209 tests passed, 0 failed, 5 skipped (214 total tests)\n","passed":true},{"durationMs":89,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"FrenMinter.buyImd(uint256)\",\"FrenMinter.mintWithEth(uint8,uint256)\",\"FrenMinter.quoteEth(uint8)\",\"FrenMinter.quoteFloor(uint256)\",\"FrenMinter.quoteFloorEth(uint256)\",\"FrenMinter.unlockCallback(bytes)\",\"FrenSwapper.ethToImd6900(uint256,address)\",\"FrenSwapper.imdToImd6900(uint256,uint256,address)\",\"FrenSwapper.unlockCallback(bytes)\",\"FrenWorkerGate.cancelOwnershipHandover()\",\"FrenWorkerGate.claim(uint256[],address)\",\"FrenWorkerGate.claimWl(uint256,bytes32[],address)\",\"FrenWorkerGate.completeOwnershipHandover(address)\",\"FrenWorkerGate.openPublic()\",\"FrenWorkerGate.renounceOwnership()\",\"FrenWorkerGate.requestOwnershipHandover()\",\"FrenWorkerGate.setWlRoot(bytes32)\",\"FrenWorkerGate.spend(address,uint256)\",\"FrenWorkerGate.transferOwnership(address)\",\"IMD6900Frens.addPairRule((uint8,uint8,uint8,uint8,uint8))\",\"IMD6900Frens.approve(address,uint256)\",\"IMD6900Frens.approveJob(uint256,uint256,uint256,(string,bytes32,string,bytes32,bytes32,string,uint256))\",\"IMD6900Frens.buyFloor(uint256)\",\"IMD6900Frens.buyFloorWithEth(uint256,uint256)\",\"IMD6900Frens.buyTreasury(uint256,uint256,uint256)\",\"IMD6900Frens.cancelOwnershipHandover()\",\"IMD6900Frens.completeOwnershipHandover(address)\",\"IMD6900Frens.freezeArt()\",\"IMD6900Frens.receive()\",\"IMD6900Frens.recycle(uint256)\",\"IMD6900Frens.releaseLapsedJob(uint256)\",\"IMD6900Frens.renounceOwnership()\",\"IMD6900Frens.requestMint(uint8,uint256)\",\"IMD6900Frens.requestMintFor(address,uint8,uint256)\",\"IMD6900Frens.requestOwnershipHandover()\",\"IMD6900Frens.retryJob(uint256)\",\"IMD6900Frens.reveal(uint256,uint24[],string,bytes32,uint256,bytes,uint256)\",\"IMD6900Frens.safeTransferFrom(address,address,uint256)\",\"IMD6900Frens.safeTransferFrom(address,address,uint256,bytes)\",\"IMD6900Frens.sealTraits()\",\"IMD6900Frens.setApprovalForAll(address,bool)\",\"IMD6900Frens.setGovernor(address)\",\"IMD6900Frens.setMaxMint(uint8[4])\",\"IMD6900Frens.setMintOpen(bool)\",\"IMD6900Frens.setModules(address,address)\",\"IMD6900Frens.setParams(uint256,uint256,uint256)\",\"IMD6900Frens.setRenderer(address)\",\"IMD6900Frens.setRoles(address,address,address)\",\"IMD6900Frens.setRoyalty(uint96)\",\"IMD6900Frens.setTiers(uint256[3],uint256[3],uint256[3])\",\"IMD6900Frens.setTraitRules(uint8,uint16[],uint8[])\",\"IMD6900Frens.setTransferValidator(address)\",\"IMD6900Frens.transferFrom(address,address,uint256)\",\"IMD6900Frens.transferOwnership(address)\",\"IMD6900Frens.unwrapWeth()\"],\"files\":{\".gitignore\":4,\"ADAPTATION.md\":215,\"README.md\":218,\"foundry.toml\":22,\"launch.json\":16,\"script/art/chunks.py\":165,\"script/art/data/bgpal/cleanlab_blue.bin\":1,\"script/art/data/bgpal/cleanlab_green.bin\":1,\"script/art/data/bgpal/cleanlab_red.bin\":1,\"script/art/data/bgpal/messylab_blue.bin\":1,\"script/art/data/bgpal/messylab_green.bin\":1,\"script/art/data/bgpal/messylab_red.bin\":1,\"script/art/data/bgpal/tubeblue.bin\":1,\"script/art/data/bgpal/tubegreen.bin\":2,\"script/art/data/bgpal/tubered.bin\":1,\"script/art/data/bgpal/tubeyellow.bin\":1,\"script/art/data/bgpal/wireframe_green.bin\":2,\"script/art/data/bgpal/wireframe_red.bin\":1,\"script/art/data/expected.json\":53,\"script/art/data/layers/bg0.bin\":9,\"script/art/data/layers/bg1.bin\":9,\"script/art/data/layers/bg10.bin\":32,\"script/art/data/layers/bg11.bin\":32,\"script/art/data/layers/bg2.bin\":9,\"script/art/data/layers/bg3.bin\":15,\"script/art/data/layers/bg4.bin\":15,\"script/art/data/layers/bg5.bin\":15,\"script/art/data/layers/bg6.bin\":26,\"script/art/data/layers/bg7.bin\":26,\"script/art/data/layers/bg8.bin\":26,\"script/art/data/layers/bg9.bin\":26,\"script/art/data/layers/coat.bin\":20,\"script/art/data/layers/item5.bin\":2,\"script/art/data/manifest.json\":500,\"script/art/data/shared.bin\":4,\"script/art/data/tables.bin\":2,\"script/art/swarm-v2.json\":374,\"script/frens/DeployFrens.s.sol\":254,\"script/frens/FrensTimelockBatch.s.sol\":105,\"script/frens/price/prices-swarm.bin\":6,\"script/frens/price/prices.bin\":6,\"script/frens/price/prices.json\":26,\"script/frens/price/prices.py\":55,\"script/frens/price/prices_sol.py\":33,\"script/placement/addresses.json\":7,\"script/placement/gen.py\":146,\"src/FrensCode.sol\":15,\"src/FrensPlacement.sol\":129,\"src/FrensPlan.sol\":37,\"src/art/WorkerArt.sol\":671,\"src/art/WorkerArtIndex.sol\":56,\"src/frens/FrenMinter.sol\":194,\"src/frens/FrenPrices.sol\":122,\"src/frens/FrenSwapper.sol\":276,\"src/frens/FrenWorkerGate.sol\":137,\"src/frens/IMD6900Frens.sol\":1083,\"src/frens/WorkerFrensRenderer.sol\":377,\"test/FrensArtFraming.t.sol\":290,\"test/FrensLaunchAdaptation.t.sol\":331,\"test/FrensLaunchFailures.t.sol\":526,\"test/FrensLaunchReview.t.sol\":449,\"test/FrensPlacedInvariants.t.sol\":634,\"test/FrensPlacement.t.sol\":818,\"test/FrensResidualRisks.t.sol\":210,\"test/FrensRevisionRisks.t.sol\":336,\"test/FrensSplitGas.t.sol\":68,\"test/frens/DataWriter.sol\":21,\"test/frens/FrenMinter.fork.t.sol\":189,\"test/frens/FrenSwapper.fork.t.sol\":134,\"test/frens/FrenSwapperAverage.t.sol\":77,\"test/frens/FrenWorkerGate.t.sol\":350,\"test/frens/FrensRules.sol\":75,\"test/frens/IMD6900Frens.t.sol\":1763,\"test/frens/IMD6900FrensPermit2.fork.t.sol\":130,\"test/frens/IMD6900FrensValidator.fork.t.sol\":146,\"test/frens/IMD6900FrensValidator.t.sol\":231,\"test/test_collection_manifest.py\":56},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"d4472861693d7be70004da417b876947a2ebf9c9b5600372afb962354b557d12","verifiedTreeHash":"2d812a91187956144748eea2ddd41dfb123d9763","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":160056,"exitCode":0,"name":"build","output":"Compiling 94 files with Solc 0.8.30\nSolc 0.8.30 finished in 159.84s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> test/FrensPlacement.t.sol:786:9:\n    |\n786 |         uint256 s = _find(j, key, 0) + key.length;\n    |         ^^^^^^^^^\nNote: The shadowed declaration is here:\n   --> test/FrensPlacement.t.sol:477:5:\n    |\n477 |     DeployFrens s;\n    |     ^^^^^^^^^^^^^\n\nWarning (2018): Function state mutability can be restricted to view\n   --> test/FrensLaunchAdaptation.t.sol:199:5:\n    |\n199 |     function test_PlacedReportsTheLaunchModules() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/frens/IMD6900Frens.sol:964:66\n    │\n964 │         return (ITransferValidator721.validateTransfer.selector, true);\n    │                                                                  ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:39\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                       ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenWorkerGate.sol:58:33\n   │\n58 │     constructor(address owner_, address frens_, address identity_, address strategy_) {\n   │                                 ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenWorkerGate.sol:58:68\n   │\n58 │     constructor(address owner_, address frens_, address identity_, address strategy_) {\n   │                                                                    ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:55\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                                       ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenMinter.sol:51:77\n   │\n51 │     constructor(address poolManager_, address frens_, address imdPoolHook_, address pairHook_) {\n   │                                                                             ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:73:9\n   │\n73 │         address imd6900_,\n   │         ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[calls-loop]: external call inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:76:30\n   │\n76 │             address holder = identity.ownerOf(id);\n   │                              ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:77:78\n   │\n77 │             if (holder != msg.sender && (holder != strategy || msg.sender != ISeatStrategyMin(strategy).seatOperator()))\n   │                                                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:72:9\n   │\n72 │         address imd_,\n   │         ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:74:9\n   │\n74 │         address frens_,\n   │         ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:76:9\n   │\n76 │         address imdPoolHook_\n   │         ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:75:9\n   │\n75 │         address pairHook_,\n   │         ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/frens/WorkerFrensRenderer.sol:59:29\n   │\n59 │           bytes memory json = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n60 │ ┃             '{\"name\":\"Worker Fren #',\n61 │ ┃             tokenId.toString(),\n62 │ ┃             '\",\"description\":\"',\n   ‡ ┃\n68 │ ┃             \"}\"\n69 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:79:17\n   │\n79 │                 revert NotYours(id);\n   │                 ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/frens/FrenWorkerGate.sol:81:30\n   │\n81 │             if (claimed[id]) revert AlreadyClaimed(id);\n   │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/frens/WorkerFrensRenderer.sol:75:29\n   │\n75 │           bytes memory json = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n76 │ ┃             '{\"name\":\"Worker Fren #',\n77 │ ┃             tokenId.toString(),\n78 │ ┃             '\",\"description\":\"',\n   ‡ ┃\n82 │ ┃             '\",\"attributes\":[{\"trait_type\":\"Status\",\"value\":\"Unrevealed\"}]}'\n83 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/WorkerFrensRenderer.sol:124:21\n    │\n124 │                 if (k == i) {\n    │                     ━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/WorkerFrensRenderer.sol:125:54\n    │\n125 │                     bytes memory out = new bytes(j - start);\n    │                                                      ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/frens/WorkerFrensRenderer.sol:158:17\n    │\n158 │ ┏                 abi.encodePacked(\n159 │ ┃                     '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 84 84\" width=\"840\" height=\"840\">',\n160 │ ┃                     '<image width=\"84\" height=\"',\n161 │ ┃                     (N * PENDING_FRAMES).toString(),\n    ‡ ┃\n165 │ ┃                     'dur=\"1.3s\" calcMode=\"discrete\" repeatCount=\"indefinite\"/></image></svg>'\n166 │ ┃                 )\n    │ ┗━━━━━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/FrenWorkerGate.sol:105:9\n    │\n105 │         emit WlRootSet(root);\n    │         ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:208:35\n    │\n208 │                 bytes1 v = bytes1(uint8(l * 3 / 5));\n    │                                   ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:221:42\n    │\n221 │         _draw(cv, _entry(Art.BG0 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                          ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:221:62\n    │\n221 │         _draw(cv, _entry(Art.BG0 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                              ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:253:74\n    │\n253 │         if (ch > 2 || face >= FACES || coat > 2 || shirt > 5 || hat > 2) revert Missing();\n    │                                                                          ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:350:31\n    │\n350 │         if (i >= Art.ENTRIES) revert Missing();\n    │                               ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:356:72\n    │\n356 │         if (p.codehash != bytes32(_hex(Art.CHUNK_HASHES, c * 64, 64))) revert BadArt();\n    │                                                                        ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/WorkerFrensRenderer.sol:288:25\n    │\n288 │                         revert(28, 4)\n    │                         ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:232:56\n    │\n232 │         _draw(back, _entry(Art.BG0 + Art.PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                        ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:232:76\n    │\n232 │         _draw(back, _entry(Art.BG0 + Art.PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, false);\n    │                                                                            ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:239:23\n    │\n239 │             _fren(cv, uint24(combo));\n    │                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:261:22\n    │\n261 │         int256 dx = -int256(CX);\n    │                      ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:262:22\n    │\n262 │         int256 dy = -int256(CY);\n    │                      ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/WorkerFrensRenderer.sol:321:56\n    │\n321 │         for (uint256 i; i < n; ++i) b[at + i] = bytes1(uint8(v >> (8 * i)));\n    │                                                        ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/frens/WorkerFrensRenderer.sol:370:49\n    │\n370 │             extcodecopy(p, add(raw, 32), add(1, mul(first, 33)), mul(frames, 33))\n    │                                                 ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[missing-events-access-control]: `keeper` is changed without an event but is used for access control\n     ╭▸ src/frens/IMD6900Frens.sol:1009:36\n     │\n1009 │         if (keeper_ != address(0)) keeper = keeper_;\n     │                                    ━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `governor` is changed without an event but is used for access control\n     ╭▸ src/frens/IMD6900Frens.sol:1075:9\n     │\n1075 │         governor = g;\n     │         ━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:118:35\n    │\n118 │             if (r.length == 68 && bytes4(r) == Quoted.selector) {\n    │                                   ━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/FrensPlacement.sol:54:13\n   │\n54 │ ┏             abi.encodePacked(\n55 │ ┃                 FrensCode.FRENS,\n56 │ ┃                 abi.encode(\n57 │ ┃                     FrensPlan.OWNER,\n   ‡ ┃\n68 │ ┃             )\n   │ ┗━━━━━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/FrensPlacement.sol:93:29\n   │\n93 │           bytes memory init = abi.encodePacked(\n   │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n94 │ ┃             FrensCode.SWAPPER,\n95 │ ┃             abi.encode(\n96 │ ┃                 FrensPlan.POOL_MANAGER, FrensPlan.IMD, FrensPlan.IMD6900, f, FrensPlan.PAIR_HOOK, FrensPlan.POOL4_HOOK\n97 │ ┃             )\n98 │ ┃         );\n   │ ┗━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/FrensPlacement.sol:109:13\n    │\n109 │ ┏             abi.encodePacked(\n110 │ ┃                 FrensCode.MINTER, abi.encode(FrensPlan.POOL_MANAGER, f, FrensPlan.POOL4_HOOK, FrensPlan.PAIR_HOOK)\n111 │ ┃             )\n    │ ┗━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/FrensPlacement.sol:115:13\n    │\n115 │             abi.encodePacked(FrensCode.GATE, abi.encode(FrensPlan.OWNER, f, FrensPlan.IDENTITY, FrensPlan.IMD6900))\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:138:79\n    │\n138 │ …     imdKey(), SwapParams({zeroForOne: true, amountSpecified: -int256(amount), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1}), \"\"\n    │                                                                 ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:140:25\n    │\n140 │                 imdIn = uint256(uint128(e.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:140:33\n    │\n140 │                 imdIn = uint256(uint128(e.amount1()));\n    │                                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:148:39\n    │\n148 │                     amountSpecified: -int256(imdIn),\n    │                                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:153:35\n    │\n153 │             revert Quoted(amount, uint256(uint128(imdIs0 ? p.amount1() : p.amount0())));\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:153:43\n    │\n153 │             revert Quoted(amount, uint256(uint128(imdIs0 ? p.amount1() : p.amount0())));\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:158:70\n    │\n158 │ …     imdKey(), SwapParams({zeroForOne: true, amountSpecified: int256(imdOut), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1}), \"\"\n    │                                                                ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:160:25\n    │\n160 │         uint256 ethIn = uint256(uint128(-d.amount0()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:160:33\n    │\n160 │         uint256 ethIn = uint256(uint128(-d.amount0()));\n    │                                 ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:161:23\n    │\n161 │         uint256 got = uint256(uint128(d.amount1()));\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenMinter.sol:161:31\n    │\n161 │         uint256 got = uint256(uint128(d.amount1()));\n    │                               ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenMinter.sol:165:9\n    │\n165 │         poolManager.settle{value: ethIn}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/FrenMinter.sol:183:29\n    │\n183 │             if (!withEth && cost > a) continue;\n    │                             ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:178:17\n    │\n178 │             try frens.quote(n) returns (uint256 q) {\n    │                 ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:22\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                      ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:51\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                                                   ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:190:84\n    │\n190 │             if (a >= frens.imdTier(t - 1) || b >= frens.imd6900Tier(t - 1) || c >= frens.identityTier(t - 1)) return t;\n    │                                                                                    ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:177:16\n    │\n177 │         return uint160(zeroForOne ? uint256(p) * 1e18 / f : uint256(p) * f / 1e18);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/FrenMinter.sol:184:22\n    │\n184 │             if (n <= frens.maxMint(_tier(withEth ? a : a - cost, b, c))) return n;\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:211:35\n    │\n211 │                 amountSpecified: -int256(amountIn),\n    │                                   ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:219:23\n    │\n219 │         uint256 pay = uint256(uint128(-paid));\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:219:31\n    │\n219 │         uint256 pay = uint256(uint128(-paid));\n    │                               ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:221:13\n    │\n221 │             poolManager.settle{value: pay}();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:225:13\n    │\n225 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:227:23\n    │\n227 │         uint256 out = uint256(uint128(got));\n    │                       ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:227:31\n    │\n227 │         uint256 out = uint256(uint128(got));\n    │                               ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:248:35\n    │\n248 │                 amountSpecified: -int256(ethIn),\n    │                                   ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:253:27\n    │\n253 │         uint256 ethPaid = uint256(uint128(-d1.amount0()));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:253:35\n    │\n253 │         uint256 ethPaid = uint256(uint128(-d1.amount0()));\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:254:26\n    │\n254 │         uint256 imdGot = uint256(uint128(d1.amount1()));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:254:34\n    │\n254 │         uint256 imdGot = uint256(uint128(d1.amount1()));\n    │                                  ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:261:35\n    │\n261 │                 amountSpecified: -int256(imdGot),\n    │                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:267:27\n    │\n267 │         uint256 imdPaid = uint256(uint128(-paid));\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:267:35\n    │\n267 │         uint256 imdPaid = uint256(uint128(-paid));\n    │                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:268:23\n    │\n268 │         uint256 out = uint256(uint128(got));\n    │                       ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:268:31\n    │\n268 │         uint256 out = uint256(uint128(got));\n    │                               ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/FrenSwapper.sol:270:9\n    │\n270 │         poolManager.settle{value: ethPaid}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:315:9\n    │\n315 │         address identity_,\n    │         ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:312:9\n    │\n312 │         address owner_,\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:314:9\n    │\n314 │         address imd6900_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:316:9\n    │\n316 │         address permit2_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:321:9\n    │\n321 │         address priceTable_\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:313:9\n    │\n313 │         address imd_,\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:317:9\n    │\n317 │         address x402Proxy_,\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:318:9\n    │\n318 │         address imdPayTo_,\n    │         ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:319:9\n    │\n319 │         address keeper_,\n    │         ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:320:9\n    │\n320 │         address relayer_,\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:335:9\n    │\n335 │         emit ICreatorToken.TransferValidatorUpdated(address(0), DEFAULT_TRANSFER_VALIDATOR);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:372:16\n    │\n372 │         return uint8(table >> (8 * trait));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:377:16\n    │\n377 │         return uint8((uint256(combo) >> _shift(trait)) & ((1 << _bits(trait)) - 1));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:386:38\n    │\n386 │             if (minTiers[v] > TIERS) revert BadTraits();\n    │                                      ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/IMD6900Frens.sol:408:13\n    │\n408 │             chars += _rules[v].cap;\n    │             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:371:30\n    │\n371 │         if (trait >= TRAITS) revert BadTraits();\n    │                              ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-no-eth]: external call can be reentered before `floorImd` is updated\n    ╭▸ src/frens/IMD6900Frens.sol:849:13\n    │\n849 │         try IFrenSwapper(swapper).imdToImd6900{gas: FLOOR_BUY_GAS}(imdIn, minOut, address(this)) {}\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:985:9\n    │\n985 │         ITransferValidator721(validator).validateTransfer(msg.sender, from, to, id);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:561:9\n    │\n561 │         emit FrensMinted(requestId, minter, first, count, tier, paid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:859:9\n    │\n859 │         emit FloorBought(spent, 0, got, reserve);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:979:13\n    │\n979 │             revert LastFrenOut();\n    │             ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:557:82\n    │\n557 │         requests[requestId] = Request(minter, tier, lowTier, false, count, 0, 1, uint32(first), 0, 0);\n    │                                                                                  ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/FrenWorkerGate.sol:135:9\n    │\n135 │         emit PublicOpened(workerMinted);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/frens/IMD6900Frens.sol:623:38\n    │\n623 │             h = keccak256(abi.encode(h, combos[i]));\n    │                                      ━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:615:13\n    │\n615 │         if (block.timestamp > deadline) revert BadVoucher();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:643:13\n    │\n643 │             emit Revealed(requestId, tokenId, combo, jobId, outputHash);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:632:28\n    │\n632 │             if (code != 0) revert BadCombo(code);\n    │                            ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/frens/IMD6900Frens.sol:634:90\n    │\n634 │             if (valueOf(combo, CHARACTER) == 0 && pepe.minted + openLowTier >= pepe.cap) revert BadCombo(3);\n    │                                                                                          ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:627:22\n    │\n627 │         r.revealed = uint8(upTo);\n    │                      ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:665:53\n    │\n665 │         if (!r.jobApproved || _spent(r.jobNonce) || block.timestamp <= r.jobDeadline) return false;\n    │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:689:9\n    │\n689 │         emit JobPaid(requestId, msg.sender);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:715:13\n    │\n715 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours || _spent(nonce)) revert BadJob();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/frens/IMD6900Frens.sol:715:53\n    │\n715 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours || _spent(nonce)) revert BadJob();\n    │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:731:9\n    │\n731 │         emit JobApproved(requestId, nonce, deadline, permitDigest, quoteDigest);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/IMD6900Frens.sol:722:25\n    │\n722 │         r.jobDeadline = uint40(deadline);\n    │                         ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint40' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/frens/IMD6900Frens.sol:873:9\n    │\n873 │         IFrenSwapper(swapper).ethToImd6900{value: ethIn}(minOut, address(this));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:895:9\n    │\n895 │         emit FloorBought(imdIn, ethIn, got, reserve);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/frens/IMD6900Frens.sol:873:9\n    │\n873 │         IFrenSwapper(swapper).ethToImd6900{value: ethIn}(minOut, address(this));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:888:9\n    │\n888 │         emit WethUnwrapped(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:911:9\n    │\n911 │         emit Recycled(tokenId, msg.sender, paid, imdPaid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:934:9\n    │\n934 │         emit TreasuryBought(tokenId, msg.sender, paid, imdPaid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/frens/IMD6900Frens.sol:955:35\n    │\n955 │     function setTransferValidator(address validator) external onlyCurator {\n    │                                   ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/frens/IMD6900Frens.sol:957:9\n    │\n957 │         emit ICreatorToken.TransferValidatorUpdated(getTransferValidator(), validator);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n     ╭▸ src/frens/IMD6900Frens.sol:1016:26\n     │\n1016 │     function setRenderer(address r) external onlyCurator {\n     │                          ━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1019:9\n     │\n1019 │         emit Setting(\"renderer\", 0, r);\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1025:9\n     │\n1025 │         emit Setting(\"artFrozen\", 1, renderer);\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n     ╭▸ src/frens/IMD6900Frens.sol:1041:17\n     │\n1041 │                 revert BadTraits();\n     │                 ━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n     ╭▸ src/frens/IMD6900Frens.sol:1053:84\n     │\n1053 │             if (m[i] == 0 || m[i] > MAX_PER_REQUEST || (i > 0 && m[i] < m[i - 1])) revert BadTraits();\n     │                                                                                    ━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n     ╭▸ src/frens/IMD6900Frens.sol:1069:9\n     │\n1069 │         emit Setting(\"royalty\", bps, address(0));\n     │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n     │\n     ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:530:21\n    │\n530 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n531 │         bytes memory sig = _sign(frens, relayerKey, id, c, d);\n532 │         vm.warp(d + 1);\n    │         ────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:540:17\n    │\n540 │         vm.warp(block.timestamp + 30 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:685:50\n    │\n685 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:686:17\n    │\n686 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:699:17\n    │\n699 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:721:50\n    │\n721 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:722:17\n    │\n722 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:737:50\n    │\n737 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:296:21\n    │\n296 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n    ‡\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:742:17\n    │\n742 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:753:17\n    │\n753 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:285:34\n    │\n285 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:279:13\n    │\n279 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/frens/IMD6900Frens.t.sol:760:17\n    │\n760 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/FrensPlacedInvariants.t.sol:337:17\n    │\n337 │         vm.roll(block.number + bound(blocks, 0, 2));\n    │         ────────━━━━━━━━━━━━─────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/FrensPlacedInvariants.t.sol:372:17\n    │\n372 │         vm.roll(block.number + bound(blocks, 0, 2));\n    │         ────────━━━━━━━━━━━━─────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":2897,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/frens/FrenSwapper.fork.t.sol:FrenSwapperForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 530.41µs (0.00ns CPU time)\n\nRan 1 test for test/frens/IMD6900FrensValidator.fork.t.sol:IMD6900FrensValidatorForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 307.82µs (0.00ns CPU time)\n\nRan 1 test for test/frens/IMD6900FrensPermit2.fork.t.sol:IMD6900FrensPermit2ForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 408.94µs (0.00ns CPU time)\n\nRan 1 test for test/frens/FrenMinter.fork.t.sol:FrenMinterForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 2.75ms (0.00ns CPU time)\n\nRan 1 test for test/FrensPlacement.t.sol:FrensPlacementForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 2.86ms (0.00ns CPU time)\n\nRan 1 test for test/FrensResidualRisks.t.sol:FrensAverageResidualRiskTest\n[PASS] test_Risk_DustFirstPinsAverageAcrossOneHundredBlocks() (gas: 9697080)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.37ms (3.20ms CPU time)\n\nRan 3 tests for test/FrensResidualRisks.t.sol:FrensResidualRisksTest\n[PASS] test_Risk_DuplicatePendingNonceStrandsOnePayment() (gas: 1554494)\n[PASS] test_Risk_PendingEthSpotPushDilutesExistingHolders() (gas: 5288577)\n[PASS] test_Risk_UnwiringSwapperAllowsCheapReserveCapture() (gas: 992804)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 6.00ms (3.08ms CPU time)\n\nRan 8 tests for test/FrensLaunchReview.t.sol:FrensAuditReviewTest\n[PASS] test_Audit_GovernorCanCloseMintAgain() (gas: 463670)\n[PASS] test_Audit_OwnerOrGovernorCanBlockAllPeerTransfers() (gas: 1379074)\n[PASS] test_Fix_LapsedJobApprovalIsReleasedByTheReveal() (gas: 1037000)\n[PASS] test_Fix_LastFrenOutStaysOutSoFeesHaveAnOwner() (gas: 1203616)\n[PASS] test_Fix_LaunchAdoptsASwapperSeededAtThePoolsPrice() (gas: 2008131)\n[PASS] test_Fix_LaunchRefusesTheWrongArt() (gas: 18811)\n[PASS] test_Fix_LaunchReplacesASwapperSeededOffASkewedPrice() (gas: 2745326)\n[PASS] test_Fix_LaunchsOwnSwapperStartsAtThePoolsPrice() (gas: 237014)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 11.39ms (5.98ms CPU time)\n\nRan 18 tests for test/frens/FrenWorkerGate.t.sol:FrenWorkerGateTest\n[PASS] test_ARefusedMintTakesNothing() (gas: 104954)\n[PASS] test_AnNftCountsOnce_whoeverHoldsItLater() (gas: 151129)\n[PASS] test_CreditsGoWhereTheHolderSays() (gas: 639698)\n[PASS] test_NoGateNoWindow() (gas: 442079)\n[PASS] test_OnlyFrensSpends() (gas: 32613)\n[PASS] test_OnlyTheHolderClaims() (gas: 67063)\n[PASS] test_OpenPublic() (gas: 528969)\n[PASS] test_OwnerMintsBeforeTheOpening() (gas: 2296619)\n[PASS] test_SeatOperatorClaimsTheStrategysNfts() (gas: 138514)\n[PASS] test_TheCreditIsTheMinters() (gas: 576375)\n[PASS] test_WindowClosesAfter420() (gas: 32492721)\n[PASS] test_WindowNeedsACreditPerFren() (gas: 714583)\n[PASS] test_WindowNeverOvershoots() (gas: 36198264)\n[PASS] test_WlAndWorkersShareThe420() (gas: 619410)\n[PASS] test_WlOnlyAsListed_once() (gas: 254028)\n[PASS] test_WlRaisedAmount_claimsTheDifference() (gas: 226858)\n[PASS] test_WlRootOnlyTheOwner_andNoRootNoWl() (gas: 62252)\n[PASS] test_WlWalletsMintInTheWindow_asMuchAsListed() (gas: 750975)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 12.63ms (10.66ms CPU time)\n\nRan 9 tests for test/frens/IMD6900FrensValidator.t.sol:IMD6900FrensValidatorTest\n[PASS] test_AuthorizationDoesNotOutliveTheSale() (gas: 193293)\n[PASS] test_AuthorizationFromTwoTopLevelCallsIsLost() (gas: 86867)\nLogs:\n  sale authorized in a separate top-level call: blocked\n\n[PASS] test_AuthorizationIsForThatFrenOnly() (gas: 394696)\n[PASS] test_AuthorizationIsForThatOperatorOnly() (gas: 479809)\n[PASS] test_ConduitAloneIsBlocked() (gas: 53267)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 79352)\n[PASS] test_HolderSendsItHerself() (gas: 87060)\n[PASS] test_OnlyTheAuthorizerAuthorizes() (gas: 74587)\n[PASS] test_ZoneAuthorizedSaleGoesThroughInOneFrame() (gas: 124594)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 55.98ms (749.45µs CPU time)\n\nRan 86 tests for test/frens/IMD6900Frens.t.sol:IMD6900FrensTest\n[PASS] test_AMintNeverFailsOnTheSwap() (gas: 1003226)\n[PASS] test_BatchCannotRepeatAFren() (gas: 641744)\n[PASS] test_BatchCountIsOneTo69() (gas: 2185647)\n[PASS] test_BatchCountsTheCapsAsItGoes() (gas: 11780401)\n[PASS] test_BatchHoldsPepesForLowTiers() (gas: 12176238)\n[PASS] test_BatchPaysOneJob() (gas: 700213)\n[PASS] test_BatchRevealIsExactlyTheVoucher() (gas: 1011974)\n[PASS] test_BatchRevealsThemAll() (gas: 2256463)\n[PASS] test_BatchSoldOut() (gas: 90792483)\n[PASS] test_BuyTreasuryRespectsMaxPay() (gas: 1842004)\n[PASS] test_BuyingFromTheTreasuryRaisesTheFloor() (gas: 3101386)\n[PASS] test_CurveWhileAboveTheFloor() (gas: 1599825)\n[PASS] test_DearerImd6900RaisesTheFloorPrice() (gas: 2861964)\n[PASS] test_DustEthBuyCannotTakeTheMintsTurn() (gas: 540029)\n[PASS] test_EveryFrenIsOneOfAKind() (gas: 947080)\n[PASS] test_FailedJob_payAnother() (gas: 1173029)\n[PASS] test_FeeEthBuysTheFloor() (gas: 1708734)\n[PASS] test_FloorBuysAreAnyones_onePerBlock() (gas: 1893053)\n[PASS] test_FloorBuysAreCapped() (gas: 55694600)\n[PASS] test_FloorIsReserveOverFrensOut() (gas: 2761464)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 2145216)\n[PASS] test_FrenSentStraightToTheTreasuryCounts() (gas: 2660288)\n[PASS] test_IsACreatorToken() (gas: 41743)\n[PASS] test_JobPaymentIsPinnedToImdsPayee() (gas: 980756)\n[PASS] test_KeeperApprovesTheJobPaidFor() (gas: 657241)\n[PASS] test_KeeperCannotApproveASpentNonce() (gas: 490236)\n[PASS] test_KeeperCannotApproveLongLivedPayments() (gas: 434019)\n[PASS] test_LapsedPaymentIsUsedAgain() (gas: 719034)\n[PASS] test_LastFrenOutStaysOut() (gas: 1615217)\n[PASS] test_LaterPartsAgreeOnWhatIsRevealed() (gas: 1347339)\n[PASS] test_LowTierRequestsNeedAPepeLeft() (gas: 11708397)\n[PASS] test_MaxMintGrowsWithTheBag() (gas: 4682663)\n[PASS] test_MaxPayStopsAPriceThatMoved() (gas: 10063499)\n[PASS] test_MaxRequest() (gas: 9863897)\n[PASS] test_MintMintsAtOnce_unrevealed() (gas: 770449)\n[PASS] test_MintNeedsSealedTraits() (gas: 8623021)\n[PASS] test_MintNeverBelowTheFloor() (gas: 3425403)\n[PASS] test_MintNeverSkipsTheBuyForWantOfGas() (gas: 708870)\n[PASS] test_NoMintIntoTheTreasury() (gas: 1596022)\n[PASS] test_NoRequestForNobody() (gas: 61912)\n[PASS] test_NoTierInsideAFlashLoan() (gas: 687636)\n[PASS] test_NobodyElseApprovesJobs() (gas: 435635)\n[PASS] test_OneRevealPerFren() (gas: 717718)\n[PASS] test_OnlyHoldersRecycle() (gas: 1593666)\n[PASS] test_OnlyRealFrens() (gas: 743319)\n[PASS] test_OnlyTheOwnerSetsTheValidator() (gas: 1063173)\n[PASS] test_OwnerAdjustsTheArtUntilFrozen() (gas: 1555030)\n[PASS] test_PairRules() (gas: 875556)\n[PASS] test_PartsOnlyGoForward() (gas: 1035497)\n[PASS] test_PendingEthCountsInTheMintPrice() (gas: 2181876)\n[PASS] test_PriceRisesWithMints() (gas: 38125610)\n[PASS] test_PriceTableIsTheCurve() (gas: 36977950)\n[PASS] test_QuoteCountsUnsweptImd() (gas: 3216110)\n[PASS] test_RareTraitsNeedTheTier() (gas: 1365734)\n[PASS] test_RecyclePaysTheFloorAndKeepsIt() (gas: 2915603)\n[PASS] test_RecyclePaysTheWaitingImdToo() (gas: 2749360)\n[PASS] test_ReleaseLapsedJobAfterTheReveal() (gas: 2142099)\n[PASS] test_RequestMintFor() (gas: 1084060)\n[PASS] test_ReserveOnlyLeavesThroughRecycle() (gas: 2838128)\n[PASS] test_RevealFollowsTheFren() (gas: 749220)\n[PASS] test_RevealInParts() (gas: 9747514)\nLogs:\n  gas, minting 69: 2117887\n  gas, revealing 30: 3092845\n\n[PASS] test_RevealNeedsTheRelayersVoucher() (gas: 803651)\n[PASS] test_RevealReleasesALapsedPayment() (gas: 919103)\n[PASS] test_RevealShowsTheFren() (gas: 1192018)\n[PASS] test_RevealsWhenever() (gas: 661560)\n[PASS] test_RoyaltiesPayTheFloor() (gas: 1724930)\n[PASS] test_RoyaltyBpsIsCappedAndOwnerOnly() (gas: 109412)\n[PASS] test_RulesAreFrozenOnceSealed() (gas: 69580)\n[PASS] test_SealNeedsEveryTraitAndTheSupply() (gas: 10518169)\n[PASS] test_SetMaxMint() (gas: 178238)\n[PASS] test_ShortSwapReverts() (gas: 1046881)\n[PASS] test_SwapperMayPullOnlyTheBuy() (gas: 957490)\n[PASS] test_TheOwnerKeepsTheCollection_theGovernorTheMechanics() (gas: 488664)\n[PASS] test_TierCountsWhatIsLeftAfterPaying() (gas: 920004)\n[PASS] test_TierIsTheBestOfTheBag() (gas: 633505)\n[PASS] test_TierIsTheMints() (gas: 1350710)\n[PASS] test_TierThresholdsOwnerOnlyAndOrdered() (gas: 154928)\n[PASS] test_TradesGoPastTheValidator() (gas: 1346243)\n[PASS] test_TraitCapsHold() (gas: 25778206)\n[PASS] test_TreasuryRoundTripCostsTheFloor() (gas: 3295585)\n[PASS] test_UnneededRetryFeedsTheFloor() (gas: 1026116)\n[PASS] test_UnrevealedFrenSellsAtTheFloor() (gas: 1022775)\n[PASS] test_UnrevealedFrensShareTheFloor() (gas: 1466205)\n[PASS] test_UnsweptImdCountsInTheTreasuryPrice() (gas: 3367000)\n[PASS] test_UnusedJobFeedsTheFloor() (gas: 696981)\n[PASS] test_VouchersExpire() (gas: 472492)\nSuite result: ok. 86 passed; 0 failed; 0 skipped; finished in 59.51ms (221.60ms CPU time)\n\nRan 16 tests for test/FrensLaunchAdaptation.t.sol:FrensLaunchAdaptationTest\n[PASS] test_BootstrapThenOpenAndResume() (gas: 2315891)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\n[PASS] test_FirstFrensBeforeFeesPreventsPublicFloorCapture() (gas: 3435830)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_FirstFrensRequiresPausedBuysAndNonzeroCount() (gas: 1785691)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Fix_SpentNonceIsRefused() (gas: 2311018)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeRefuseAnOwnerlessFloor() (gas: 1828783)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeRejectChangedWiring() (gas: 2680591)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_OpenAndResumeWarnWhenAverageLags() (gas: 4784847)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  WARNING: the swapper's average is more than 2x off the pool's price\n  WARNING: the swapper's average is more than 2x off the pool's price\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\n[PASS] test_PlacedRejectsMissingOrWrongModules() (gas: 126575)\n[PASS] test_PlacedRejectsWrongCollectionAndForeignDependencies() (gas: 65561)\n[PASS] test_PlacedReportsTheLaunchModules() (gas: 51824)\n[PASS] test_ResumeRequiresDistributorAndActualSwapperExemption() (gas: 2164855)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Risk_FirstMintCapturesExistingFloorIfRunbookIsBypassed() (gas: 3054450)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_Risk_LaunchBlockPushSeedsSwapperAndSetupRejectsAfterPriceReturns() (gas: 2572174)\n[PASS] test_Risk_UnsupportedRoyaltyTokenHasNoFloorRoute() (gas: 3781565)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_SetupPausesBeforeFirstMintEvenWhenAlreadyDistributor() (gas: 1815518)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n\n[PASS] test_SetupUsesTheLaunchsReplacementSwapper() (gas: 6981779)\nLogs:\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 set up, sealed, drawn by 0x1d1499e622D69689cdf9004d05Ec547d650Ff211\n  floor buys paused: firstFrens(), then the timelock batch, then resume()\n  frens 0x6900d042460d6bdbe68CE994F4dE36706797CCd4 floor buys resumed\n\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 65.21ms (14.55ms CPU time)\n\nRan 6 tests for test/frens/FrenSwapperAverage.t.sol:FrenSwapperAverageTest\n[PASS] test_AFullBuyMovesItOneStep() (gas: 103606)\n[PASS] test_ASmallBuyMovesItInProportion() (gas: 52842)\n[PASS] test_DustMovesItNextToNothing() (gas: 7606082)\n[PASS] test_FloorRateIsTheLowerReading() (gas: 17282)\n[PASS] test_MovesDownToo() (gas: 52658)\n[PASS] test_OnceABlock() (gas: 91443)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 81.34ms (3.02ms CPU time)\n\nRan 12 tests for test/FrensArtFraming.t.sol:FrensArtFramingTest\n[PASS] testFuzz_EntryPastTheIndexIsMissing(uint256) (runs: 256, μ: 13971, ~: 13943)\nLogs:\n  Bound result 3237800000000000000\n\n[PASS] testFuzz_UnknownBackgroundIsMissing(uint256) (runs: 256, μ: 35612, ~: 35592)\nLogs:\n  Bound result 52\n\n[PASS] test_ATamperedChunkDrawsNothing() (gas: 90530)\n[PASS] test_AttributesDoNotValidate() (gas: 369298)\n[PASS] test_ChunksAreFramedPushData() (gas: 2731712)\n[PASS] test_EveryValueHasAName() (gas: 15679313)\n[PASS] test_IndexHashesAreTheChunks() (gas: 788314)\n[PASS] test_IndexPointsInsideTheChunks() (gas: 525933)\n[PASS] test_LaunchLayersAreWellFormed() (gas: 46186857)\n[PASS] test_NoSuchFrenIsMissing() (gas: 4375022)\n[PASS] test_PalettesFitTogether() (gas: 13989817)\n[PASS] test_WithoutTheSwarmNothingDraws() (gas: 5947823)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 81.43ms (259.04ms CPU time)\n\nRan 19 tests for test/FrensLaunchFailures.t.sol:FrensLaunchFailuresTest\n[PASS] testFuzz_CheckKnowsAFren(uint24,uint8) (runs: 512, μ: 1940731, ~: 1907931)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_OtherSaltsLandElsewhere(bytes32) (runs: 256, μ: 34990, ~: 34990)\n[PASS] testFuzz_QuoteIsTheCurveBeforeAnyoneHolds(uint256) (runs: 512, μ: 310738, ~: 294461)\nLogs:\n  Bound result 52\n\n[PASS] test_AnotherOwnerMovesTheFrens() (gas: 34259)\n[PASS] test_CollectionRefusesAShortPriceTable() (gas: 3059925)\n[PASS] test_CollectionWaitsForTheTeamWallet() (gas: 5941480)\n[PASS] test_EveryPriceIsTheTables() (gas: 27580866)\n[PASS] test_FreshChainLaunchesAreIndependent() (gas: 1351085)\n[PASS] test_LaunchContractsTakeNoEth() (gas: 687657)\n[PASS] test_ModulesFailOverCorruptedFrens() (gas: 97101)\n[PASS] test_ModulesNeedTheCollection() (gas: 106270)\n[PASS] test_NoLaunchActorHoldsARole() (gas: 4580578)\n[PASS] test_NoPricePastTheTable() (gas: 41219)\n[PASS] test_OtherSaltSchemeFailsTheLaunch() (gas: 140656)\n[PASS] test_RefusingDeployerFailsTheLaunch() (gas: 103369)\n[PASS] test_ScriptRefusesAnUnplacedChain() (gas: 14786192)\n[PASS] test_SecondLaunchReusesWhatIsPlaced() (gas: 1313404)\n[PASS] test_SilentDeployerFailsTheLaunch() (gas: 93234)\n[PASS] test_WrongArtFailsTheArtLaunch() (gas: 588910)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 83.79ms (245.22ms CPU time)\n\nRan 3 tests for test/FrensLaunchReview.t.sol:FrensFactoryReviewTest\n[PASS] test_Audit_ConstructorLeavesTheDocumentedTeamSetup() (gas: 25294534)\n[PASS] test_FreshChainCallsOnlyContractsCreatedByTheLaunch() (gas: 28696930)\n[PASS] test_ProtectedFactoryDeploysBothLaunchesInOrder() (gas: 25493481)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 87.70ms (12.63ms CPU time)\n\nRan 13 tests for test/FrensPlacement.t.sol:FrensPlacementTest\n[PASS] test_Attributes() (gas: 19594305)\n[PASS] test_CodeIsWhatTheSourcesBuild() (gas: 89696)\n[PASS] test_DeploysOnAFreshChain() (gas: 51811985)\n[PASS] test_EachLaunchFitsOneTransaction() (gas: 105863223)\nLogs:\n  the collection launch: gas, all in: 12451232\n  the art launch: gas, all in: 13605058\n\n[PASS] test_LandsWhereThePlanSays() (gas: 51923654)\n[PASS] test_NewArtReadsBackExactly() (gas: 14173584)\n[PASS] test_PassesTheAdmissionScan() (gas: 88302675)\n[PASS] test_PlanFollowsFromTheCode() (gas: 80750)\n[PASS] test_PlanMatchesTheScript() (gas: 14314057)\n[PASS] test_PriceTableIsTheCurve() (gas: 53105398)\n[PASS] test_RefusesOtherArt() (gas: 12971045)\n[PASS] test_SameAddressesWhoeverRunsIt() (gas: 102774643)\n[PASS] test_TakesWhatSomeonePlacedFirst() (gas: 52491481)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 94.65ms (208.15ms CPU time)\n\nRan 1 test for test/FrensSplitGas.t.sol:FrensSplitGasTest\n[PASS] test_CollectionFitsButLegacyFourContractBundleExceedsCap() (gas: 101750011)\nLogs:\n  legacy four-contract launch: 23877683\n  two-contract collection launch: 13494921\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 121.16ms (121.01ms CPU time)\n\nRan 1 test for test/FrensPlacedInvariants.t.sol:FrensPlacedInvariantsTest\n[PASS]\nFrensPlacedInvariantsTest invariants:\n[PASS] invariant_CapsHold\n[PASS] invariant_FloorIsAShareOfTheWorld\n[PASS] invariant_FrensAreConserved\n[PASS] invariant_ImdIsTheBooks\n[PASS] invariant_ReserveIsExactlyWhatIsHere\n FrensPlacedInvariantsTest invariants (runs: 32, calls: 1920, reverts: 3)\n\n╭--------------------+-----------------+-------+---------+----------╮\n| Contract           | Selector        | Calls | Reverts | Discards |\n+===================================================================+\n| PlacedFrensHandler | approveJob      | 130   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyFloor        | 106   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyTreasury     | 124   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donate6900      | 121   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donateImd       | 126   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | feeEth          | 123   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mint            | 242   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mintThenRecycle | 106   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | recycle         | 114   | 3       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | retryJob        | 112   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | reveal          | 126   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setParams       | 116   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setRate         | 126   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | transfer        | 126   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | warp            | 122   | 0       | 0        |\n╰--------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 11884\n  Bound result 927403375\n  Bound result 7781167491911163\n  Bound result 72\n  Bound result 709\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 22022\n  Bound result 6\n  Bound result 1627\n  Bound result 2595\n  Bound result 1\n  Bound result 564412541\n  Bound result 2\n  Bound result 608\n  Bound result 4281\n  Bound result 6226\n  Bound result 291054\n  Bound result 1\n  Bound result 8781\n  Bound result 19\n  Bound result 20\n  Bound result 4\n  Bound result 7834\n  Bound result 2\n  Bound result 2\n  Bound result 435\n  Bound result 3533\n  Bound result 22\n  Bound result 23332\n  Bound result 564\n  Bound result 1\n  Bound result 15\n  Bound result 0\n  Bound result 26880\n  Bound result 8220\n  Bound result 15584\n  Bound result 23331\n  Bound result 1\n  Bound result 11738\n  Bound result 1778\n  Bound result 11574\n  Bound result 1\n  Bound result 0\n  Bound result 300000\n  Bound result 499999999999999861\n  Bound result 640\n  Bound result 983\n  Bound result 5352\n  Bound result 60\n  Bound result 4712\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.77s (2.77s CPU time)\n\nRan 19 test suites in 2.77s (3.54s CPU time): 196 tests passed, 0 failed, 5 skipped (201 total tests)\n","passed":true},{"durationMs":107,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"FrenMinter.buyImd(uint256)\",\"FrenMinter.mintWithEth(uint8,uint256)\",\"FrenMinter.quoteEth(uint8)\",\"FrenMinter.quoteFloor(uint256)\",\"FrenMinter.quoteFloorEth(uint256)\",\"FrenMinter.unlockCallback(bytes)\",\"FrenSwapper.ethToImd6900(uint256,address)\",\"FrenSwapper.imdToImd6900(uint256,uint256,address)\",\"FrenSwapper.unlockCallback(bytes)\",\"FrenWorkerGate.cancelOwnershipHandover()\",\"FrenWorkerGate.claim(uint256[],address)\",\"FrenWorkerGate.claimWl(uint256,bytes32[],address)\",\"FrenWorkerGate.completeOwnershipHandover(address)\",\"FrenWorkerGate.openPublic()\",\"FrenWorkerGate.renounceOwnership()\",\"FrenWorkerGate.requestOwnershipHandover()\",\"FrenWorkerGate.setWlRoot(bytes32)\",\"FrenWorkerGate.spend(address,uint256)\",\"FrenWorkerGate.transferOwnership(address)\",\"IMD6900Frens.addPairRule((uint8,uint8,uint8,uint8,uint8))\",\"IMD6900Frens.approve(address,uint256)\",\"IMD6900Frens.approveJob(uint256,uint256,uint256,(string,bytes32,string,bytes32,bytes32,string,uint256))\",\"IMD6900Frens.buyFloor(uint256)\",\"IMD6900Frens.buyFloorWithEth(uint256,uint256)\",\"IMD6900Frens.buyTreasury(uint256,uint256,uint256)\",\"IMD6900Frens.cancelOwnershipHandover()\",\"IMD6900Frens.completeOwnershipHandover(address)\",\"IMD6900Frens.freezeArt()\",\"IMD6900Frens.receive()\",\"IMD6900Frens.recycle(uint256)\",\"IMD6900Frens.releaseLapsedJob(uint256)\",\"IMD6900Frens.renounceOwnership()\",\"IMD6900Frens.requestMint(uint8,uint256)\",\"IMD6900Frens.requestMintFor(address,uint8,uint256)\",\"IMD6900Frens.requestOwnershipHandover()\",\"IMD6900Frens.retryJob(uint256)\",\"IMD6900Frens.reveal(uint256,uint24[],string,bytes32,uint256,bytes,uint256)\",\"IMD6900Frens.safeTransferFrom(address,address,uint256)\",\"IMD6900Frens.safeTransferFrom(address,address,uint256,bytes)\",\"IMD6900Frens.sealTraits()\",\"IMD6900Frens.setApprovalForAll(address,bool)\",\"IMD6900Frens.setGovernor(address)\",\"IMD6900Frens.setMaxMint(uint8[4])\",\"IMD6900Frens.setMintOpen(bool)\",\"IMD6900Frens.setModules(address,address)\",\"IMD6900Frens.setParams(uint256,uint256,uint256)\",\"IMD6900Frens.setRenderer(address)\",\"IMD6900Frens.setRoles(address,address,address)\",\"IMD6900Frens.setRoyalty(uint96)\",\"IMD6900Frens.setTiers(uint256[3],uint256[3],uint256[3])\",\"IMD6900Frens.setTraitRules(uint8,uint16[],uint8[])\",\"IMD6900Frens.setTransferValidator(address)\",\"IMD6900Frens.transferFrom(address,address,uint256)\",\"IMD6900Frens.transferOwnership(address)\",\"IMD6900Frens.unwrapWeth()\"],\"files\":{\".gitignore\":4,\"ADAPTATION.md\":160,\"README.md\":218,\"foundry.toml\":22,\"launch.json\":16,\"script/art/chunks.py\":165,\"script/art/data/bgpal/cleanlab_blue.bin\":1,\"script/art/data/bgpal/cleanlab_green.bin\":1,\"script/art/data/bgpal/cleanlab_red.bin\":1,\"script/art/data/bgpal/messylab_blue.bin\":1,\"script/art/data/bgpal/messylab_green.bin\":1,\"script/art/data/bgpal/messylab_red.bin\":1,\"script/art/data/bgpal/tubeblue.bin\":1,\"script/art/data/bgpal/tubegreen.bin\":2,\"script/art/data/bgpal/tubered.bin\":1,\"script/art/data/bgpal/tubeyellow.bin\":1,\"script/art/data/bgpal/wireframe_green.bin\":2,\"script/art/data/bgpal/wireframe_red.bin\":1,\"script/art/data/expected.json\":53,\"script/art/data/layers/bg0.bin\":9,\"script/art/data/layers/bg1.bin\":9,\"script/art/data/layers/bg10.bin\":32,\"script/art/data/layers/bg11.bin\":32,\"script/art/data/layers/bg2.bin\":9,\"script/art/data/layers/bg3.bin\":15,\"script/art/data/layers/bg4.bin\":15,\"script/art/data/layers/bg5.bin\":15,\"script/art/data/layers/bg6.bin\":26,\"script/art/data/layers/bg7.bin\":26,\"script/art/data/layers/bg8.bin\":26,\"script/art/data/layers/bg9.bin\":26,\"script/art/data/layers/coat.bin\":20,\"script/art/data/layers/item5.bin\":2,\"script/art/data/manifest.json\":500,\"script/art/data/shared.bin\":4,\"script/art/data/tables.bin\":2,\"script/art/swarm-v2.json\":374,\"script/frens/DeployFrens.s.sol\":254,\"script/frens/FrensTimelockBatch.s.sol\":105,\"script/frens/price/prices-swarm.bin\":6,\"script/frens/price/prices.bin\":6,\"script/frens/price/prices.json\":26,\"script/frens/price/prices.py\":55,\"script/frens/price/prices_sol.py\":33,\"script/placement/addresses.json\":7,\"script/placement/gen.py\":146,\"src/FrensCode.sol\":15,\"src/FrensPlacement.sol\":129,\"src/FrensPlan.sol\":37,\"src/art/WorkerArt.sol\":671,\"src/art/WorkerArtIndex.sol\":56,\"src/frens/FrenMinter.sol\":194,\"src/frens/FrenPrices.sol\":122,\"src/frens/FrenSwapper.sol\":276,\"src/frens/FrenWorkerGate.sol\":137,\"src/frens/IMD6900Frens.sol\":1083,\"src/frens/WorkerFrensRenderer.sol\":377,\"test/FrensArtFraming.t.sol\":290,\"test/FrensLaunchAdaptation.t.sol\":331,\"test/FrensLaunchFailures.t.sol\":526,\"test/FrensLaunchReview.t.sol\":449,\"test/FrensPlacedInvariants.t.sol\":634,\"test/FrensPlacement.t.sol\":818,\"test/FrensResidualRisks.t.sol\":210,\"test/FrensSplitGas.t.sol\":68,\"test/frens/DataWriter.sol\":21,\"test/frens/FrenMinter.fork.t.sol\":189,\"test/frens/FrenSwapper.fork.t.sol\":134,\"test/frens/FrenSwapperAverage.t.sol\":77,\"test/frens/FrenWorkerGate.t.sol\":350,\"test/frens/FrensRules.sol\":75,\"test/frens/IMD6900Frens.t.sol\":1763,\"test/frens/IMD6900FrensPermit2.fork.t.sol\":130,\"test/frens/IMD6900FrensValidator.fork.t.sol\":146,\"test/frens/IMD6900FrensValidator.t.sol\":231,\"test/test_collection_manifest.py\":56},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"f8fd26ea395ddcca7794e77c101f5b497e6431195b0fbf28ef515219aea217e7","verifiedTreeHash":"e3d58c7cd9063bb0613a8c798ba7d6e7d8445b7c","verifierVersion":"0.1.0+ad90ce4c"}]}