{"assessments":[],"deployments":[{"attestationHash":"fd88961c8b8d72e8e7210a7c994c6061e735f591b335b7bd029968913b116794","chainId":1,"contracts":[{"address":"0x1e3ac46d3f6b70f6cc6e6e84e7eed68b2b030758","blockNumber":26149508,"name":"WorkerArt2","role":"other","txHash":"0x0b0b1a0a85a4888bf3cd3aabe0a26cf0a0fd86c5fd9e143d30c8f87805e081ca"},{"address":"0x048f317cce4c0170d8974011870779bb12a7ef9f","blockNumber":26149508,"name":"WorkerArt1","role":"other","txHash":"0x0b0b1a0a85a4888bf3cd3aabe0a26cf0a0fd86c5fd9e143d30c8f87805e081ca"},{"address":"0x0a2e5e0c1d00fe63ab4e391c052a023cc7a16292","blockNumber":26149508,"name":"WorkerFrensRenderer","role":"other","txHash":"0x0b0b1a0a85a4888bf3cd3aabe0a26cf0a0fd86c5fd9e143d30c8f87805e081ca"}],"id":"d70912ef-442c-4917-b8fd-2b581cdab2a0","manifestHash":"7a464c7951e51aa08c5eb1773f3e6e4e35e05845cb9b90f4e2bc92e73119c044","status":"live"}],"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":"3dde4444-a257-47fc-aa52-62677afe414f","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"37efd4a24e19086bb6956ff9ce2d46bf32084c875368e18c73513b4febaea74f","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":"4a5e2cb466e78848ca660e4af392d80a32453773782eb9bc3692224b40a1b03c","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":"6fb8c8778fe2f3c885d764fab5cb8c219cd4ae16975a420862ce1d1d229f4a82","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":"f16adb569505eb837d7bbd92ad03757131d9adbdbe428240e459da909b45eeb0","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":"7a8c91121ee27176104a7b57cd5e2572e3b9fe089a65dda3ac976802c285819e","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":"0c86c3d29261c187a8d318f7086130ea20c30d4fec1a47ac475bd225a7384dd3","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":"703ad07c5dfb60510bf787860c3b3e817ff992d36f774f8fe0cc8bcdc6a1e8cd","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":"f5a419f5c98c2492ca7d97a38b718e29f4f75c07fe0b3db58c1283c9108bd19c","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"484ebb51b4eb9eb59d50f8ceedd220aa4d2b801cf501c6409184b9bb19de3b7c","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 Worker Frens' art on Ethereum, exactly as it is in the repository: the ART launch, three contracts in this order: WorkerArt1 and WorkerArt2 (src/art/WorkerArt.sol, no constructor arguments: data contracts whose code is the new art, framed so every byte is PUSH data), then WorkerFrensRenderer (src/frens/WorkerFrensRenderer.sol) with two arguments, $contract:WorkerArt1 and $contract:WorkerArt2. The renderer draws each fren on chain from those two chunks and the IMD swarm's FrenArtChunk1..7 already on Ethereum, checking every chunk's code hash on every read; its constructor checks the two new chunks' code hashes (BadArt) and calls nothing, so it deploys on a fresh chain too. None of the three has an owner or any role. About 13.6M gas in one transaction, all in. The collection (PlaceFrens, PlaceModules) is a separate launch; the team wallet points it at this renderer (script/frens/DeployFrens.s.sol setup(), RENDERER). FrensPlacementForkTest (MAINNET_RPC_URL) checks the renderer draws exactly the art kit's reference renders byte for byte (script/art/data/expected.json). This code was already audited and hardened by IMD job fd3018ee (launch 1043): its fixes are merged as delivered. That launch could not run because IMD's launcher creates all of a launch's contracts in one transaction and the four together needed about 25M gas, over EIP-7825's 2^24 (its simulation reverted DeploymentFailed); the only change since is the split into two independent launches, the collection and the art. Do not change src/frens/, src/art/, src/FrensCode.sol, src/FrensPlan.sol, the salts or foundry.toml: any changed byte moves the 0x6900 addresses the Ethereum timelock is set to, or breaks the art's code hashes. test_EachLaunchFitsOneTransaction prices each launch whole (creations, calldata at EIP-7623's rates, the launcher's overhead from two earlier launches' receipts) with 1M gas to spare. Everything builds offline (lib/ is vendored). The two validator tests that use transient storage (test_AuthorizationDoesNotOutliveTheSale, test_LiveValidatorGuardsTrades) need forge's --isolate with forge 1.4; they pass as is with 1.8.","parentJobId":null,"planHash":"7e25467372f840bb5912e47bed2cd4866910659b1820f22f6720caaaac5b9f71","previousHash":"6fe47f4a55fe3abdb8ebe0f3f8f31c462cd644e1a326350da4a0e18b5b42d3fc","projectId":"3dde4444-a257-47fc-aa52-62677afe414f","publication":{"commit":"2c599add059198c1794d4786c08a8f4cc3dcc89d","deliveredAt":"2026-10-08T18:31:01.749Z","repoUrl":"https://github.com/identity-md-launches/launch-1067-workerart1-workerart2-workerfrensrendere"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[],"site":null,"snapshotHash":"967f81295eb980259e56f2506f83536cef1d9d2daa09f6029a593cb2226fa979","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"1731fbfe0c4574fb","findings":[{"citation":"resolved","description":"Not a contract defect; a stale assumption in the fork suite the launch brief points reviewers at (FrensPlacementForkTest). The test schedules FrensTimelockBatch.batch(FRENS_AT, SWAPPER_AT, true, false) with salt keccak256(\"worker-frens-open-1\") on the Ethereum timelock 0xBd3ed9F4AbD9946cA6F59C8F13A3EbebDE1EA29D and expects the operation to be Unset. On mainnet that exact operation (id 0x6ebf60b5d6a49cec8df7fdb89ce96061ddb4316aaba0dc170007c223634c1ffa) has already been scheduled by the team: getTimestamp(id) returns 1791649787 (pending, about 47 hours after the fork block's timestamp 1791479447), so OpenZeppelin's TimelockController reverts with TimelockUnexpectedOperationState(id, 0x…01) (selector 0x5ead8eb5, expected state bit 1 = Unset). The other five fork tests, including test_fork_DrawsLikeTheReference (the art launch's byte-for-byte render check), pass against the same fork. The art launch itself is unaffected: WorkerArt1, WorkerArt2 and WorkerFrensRenderer never touch the timelock. The author should either skip the scheduleBatch when tl.getTimestamp(id) != 0 and warp to that timestamp instead, or pin the fork to a block before the schedule transaction, so the suite stays green on the live chain the launch is checked against.","line":679,"path":"test/FrensPlacement.t.sol","reproduction":"MAINNET_RPC_URL=https://ethereum-rpc.publicnode.com forge test --match-contract FrensPlacementForkTest (fork block 26149055 or later). Expected: 6 passed. Actual: test_fork_WorkersFirstThenPublic fails at the scheduleBatch call with custom error 0x5ead8eb5 and data (0x6ebf60b5d6a49cec8df7fdb89ce96061ddb4316aaba0dc170007c223634c1ffa, 1). Confirming the state directly: ITimelockController(0xBd3ed9F4AbD9946cA6F59C8F13A3EbebDE1EA29D).getTimestamp(hashOperationBatch(targets, values, datas, bytes32(0), keccak256(\"worker-frens-open-1\"))) == 1791649787 on the same fork, where targets/values/datas are FrensTimelockBatch.batch(0x69007Ce82E0BF7981780585afF7c597415903547, 0x6900453deFAc8Bb12eabdcf57CCC5a14E7628AeE, true, false). Before the team's schedule transaction the call returned 0 and the test passed.","severity":"info","snippet":"        tl.scheduleBatch(targets, values, datas, bytes32(0), salt, 48 hours);","title":"test_fork_WorkersFirstThenPublic now fails on live mainnet: the timelock batch it schedules is already scheduled on chain"}],"hash":"0c86c3d29261c187a8d318f7086130ea20c30d4fec1a47ac475bd225a7384dd3","nodeId":"dcf8433a-0527-4fa6-b1b9-af444ead4634","outcome":"completed","summary":"The review is complete and `.imd-findings.json` is written and validated: all 57 listed entry points have a coverage row (69 rows total, including the art launch's own entry points and the gas-model invariant), the one finding's snippet matches the tree, and no tracked file was changed.\n\n**Outcome: no math, boundary or numerical-gap defect in the art launch.** One info-level finding, outside the art contracts, about a fork test that has gone stale against live mainnet.\n\n**What I verified in the assigned area**\n\n- **Byte-for-byte correctness on mainnet.** With a public RPC, FrensPlacementForkTest's render check passed: all 7 revealed and 3 pending references in expected.json match the renderer's output exactly, reading the swarm's chunks on chain plus the two new ones.\n- **Every art entry, not just the sampled ones.** A scratch fork test walked all 82 index entries (swarm chunks 0 to 3, launch chunks 7 and 8): each layer is a well-formed 4-byte header plus runs covering exactly its width, each palette is BGR0-aligned, and all 39 faces, 3 hats and 16 items draw. Swarm chunks 3 and 6 are framed on chain, matching the index's FRAMED bits.\n- **Edge arithmetic in the renderer.** The framed read's frame/offset math, the palette layout (the largest own palette ends at exactly byte 1024), the background window (offsets up to 36 over 120-wide art always cover the 84 canvas), signed clipping in the drawing loop, the pending-combo bit packing (max bit 22, so the uint24 cast never truncates), and the BMP header fields all check out. The custom-error selector hardcoded in the assembly revert is the right one.\n- **Gas boundaries.** The launch model prices the art launch at about 13.6M gas with its overhead term sitting above both real receipts, leaving about 3.2M under the 2^24 cap. Worst-case revealed render is about 4.7M gas; the pending card's worst case is about 14.9M, under the cap and under common eth_call limits.\n- **Collection math (secondary pass).** I read all of IMD6900Frens, FrenMinter, FrenSwapper and FrenWorkerGate for rounding, underflow, scale mixing and casts. The pair hook's fee reads 690 on mainnet, confirming the swapper's bips assumption. Nothing reportable.\n\n**The one finding (info).** test_fork_WorkersFirstThenPublic now fails on live mainnet because the team has already scheduled the exact timelock batch the test schedules. The timelock reports that operation as pending (ready at timestamp 1791649787), so the test's scheduleBatch reverts with an \"already scheduled\" state error. It is a stale test assumption, not a contract defect, and the other five fork tests pass. The suggested fix is to skip scheduling when the operation already exists and warp to its ready time instead.\n\nThe full offline suite passes (173 tests). The scratch tests under test/scratch/ were working material only and carry no proof, since there are no critical or high findings.","treeHash":null,"usage":{"cachedInputTokens":2580541,"inputTokens":610,"model":"claude-fable-5-1","outputTokens":56254,"runtime":"claude","turns":47,"wallClockMs":991632}},{"artifacts":[],"attempt":1,"bundleHash":"48c0a99a122e3bf26276dc985ee019d5f000c398d2e055b8bda3cd69d26451f6","device":"2eac007f7332b54c","findings":[],"hash":"37efd4a24e19086bb6956ff9ce2d46bf32084c875368e18c73513b4febaea74f","nodeId":"88f38de2-4462-4946-8073-a354d8e63a21","outcome":"completed","summary":"Corrected `launch.json` to deploy WorkerArt1, WorkerArt2, then WorkerFrensRenderer. Added manifest regression tests and updated `ADAPTATION.md`. Contract sources and salts remain unchanged.\n\nValidation:\n- Offline build passed.\n- Tests: 174 passed, 0 failed, 5 skipped.\n- Manifest checks: 2 passed.\n- Pinned mainnet fork: 6 passed.\n- Art launch: 13,605,058 gas.\n\nThe latest-block fork has one collection-test failure because its timelock batch is already scheduled; documented in `ADAPTATION.md`.","treeHash":"54e12cfbcf21dd2ff7c0021da42672b13dbd7783","usage":{"cachedInputTokens":2784256,"inputTokens":115197,"model":"gpt-6-astra","outputTokens":18522,"runtime":"codex","turns":8,"wallClockMs":813365}},{"artifacts":[],"attempt":1,"bundleHash":"20cf3d490a01c4c1050699f185022c8bc1b375f0aaae33a3ed97c077fd507262","device":"a12c1a6f9430f757","findings":[],"hash":"484ebb51b4eb9eb59d50f8ceedd220aa4d2b801cf501c6409184b9bb19de3b7c","nodeId":"c9d49294-0c2e-4346-b0bb-3f7924266ac2","outcome":"completed","summary":"Added offline reference-render checks, failure-path fuzzing, and random-call ETH/immutability invariants. Only `test/` changed.\n\nVerification passed:\n\n- `forge build --offline`\n- `forge test --offline`: **186 passed, 0 failed, 5 skipped**\n\nFork tests skipped because `MAINNET_RPC_URL` is unset; all ten reference renders passed offline.","treeHash":"0ddc491c0fdf247fd1b1752930b9ad452a0a406f","usage":{"cachedInputTokens":5352064,"inputTokens":132609,"model":"gpt-6-astra","outputTokens":26874,"runtime":"codex","turns":13,"wallClockMs":1675638}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"433c37ef2c9c708d","findings":[{"citation":"resolved","description":"The art launch's handoff to the collection is the team wallet running setup() with RENDERER set to the launch's confirmed WorkerFrensRenderer address. The launch output lists three new addresses (WorkerArt1, WorkerArt2, WorkerFrensRenderer); the script's only guard is that RENDERER has code, which WorkerArt1, WorkerArt2, PlaceModules, the swapper or any other contract also satisfies. IMD6900Frens.setRenderer (src/frens/IMD6900Frens.sol:1005) stores whatever it is given and IMD6900Frens.tokenURI (line 345) then calls pendingURI/tokenURI on it: an art chunk is a STOP byte and returns no data, so abi-decoding the string reverts and every token's metadata is unavailable (marketplaces show nothing). The error is recoverable with another setRenderer only while artFrozen is false; freezeArt() (line 1012) does not check that a working renderer is set (not even that renderer != address(0)), so the same mistake followed by freezeArt is permanent and the collection contract cannot be changed (its bytes fix the 0x6900 address). Flow-gap seam: execution (setup runs clean) x periphery (a coded address that is not a renderer) x first principles (the collection must draw its frens on chain). Minimal fix in the script, which is not address-sensitive: before broadcasting, verify the target is the launch's renderer, e.g. require(WorkerFrensRenderer(renderer).art1().codehash == <index hash 7> && ...art2().codehash == <index hash 8>) or staticcall renderer.pendingURI(1) and require a non-empty string; optionally refuse RENDERER equal to any of the collection launch's addresses.","line":66,"path":"script/frens/DeployFrens.s.sol","reproduction":"Mainnet fork (reproduced at block 26,149,000 with test/scratch/ArtEconomics2.fork.t.sol test_fork_SetupAcceptsAnyCodedRenderer): deploy PlaceFrens, PlaceModules(PlaceFrens), WorkerArt1, WorkerArt2, WorkerFrensRenderer(art1, art2); set MODULES=<PlaceModules> and RENDERER=<WorkerArt1 address>; run DeployFrens.setup(). Expected: setup refuses (RENDERER is not a renderer). Actual: setup succeeds, frens.renderer() == WorkerArt1, the trait rules are sealed. Then as the governor requestMintFor(0xBEEF, 1, max): frens.tokenURI(1) reverts (empty return data from the STOP contract fails string decoding). As the owner call freezeArt(): setRenderer(<real renderer>) now reverts Unauthorized, so the collection's metadata is broken for good. The same sequence with RENDERER=<WorkerFrensRenderer> renders fine (12.6M gas pending, 4.1M revealed).","severity":"low","snippet":"        require(renderer.code.length != 0, \"no renderer at RENDERER\");","title":"setup() accepts any contract with code as RENDERER: pointing the collection at WorkerArt1 (or any non-renderer) makes every tokenURI revert, irreversibly once freezeArt is called"}],"hash":"4a5e2cb466e78848ca660e4af392d80a32453773782eb9bc3692224b40a1b03c","nodeId":"44e0e913-2e1a-4d43-9b81-7791ba632fae","outcome":"completed","summary":"The review is complete and the findings file is written. No source files were changed. The findings file and the scratch tests are the only additions, and both are git-ignored by the harness.\n\n**Outcome.** The art launch is economically inert and I found no defect in its three contracts. One low finding sits in the handoff script.\n\n**What I checked, against the Economic Security, Invariant and Flow Gap guides:**\n- **Dependencies.** The renderer's only external dependency is seven swarm data contracts on mainnet. Their code hashes match the index at the current head, and post-Cancun nothing can remove existing code, so the dependency is permanent. Every read checks the hash, and the constructor makes no call, so it deploys on a fresh chain.\n- **Invariants.** On a mainnet fork, all 82 art entries read back and are well-formed for the drawing loop, and all 39 faces, 2 hats, 15 items and 12 backgrounds draw. The repository's fork suite reproduced the 10 byte-for-byte reference renders. All 12 backgrounds are 120x120, so the seed window never exposes an unfilled pixel. The collection's reveal check and the renderer's combo guards enforce identical value ranges, so no revealed token can make the renderer revert.\n- **Gas economics.** The launch prices at 13.6M gas all in, 3.17M under EIP-7825's cap. Through the collection's own `tokenURI`, a pending fren costs 12.6M and a revealed one 4.1M. Both are under the cap, but the pending read uses about 80% of it. I noted that as an open item rather than a finding since I could not reproduce a failure against a specific provider.\n- **Flow gaps.** Either launch may land first; the art launch has no dependency on the collection.\n\n**The finding (low).** The setup script's only check on RENDERER is that the address has code. Pointing it at WorkerArt1, which appears in the same launch output, succeeds and then every `tokenURI` reverts. That is recoverable with another `setRenderer`, but `freezeArt` has no check either, so the same mistake followed by freezing is permanent and the collection's bytes cannot change. Reproduced on a fork with a scratch test. The fix belongs in the script, which is not address-sensitive.\n\n**Coverage record.** All 56 listed entry points have rows, plus 10 rows for the art launch's own functions and invariants. The entry points outside my area are marked as untouched by the renderer rather than traced in depth.\n\nOne existing fork test fails against the live chain because the timelock batch it schedules is already scheduled on mainnet. That matches what the adaptation notes already record.","treeHash":null,"usage":{"cachedInputTokens":2762078,"inputTokens":578,"model":"claude-fable-5-1","outputTokens":42728,"runtime":"claude","turns":47,"wallClockMs":1073994}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"468e82a89b9bfe18","findings":[],"hash":"6fb8c8778fe2f3c885d764fab5cb8c219cd4ae16975a420862ce1d1d229f4a82","nodeId":"4223c9c0-a78f-4b56-9238-851b867d4450","outcome":"completed","summary":"The review is complete. The findings file is written with an empty findings list and 63 coverage rows.\n\n**Result: no defect substantiated in the art launch.** I traced the Execution Trace, Periphery and First Principles checklists against WorkerArt1, WorkerArt2 and WorkerFrensRenderer and could not produce a failing input.\n\nWhat I verified, beyond reading the code:\n\n- **Offline suite** passes as the author states (173 passed, 5 fork suites skipped), and the manifest regression script passes.\n- **Mainnet fork today** reproduces the art kit's reference renders byte for byte, and the renderer's setup path points the collection at the launch's renderer. The seven swarm chunks still hash to the index's values.\n- **A scratch fork test I wrote** (now deleted) covered what the repo's tests do not: all 57 non-background layers, including every one of the 39 faces, are well formed for the run decoder, every swarm index entry fits inside its chunk's code, and no face, hat, item or coat uses a colour index outside the shared palette or the nine slots. That last point is the split-palette design's key invariant; a violation would have made a fren's colours depend on its background.\n- **Decoder and reader safety**: the framed read arithmetic stays inside its buffers for every index entry, a zero-length run ends in `Missing` rather than an infinite loop, off-canvas clipping uses signed compares, and the slot lookup only fires for indices 244 to 252.\n- **Boundary with the collection**: `check()` bounds each trait value to exactly the ranges the renderer draws, so a revealed token can never hit `Missing`, and a revealed seed is forced odd so it never falls back to the pending card.\n- **Launch shape**: three nonpayable, ownerless contracts in dependency order with backward references, within EIP-170 and EIP-3860, clean under the admission scan, and the one-transaction pricing leaves about 3.2M gas under the EIP-7825 cap.\n\nCoverage: the 56 listed entry points all belong to the collection launch. Seven that touch the art boundary (setRenderer, freezeArt, reveal, the trait-rule setters) are traced with reasons. The rest are marked as untouched by the art launch, per the assignment's rule for entry points outside the area. Seven extra rows record the renderer's constructor, the chunk constructors, the reader, the decoder, the palette invariant, the view surface and the manifest.\n\nOne observation that is not a defect: `pendingURI` costs 13.06 to 13.65M gas on the fork, as the brief says, which is fine for an RPC read but rules out any on-chain caller.","treeHash":null,"usage":{"cachedInputTokens":1405235,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":35502,"runtime":"claude","turns":41,"wallClockMs":1008226}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"61b4050710026370","findings":[{"citation":"resolved","description":"The art launch and the collection launch are independent, and the only thing that joins them is the team wallet's setup() run with RENDERER in the environment. setup() checks only that RENDERER has code, then in one broadcast calls setRenderer(renderer), setModules, launchRules and sealTraits. Nothing confirms that the address is a WorkerFrensRenderer at all (e.g. that art1()/art2() answer and their code hashes are WorkerArtIndex's entries 7 and 8, or a dry pendingURI(1)/tokenURI(1, combo, seed) call succeeds). Any contract address pasted by mistake (the collection launch's PlaceModules, a chunk, an old renderer from the aborted launch 1043 on another chain) passes the check. Trust-gap seam (access x asymmetry): the renderer's own constructor refuses wrong art (BadArt), so the launch-side check is strong, but the setup-side check that actually wires the collection to it is weak; the collection's tokenURI then reverts for every token until the owner notices and calls setRenderer by hand. setup() cannot simply be re-run with the right value: its sealTraits() reverts with TraitsAreSealed on a second run, so the recovery path is an ad-hoc setRenderer transaction the scripts do not provide. If freezeArt() were run in between (see the next finding) the wrong renderer would be permanent. Recoverable, operator-only, no funds at risk: low.","line":66,"path":"script/frens/DeployFrens.s.sol","reproduction":"Offline, with the IMD6900 strategy stubbed to isDistributor()=false: pf = new PlaceFrens(); pm = new PlaceModules(pf); set env MODULES=<pm>, RENDERER=<pm> (the collection launch's address instead of the art launch's); call DeployFrens.setup(). Expected: setup refuses an address that is not a WorkerFrensRenderer over WorkerArt1/WorkerArt2. Actual: setup() completes and logs 'set up, sealed, drawn by <pm>'; frens.renderer() == pm; traitsSealed() == true; a staticcall pendingURI(1) on pm fails, so IMD6900Frens.tokenURI(id) reverts for every minted token. A second setup() with the right RENDERER reverts with TraitsAreSealed(). Suggested check in setup(): require(WorkerFrensRenderer(renderer).art1().codehash == <index entry 7> && ...art2().codehash == <entry 8>) or a try/catch dry call of pendingURI(1) before broadcasting; none of src/ changes.","severity":"low","snippet":"        require(renderer.code.length != 0, \"no renderer at RENDERER\");","title":"setup() accepts any contract with code as RENDERER: the cross-launch handoff is not checked to be a WorkerFrensRenderer over the launch's art"},{"citation":"resolved","description":"Asymmetry between setRenderer and freezeArt: setRenderer refuses nothing (any address, address(0) included) and freezeArt refuses nothing either, so the irreversible step can be taken while renderer is unset or points at a contract that is not a renderer (finding 1's state). After that, IMD6900Frens.tokenURI reverts for every token, revealed or not, for the life of the collection, and no role can repair it. Access note: onlyCurator admits the owner OR the governor, so after handover the Ethereum timelock can also change or freeze the art, which the README's role split ('Owner ... the art') does not say. This is curator-only self-harm with no unprivileged amplifier, and src/frens/ cannot change without moving the 0x6900 addresses, so it is recorded as a trust assumption for the operator: run freezeArt only after tokenURI has been read successfully through the wired renderer. Not a blocking defect.","line":1012,"path":"src/frens/IMD6900Frens.sol","reproduction":"pf = new PlaceFrens(); frens = pf.frens(); as FrensPlan.OWNER call frens.freezeArt() (renderer still address(0)); then as OWNER call frens.setRenderer(<WorkerFrensRenderer>). Expected (defensively): freezeArt refuses while renderer has no code. Actual: freezeArt succeeds, setRenderer reverts Unauthorized(), renderer stays address(0), and tokenURI(id) for any minted id reverts forever (scratch test test_FreezeArtBeforeSetRenderer reproduces this offline).","severity":"info","snippet":"    function freezeArt() external onlyCurator {\n        artFrozen = true;\n        emit Setting(\"artFrozen\", 1, renderer);\n    }","title":"freezeArt() has no check that a renderer is set and draws: a freeze before (or with) a wrong setRenderer makes tokenURI revert forever (trust assumption, curator-only)"}],"hash":"703ad07c5dfb60510bf787860c3b3e817ff992d36f774f8fe0cc8bcdc6a1e8cd","nodeId":"d4d7d75c-d2c8-4520-a02a-a55d8919094b","outcome":"completed","summary":"The review is complete and written to `.imd-findings.json`. The tree is otherwise unchanged.\n\n**Outcome for the assigned area.** The three art-launch contracts hold under the Access Control, Trust Gap and Asymmetry guides. WorkerArt1 and WorkerArt2 have no functions, storage or roles. WorkerFrensRenderer has only view and pure functions, no owner, and a nonpayable constructor that makes no calls and refuses any chunk pair but the exact art. Every chunk read re-checks the code hash. The combo ranges the renderer rejects match the ranges the collection's reveal check enforces, so the permissive attributes function is unreachable with a bad combo. I confirmed the seven swarm chunk hashes in the index against mainnet with cast at block 26,149,046, and all match. The manifest validates against the schema and the offline suite passes (173 tests).\n\n**Two findings, both at the seam where the collection is wired to this renderer, neither in the art contracts:**\n\n- **Low, script/frens/DeployFrens.s.sol line 66.** The setup script accepts any address with code as RENDERER. It never checks the target is a WorkerFrensRenderer over the launch's art. Reproduced offline: with RENDERER set to the collection launch's PlaceModules, setup completes, seals the traits and wires the collection to a contract whose tokenURI call reverts. A second setup run with the right value fails on TraitsAreSealed, so recovery is a manual setRenderer transaction the scripts do not provide. Suggested fix: check art1 and art2 code hashes or dry-call pendingURI before broadcasting. No source under src/ changes.\n- **Info, src/frens/IMD6900Frens.sol line 1012.** freezeArt has no check that a working renderer is set, so a freeze before or with a wrong setRenderer breaks tokenURI permanently. It is curator-only self-harm with no unprivileged amplifier, and src/frens/ is frozen by the address constraint, so it is recorded as an operator trust assumption rather than a blocking defect. The curator modifier also admits the governor, so the timelock can change or freeze the art after handover.\n\n**Coverage.** All 57 listed entry points have a row, 55 holds and 2 finding, plus six rows for the art launch's own constructors, view surface, hash invariants and the manifest. No row is unreached. The collection rows reflect the access-control lens only. Economic and numerical depth on the floor and swap paths belongs to the other specialists.","treeHash":null,"usage":{"cachedInputTokens":1453546,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":28347,"runtime":"claude","turns":41,"wallClockMs":541570}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"c0fc4ea4f50e3380","findings":[{"citation":"resolved","description":"Merged from audit_permissions (8da5a9cb) and audit_economics (51cdc233): one root cause. The art launch and the collection launch are independent and the only thing that joins them is the team wallet running setup() with RENDERER in the environment. setup() checks only that RENDERER has code, then in one broadcast calls setRenderer(renderer), setParams, setModules, launchRules and sealTraits. The art launch's output lists three new addresses (WorkerArt1, WorkerArt2, WorkerFrensRenderer) and the collection launch's PlaceModules also has code, so any of them pasted by mistake passes the check. IMD6900Frens.setRenderer (src/frens/IMD6900Frens.sol:1005) stores whatever it is given and tokenURI (line 345) then calls pendingURI/tokenURI on it: an art chunk is a STOP byte and returns no data, so decoding the string reverts and every token's metadata is unavailable. setup() cannot be re-run: its last call sealTraits() reverts TraitsAreSealed on a second run, so the recovery path is an ad-hoc setRenderer transaction the scripts do not provide, and it exists only while artFrozen is false (see finding 2). Operator-only, recoverable before freezeArt, no funds at risk: low. The renderer's own constructor refuses wrong art (BadArt), so the launch side is strong; only the setup side that wires the collection to it is weak. Minimal fix, in the script only (nothing in src/ changes, so the 0x6900 addresses and the art hashes stay): before broadcasting, require that RENDERER is a WorkerFrensRenderer over the launch's art, e.g. WorkerFrensRenderer(renderer).art1().codehash and art2().codehash equal WorkerArtIndex's entries 7 and 8, or a staticcall to pendingURI(1) that returns a non-empty string; optionally refuse RENDERER equal to MODULES or to the collection's own addresses.","line":66,"path":"script/frens/DeployFrens.s.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {ERC20} from \"solady/tokens/ERC20.sol\";\nimport {Ownable} from \"solady/auth/Ownable.sol\";\nimport {FrensPlan} from \"src/FrensPlan.sol\";\nimport {PlaceFrens, PlaceModules} from \"src/FrensPlacement.sol\";\nimport {IMD6900Frens} from \"src/frens/IMD6900Frens.sol\";\nimport {WorkerFrensRenderer} from \"src/frens/WorkerFrensRenderer.sol\";\nimport {WorkerArt1, WorkerArt2} from \"src/art/WorkerArt.sol\";\nimport {DeployFrens} from \"script/frens/DeployFrens.s.sol\";\n\n/// @dev A plain ERC20 with explicit allowances only (like $IMD)\ncontract Imd is ERC20 {\n    function name() public pure override returns (string memory) {\n        return \"IMD\";\n    }\n\n    function symbol() public pure override returns (string memory) {\n        return \"IMD\";\n    }\n\n    function mint(address to, uint256 a) external {\n        _mint(to, a);\n    }\n\n    function _givePermit2InfiniteAllowance() internal pure override returns (bool) {\n        return false;\n    }\n}\n\n/// @dev What the collection reads of IMD6900 and identity.md here: balances (zero), and isDistributor (false)\ncontract Strategy is ERC20 {\n    function name() public pure override returns (string memory) {\n        return \"IMD6900\";\n    }\n\n    function symbol() public pure override returns (string memory) {\n        return \"IMD6900\";\n    }\n\n    function isDistributor(address) external pure returns (bool) {\n        return false;\n    }\n}\n\n/// @notice The team wallet's setup() takes any contract with code as RENDERER: pointing the collection at WorkerArt1\n///         (an address the same launch output lists) makes every tokenURI revert, and freezeArt() makes that permanent.\ncontract SetupRendererProofTest is Test {\n    bytes constant CREATE2_DEPLOYER_CODE =\n        hex\"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe03601600081602082378035828234f58015156039578182fd5b8082525050506014600cf3\";\n    address constant OWNER = FrensPlan.OWNER;\n\n    DeployFrens s;\n    IMD6900Frens frens;\n    PlaceModules pm;\n    address art1;\n    address renderer;\n\n    function setUp() public {\n        vm.etch(FrensPlan.CREATE2_DEPLOYER, CREATE2_DEPLOYER_CODE);\n        vm.etch(FrensPlan.IMD, address(new Imd()).code);\n        vm.etch(FrensPlan.IMD6900, address(new Strategy()).code);\n        vm.etch(FrensPlan.IDENTITY, address(new Strategy()).code);\n        // the collection launch, then the art launch\n        PlaceFrens pf = new PlaceFrens();\n        pm = new PlaceModules(pf);\n        art1 = address(new WorkerArt1());\n        address art2 = address(new WorkerArt2());\n        renderer = address(new WorkerFrensRenderer(art1, art2));\n        frens = IMD6900Frens(payable(pf.frens()));\n        s = new DeployFrens();\n        vm.setEnv(\"MODULES\", vm.toString(address(pm)));\n        Imd(FrensPlan.IMD).mint(OWNER, 100e18);\n    }\n\n    /// @dev Fails on the code as it is: setup() accepts WorkerArt1 as RENDERER. Passes once setup() checks that RENDERER\n    ///      is a WorkerFrensRenderer over the launch's art (or at least answers pendingURI).\n    function test_SetupRefusesAnAddressThatIsNotARenderer() public {\n        vm.setEnv(\"RENDERER\", vm.toString(art1)); // the art launch's first address instead of its third\n        try s.setup() {\n            assertTrue(false, \"setup() accepted an address that cannot draw a fren as the renderer\");\n        } catch {}\n        assertTrue(frens.renderer() != art1, \"the collection must not point at an art chunk\");\n    }\n}","reproduction":"Offline (test/scratch/SetupRendererProof.t.sol, attached as proof; run with forge test --offline --match-path test/scratch/SetupRendererProof.t.sol): etch a plain ERC20 at FrensPlan.IMD and an ERC20 answering isDistributor()=false at FrensPlan.IMD6900 and FrensPlan.IDENTITY; deploy PlaceFrens, PlaceModules(PlaceFrens), WorkerArt1, WorkerArt2, WorkerFrensRenderer(art1, art2); set MODULES=<PlaceModules>, RENDERER=<WorkerArt1>; call DeployFrens.setup(). Expected: setup() refuses an address that is not a WorkerFrensRenderer over WorkerArt1/WorkerArt2. Actual: setup() completes and logs 'set up, sealed, drawn by <WorkerArt1>'; frens.renderer() == WorkerArt1; traitsSealed() == true. Then (test_WrongRendererBreaksTokenURIAndFreezeMakesItPermanent in test/scratch/SetupRenderer.t.sol, passing on this code): as FrensPlan.OWNER approve $IMD and requestMintFor(IMD6900, 1, max) -> token 1 exists; frens.tokenURI(1) reverts (empty return data from the STOP contract fails the string decode); as OWNER sealTraits() reverts TraitsAreSealed() (so setup() cannot be re-run with the right value); as OWNER freezeArt() then setRenderer(<real renderer>) reverts Unauthorized(): the collection's metadata is broken for good. The same sequence with RENDERER=<WorkerFrensRenderer> is the fork suite's test_fork_SetupDrawsWithTheLaunchsArt, which passes at mainnet block 26149450.","severity":"low","snippet":"        require(renderer.code.length != 0, \"no renderer at RENDERER\");","title":"setup() accepts any contract with code as RENDERER: a wrong art-launch address wires the collection to a non-renderer, and freezeArt() can make it permanent"},{"citation":"resolved","description":"From audit_permissions (8c91efca), reproduced. setRenderer refuses nothing (any address, address(0) included, until artFrozen) and freezeArt refuses nothing either: not that renderer != address(0), not that it has code, not that it draws. So the irreversible step can be taken while renderer is unset or points at a contract that is not a renderer (finding 1's state), and after that IMD6900Frens.tokenURI reverts for every token, revealed or not, for the life of the collection; no role can repair it. onlyCurator admits the owner or the governor, so after the handover the Ethereum timelock can also change or freeze the art. There is no unprivileged amplifier: this is curator self-harm, and src/frens/ cannot change without moving the 0x6900 addresses, so it is recorded as an operating rule for the team wallet rather than a blocking defect: call freezeArt() only after tokenURI has been read successfully through the wired renderer (and, per finding 1, have setup() check the renderer before wiring it).","line":1012,"path":"src/frens/IMD6900Frens.sol","reproduction":"Offline (test_FreezeArtBeforeAnyRenderer in test/scratch/SetupRenderer.t.sol, passing on this code): pf = new PlaceFrens(); frens = IMD6900Frens(pf.frens()); renderer() == address(0); as FrensPlan.OWNER call frens.freezeArt(); then as OWNER call frens.setRenderer(<WorkerFrensRenderer>). Expected (defensively): freezeArt refuses while renderer has no code. Actual: freezeArt succeeds (artFrozen() == true), setRenderer reverts Unauthorized(), renderer stays address(0), and tokenURI(id) for any minted id reverts forever (the call to address(0).pendingURI has no code to answer and the string decode fails).","severity":"info","snippet":"    function freezeArt() external onlyCurator {\n        artFrozen = true;","title":"freezeArt() checks nothing: it can freeze an unset or non-drawing renderer, after which tokenURI reverts forever (curator-only trust assumption)"},{"citation":"resolved","description":"From audit_math (a51500d7), reproduced. Not a contract defect: a stale assumption in the fork suite the brief points reviewers at (FrensPlacementForkTest). The test schedules FrensTimelockBatch.batch(FRENS_AT, SWAPPER_AT, true, false) with salt keccak256(\"worker-frens-open-1\") on the Ethereum timelock 0xBd3ed9F4AbD9946cA6F59C8F13A3EbebDE1EA29D and expects the operation to be Unset. On mainnet that exact operation (id 0x6ebf60b5d6a49cec8df7fdb89ce96061ddb4316aaba0dc170007c223634c1ffa) has already been scheduled by the team: getTimestamp(id) returns 1791649787 (pending), so OpenZeppelin's TimelockController reverts with TimelockUnexpectedOperationState(id, 0x..01) (selector 0x5ead8eb5, expected-state bit 1 = Unset). The other five fork tests, including test_fork_DrawsLikeTheReference (the art launch's byte-for-byte render check) and test_fork_SetupDrawsWithTheLaunchsArt, pass against the same fork. The art launch itself is unaffected: WorkerArt1, WorkerArt2 and WorkerFrensRenderer never touch the timelock. The author should either skip the scheduleBatch when tl.getTimestamp(id) != 0 and warp to that timestamp instead, or pin the fork to a block before the schedule transaction, so the suite stays green on the live chain the launch is checked against.","line":679,"path":"test/FrensPlacement.t.sol","reproduction":"MAINNET_RPC_URL=https://ethereum-rpc.publicnode.com forge test --offline --match-contract FrensPlacementForkTest -vv (reproduced at fork block 26149450). Expected: 6 passed. Actual: 5 passed, 1 failed: test_fork_WorkersFirstThenPublic fails at the scheduleBatch call with custom error 0x5ead8eb5 and data (0x6ebf60b5d6a49cec8df7fdb89ce96061ddb4316aaba0dc170007c223634c1ffa, 1). Confirming the state directly: cast call 0xBd3ed9F4AbD9946cA6F59C8F13A3EbebDE1EA29D 'getTimestamp(bytes32)(uint256)' 0x6ebf60b5d6a49cec8df7fdb89ce96061ddb4316aaba0dc170007c223634c1ffa returns 1791649787, where the id is hashOperationBatch over FrensTimelockBatch.batch(0x69007Ce82E0BF7981780585afF7c597415903547, 0x6900453deFAc8Bb12eabdcf57CCC5a14E7628AeE, true, false) with predecessor 0 and salt keccak256(\"worker-frens-open-1\"). Before the team's schedule transaction the call returned 0 and the test passed.","severity":"info","snippet":"        tl.scheduleBatch(targets, values, datas, bytes32(0), salt, 48 hours);","title":"test_fork_WorkersFirstThenPublic fails on live mainnet: the timelock batch it schedules is already scheduled on chain"}],"hash":"7a8c91121ee27176104a7b57cd5e2572e3b9fe089a65dda3ac976802c285819e","nodeId":"6ff444da-ea02-46ef-a725-d442bdbf1d6f","outcome":"completed","summary":"The review is complete. The findings file is on disk, no tracked file changed, and the scratch files sit in the excluded `test/scratch/` directory.\n\n**Outcome: the art launch itself is clean.** Nothing in WorkerArt1, WorkerArt2 or WorkerFrensRenderer reproduced as a defect, and my own pass found nothing the four specialists missed. Three specialist findings survived reproduction, merged from four reports. One is low, two are informational, and none blocks the art launch.\n\n**What I verified for the art launch**\n\n- The three contracts build and deploy offline with no owner or role. The renderer's constructor reads only the two code hashes and reverts BadArt on swapped, empty or foreign chunks.\n- Every art read rechecks its chunk's hash. The framed PUSH32 unpacking in `_entry` is correct for any offset and length, and the launch's entries read back as the art kit's bytes.\n- The seven live swarm chunks on Ethereum hash to the index's entries. I fetched their code directly and compared.\n- On a mainnet fork at block 26149450 the renderer draws the reference renders byte for byte. Revealed reads cost about 4M gas and pending reads about 13.1M to 13.7M, under the 2^24 cap.\n- The manifest is schema-valid, in dependency order, and the launch prices at 13.6M gas with over 3M to spare. The offline suite passes with 186 tests.\n\n**Findings written to .imd-findings.json**\n\n1. **Low, merged from permissions and economics.** The team wallet's setup() in `script/frens/DeployFrens.s.sol` accepts any address with code as RENDERER. Pointing it at WorkerArt1 wires the collection to a STOP contract, every tokenURI reverts, setup() cannot be re-run because sealTraits() refuses a second run, and freezeArt() makes the mistake permanent. Reproduced offline with a proof test that fails on this code and passes once setup() checks the renderer. The fix is script-only, so the 0x6900 addresses and art hashes stay put.\n2. **Info, trust assumption.** freezeArt() checks nothing, so it can freeze an unset or non-drawing renderer. Curator-only self-harm, reproduced offline, no unprivileged amplifier.\n3. **Info, test suite.** test_fork_WorkersFirstThenPublic fails on live mainnet because the team has already scheduled that exact timelock batch. Reproduced on the fork and confirmed with a direct getTimestamp read. The other five fork tests pass.\n\n**Coverage.** All 56 listed entry points have a row, 55 as holds with one-line reasons and freezeArt as finding 2, plus seven rows for the art launch's own invariants.","treeHash":null,"usage":{"cachedInputTokens":2445406,"inputTokens":514,"model":"claude-fable-5-1","outputTokens":42713,"runtime":"claude","turns":42,"wallClockMs":827107}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"1272a65455eb54ca","findings":[],"hash":"a57c90eafe65fbf83ceb8de5feda7b20cab2847994091d2c1343daab7c2b7bf4","nodeId":"88f38de2-4462-4946-8073-a354d8e63a21","outcome":"failed","summary":"the task produced no changes; the agent's last message was: Blocked by the execution environment: every command fails before starting with `bwrap: setting up uid map: Permission denied`.\n\nNo files changed. I could not read the supplied checks, reproduce the finding, or run `forge build` and `forge test`; both attempts hit the same error.\n\nPlease rerun this assignment with a working sandbox.","treeHash":null,"usage":{"cachedInputTokens":62848,"inputTokens":13013,"model":"gpt-6-astra","outputTokens":712,"runtime":"codex","turns":3,"wallClockMs":43779}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"b29f0968f2df07a0","findings":[],"hash":"c0958b2318213fd2663eb8463258b9427a2bb382ccecc5ac4e0e08eadcd90a37","nodeId":"13300c8f-f96c-4d74-85c8-e7d8a13a329b","outcome":"failed","summary":"the local build failed, so this was not submitted:\nforge build exited with 1:\nCompiling 91 files with Solc 0.8.30\nError: solc exited with signal: 9 (SIGKILL)\n<empty output>","treeHash":null,"usage":{"cachedInputTokens":4510336,"inputTokens":179536,"model":"gpt-6-astra","outputTokens":24161,"runtime":"codex","turns":31,"wallClockMs":2597101}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"e382bd4d2b3e471f","findings":[{"citation":"resolved","description":"Commit e1bdafd split the launch into the collection (PlaceFrens, PlaceModules(PlaceFrens)) and the art (WorkerArt1, WorkerArt2, WorkerFrensRenderer(art1, art2)), but launch.json was not touched by that commit and still describes the launch that failed as launch 1043: one launch of PlaceFrens, WorkerArt1, WorkerArt2 and PlaceModules. Three things in it no longer match the sources. (1) PlaceModules' constructor is now `constructor(PlaceFrens placed)` (src/FrensPlacement.sol:90, ABI: one input) while the manifest supplies three arguments; the two extra words are silently ignored by the ABI decoder, so nothing checks them. (2) WorkerFrensRenderer is not in the manifest at all, and PlaceModules no longer creates it (the notes' sentence 'PlaceModules creates FrenSwapper, FrenMinter, FrenWorkerGate and WorkerFrensRenderer' is false): a launch run from this file would deploy the two art chunks and never the renderer, so setup() would have no RENDERER to point the collection at and every tokenURI would revert. (3) Priced exactly as test_EachLaunchFitsOneTransaction prices a launch (creations, EIP-7623 calldata, 300,000 + 7 gas a byte of launcher overhead), the four contracts cost 23,761,253 gas, 6,984,037 over the 2^24 cap, so the deployer's simulation reverts again as it did for launch 1043. ADAPTATION.md carries the same stale four-contract table. The art launch's code itself (WorkerArt1, WorkerArt2, WorkerFrensRenderer) needs no change: the adapter must replace launch.json with the three-contract art manifest in the order WorkerArt1, WorkerArt2, WorkerFrensRenderer [\"$contract:WorkerArt1\", \"$contract:WorkerArt2\"], which the sources' own test prices at 13,605,058 gas all in.","line":17,"path":"launch.json","reproduction":"State: the tree at HEAD (e1bdafd). Input: launch.json as committed. Check 1: `forge inspect PlaceModules abi` prints `constructor(PlaceFrens)` (one address), the manifest gives three constructorArgs. Check 2: `grep -c WorkerFrensRenderer launch.json` in the contracts array is 0; the only renderer creation in src/ is the standalone contract src/frens/WorkerFrensRenderer.sol, and src/FrensPlacement.sol no longer references it. Check 3: a scratch test that deploys PlaceFrens, WorkerArt1, WorkerArt2 and PlaceModules(PlaceFrens) with `new`, measures the creations, and prices them with the formula from test/FrensPlacement.t.sol _launchTx (21_000 + 4*tokens + creations + 300_000 + 7*bytes, tokens at 1 per zero byte and 4 per non-zero byte of the four creation codes with the manifest's constructor arguments appended) logs 23,761,253; expected: under 16,777,216 with 1,000,000 to spare (the repository's own MARGIN); actual: 6,984,037 over the cap. For comparison, `forge test --match-test test_EachLaunchFitsOneTransaction -vv` logs the split launches at 12,308,310 (collection) and 13,605,058 (art).","severity":"medium","snippet":"      \"contract\": \"PlaceModules\",\n      \"constructorArgs\": [\n        \"$contract:PlaceFrens\",\n        \"$contract:WorkerArt1\",\n        \"$contract:WorkerArt2\"\n      ]","title":"Committed launch.json is the pre-split four-contract manifest: 23.76M gas (7.0M over EIP-7825), three arguments to PlaceModules' one-argument constructor, and no WorkerFrensRenderer"},{"citation":"resolved","description":"Read in full: src/art/WorkerArt.sol (WorkerArt1, WorkerArt2: constructors that return a STOP byte followed by PUSH32-framed art, no storage, no functions), src/art/WorkerArtIndex.sol, src/frens/WorkerFrensRenderer.sol (every function, including the assembly in _draw, _copy and _entry), src/FrensPlacement.sol, src/FrensPlan.sol, src/FrensCode.sol (header), script/frens/DeployFrens.s.sol, test/FrensPlacement.t.sol, test/FrensArtFraming.t.sol, the protected harness and launch.json. Read only where the renderer touches them: src/frens/IMD6900Frens.sol (IFrenRenderer, tokenURI, setRenderer, freezeArt). Not reviewed this round (unchanged since IMD job fd3018ee and outside the art launch): the rest of IMD6900Frens, FrenSwapper, FrenMinter, FrenWorkerGate, FrenPrices, and IMD's launcher contract, whose overhead the sources model as 300,000 + 7 gas a byte from two receipts. Findings on the art launch's code: none. The three contracts have no owner, role, storage write after construction, payable path, receive or fallback, external call, DELEGATECALL, CALLCODE or SELFDESTRUCT (the admission scan passes on creation and runtime code). The renderer's constructor only reads the two chunks' code hashes against WorkerArtIndex.CHUNK_HASHES, so a mis-ordered or foreign chunk reverts with BadArt and it deploys on a fresh chain. Every _entry read re-checks its chunk's code hash; the swarm's seven chunks on Ethereum hash to exactly the index's entries 0..6 (fetched by eth_getCode at block 0x18f0011). _draw clamps rows and runs to the 84x84 canvas, so no write leaves the canvas buffer; a layer that ends early reverts with Missing. The BMP header is consistent (84-byte rows need no padding, 1024-byte palette, bottom-up rows). Runtime sizes: WorkerArt1 23,332, WorkerArt2 17,920, WorkerFrensRenderer 16,192 bytes, all under EIP-170; initcodes 28,472 / 21,869 / 17,407 bytes, under EIP-3860.","line":23,"path":"src/frens/WorkerFrensRenderer.sol","reproduction":"Verification run: `forge build --offline` and `forge test --offline` (all suites: 0 failed); `MAINNET_RPC_URL=https://ethereum-rpc.publicnode.com forge test --match-contract FrensPlacementForkTest` (6 passed: the renderer draws the art kit's seven reference frens and three unrevealed cards byte for byte, tokenURI 3.7M-4.2M gas, pendingURI 13.1M-13.7M gas, under 2^24). Beyond the shipped tests, a scratch fork test (a) parsed all 82 index entries against the drawing loop's format (4-byte header, h rows of runs each covering exactly w pixels, nothing after), (b) drew every (character, face) pair, every item and every hat, and (c) fuzzed 256 seeds over all 12 backgrounds asserting no canvas pixel keeps index 0, i.e. the window (offsets 0..36 over a 120x120 background) always covers the canvas: all passed. Expected and actual agree; nothing to fix in the art launch.","severity":"info","snippet":"contract WorkerFrensRenderer {","title":"Review coverage: the three art-launch contracts read in full and verified on a mainnet fork; no defect found in them"}],"hash":"f16adb569505eb837d7bbd92ad03757131d9adbdbe428240e459da909b45eeb0","nodeId":"c3fdc03b-9d51-4016-b670-3a5fc0658efb","outcome":"completed","summary":"The review is complete. The findings file holds two entries and the tree is otherwise unchanged.\n\n**Outcome.** The art launch's three contracts, WorkerArt1, WorkerArt2 and WorkerFrensRenderer, have no defect I could substantiate. The one real problem is outside their code: the committed manifest is the pre-split one.\n\n**Finding, medium: launch.json still describes the launch that failed.** The split commit did not touch it. It lists PlaceFrens, WorkerArt1, WorkerArt2 and PlaceModules as one launch, passes three arguments to a constructor that now takes one, and omits the renderer entirely, while its notes still claim PlaceModules creates it. Priced by the repository's own formula, the four contracts cost 23.76M gas, about 7.0M over the 2^24 cap, so the deployer's simulation would revert again. The adapter must replace it with the three-contract art manifest, which the sources' test prices at 13.6M. ADAPTATION.md carries the same stale table.\n\n**What was read.** The three art-launch contracts in full, including the renderer's assembly, plus WorkerArtIndex, FrensPlacement, FrensPlan, the deploy script, the launch and framing tests, the protected harness and the manifest. The collection's contracts were read only where the renderer touches them, since they are unchanged since the earlier hardening job and belong to the other launch. IMD's launcher contract was not reachable, so its overhead remains the sources' extrapolation.\n\n**What was verified.**\n- The offline suite and the mainnet fork suite pass. The renderer draws the art kit's reference frens and unrevealed cards byte for byte, and the seven swarm chunks on Ethereum hash exactly to the index.\n- Beyond the shipped tests, a scratch fork test parsed all 82 art entries against the drawing loop's format, drew every character, face, hat and item, and fuzzed 256 seeds over all 12 backgrounds to confirm the window always covers the canvas. All passed.\n- The three contracts hold no owner, role, payable path, external call or forbidden opcode. The renderer's constructor only compares code hashes, so a mis-ordered chunk reverts and it deploys on a fresh chain. Runtime and initcode sizes sit under the EIP-170 and EIP-3860 limits.\n\nThe findings are in `.imd-findings.json`. The scratch tests were removed after running.","treeHash":null,"usage":{"cachedInputTokens":1155020,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":26617,"runtime":"claude","turns":32,"wallClockMs":971086}},{"artifacts":[],"attempt":2,"bundleHash":"d6cee31faa5a1d357cdc3d45ed3684782bbaf1342697f87fca53f57a43bfcddc","device":"bfffb5246a55ee5d","findings":[],"hash":"f5a419f5c98c2492ca7d97a38b718e29f4f75c07fe0b3db58c1283c9108bd19c","nodeId":"13300c8f-f96c-4d74-85c8-e7d8a13a329b","outcome":"completed","summary":"Updated only [launch.json](/home/startover/.identitymd/work/3dde4444-a257-47fc-aa52-62677afe414f/13300c8f-f96c-4d74-85c8-e7d8a13a329b/launch.json), preserving the required contract order and arguments.\n\nValidation passed: schema, constructor ABIs, and `forge build`. `forge test`: 173 passed; 5 fork suites skipped because `MAINNET_RPC_URL` is unset. Art launch gas: 13,605,058.","treeHash":"6576345185199b4b4bbbea3e408a6739482087ac","usage":{"cachedInputTokens":2559104,"inputTokens":132617,"model":"gpt-6-astra","outputTokens":15927,"runtime":"codex","turns":17,"wallClockMs":1241387}}],"verification":[{"checks":[{"durationMs":135256,"exitCode":0,"name":"build","output":"Compiling 91 files with Solc 0.8.30\nSolc 0.8.30 finished in 135.03s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> test/FrensPlacement.t.sol:778:9:\n    |\n778 |         uint256 s = _find(j, key, 0) + key.length;\n    |         ^^^^^^^^^\nNote: The shadowed declaration is here:\n   --> test/FrensPlacement.t.sol:472:5:\n    |\n472 |     DeployFrens s;\n    |     ^^^^^^^^^^^^^\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/frens/IMD6900Frens.sol:953:66\n    │\n953 │         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:70:9\n   │\n70 │         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:69:9\n   │\n69 │         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:71:9\n   │\n71 │         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:67:9\n   │\n67 │         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:68:9\n   │\n68 │         address imd6900_,\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:998:36\n    │\n998 │         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:1064:9\n     │\n1064 │         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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:150:16\n    │\n150 │         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[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[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[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/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[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[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[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:184:35\n    │\n184 │                 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:192:23\n    │\n192 │         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:192:31\n    │\n192 │         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:194:13\n    │\n194 │             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:198:13\n    │\n198 │             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:200:23\n    │\n200 │         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:200:31\n    │\n200 │         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:221:35\n    │\n221 │                 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:226:27\n    │\n226 │         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:226:35\n    │\n226 │         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:227:26\n    │\n227 │         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:227:34\n    │\n227 │         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:234:35\n    │\n234 │                 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:240:27\n    │\n240 │         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:240:35\n    │\n240 │         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:241:23\n    │\n241 │         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:241:31\n    │\n241 │         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:243:9\n    │\n243 │         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:314:9\n    │\n314 │         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:311:9\n    │\n311 │         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 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:312:9\n    │\n312 │         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 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:318:9\n    │\n318 │         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:319:9\n    │\n319 │         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:310:9\n    │\n310 │         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:313:9\n    │\n313 │         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 imdPayTo_,\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:333:9\n    │\n333 │         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:370:16\n    │\n370 │         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:375:16\n    │\n375 │         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:384:38\n    │\n384 │             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:406:13\n    │\n406 │             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:369:30\n    │\n369 │         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:843:13\n    │\n843 │         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:974:9\n    │\n974 │         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:568:9\n    │\n568 │         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:853:9\n    │\n853 │         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:968:13\n    │\n968 │             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:564:82\n    │\n564 │         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:630:38\n    │\n630 │             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:622:13\n    │\n622 │         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:650:13\n    │\n650 │             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:639:28\n    │\n639 │             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:641:90\n    │\n641 │             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:634:22\n    │\n634 │         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:672:53\n    │\n672 │         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:696:9\n    │\n696 │         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:722:13\n    │\n722 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours) 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:722:53\n    │\n722 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours) 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:738:9\n    │\n738 │         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:729:25\n    │\n729 │         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:867:9\n    │\n867 │         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:889:9\n    │\n889 │         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:867:9\n    │\n867 │         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:882:9\n    │\n882 │         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:904:9\n    │\n904 │         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:923:9\n    │\n923 │         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:944:35\n    │\n944 │     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:946:9\n    │\n946 │         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:1005:26\n     │\n1005 │     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:1008:9\n     │\n1008 │         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:1014:9\n     │\n1014 │         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:1030:17\n     │\n1030 │                 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:1042:84\n     │\n1042 │             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:1058:9\n     │\n1058 │         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:527:21\n    │\n527 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n528 │         bytes memory sig = _sign(frens, relayerKey, id, c, d);\n529 │         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:537:17\n    │\n537 │         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:682:50\n    │\n682 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n683 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n683 │         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:683:17\n    │\n683 │         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:267:34\n    │\n267 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n696 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n696 │         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:696:17\n    │\n696 │         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:718:50\n    │\n718 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n719 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n719 │         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:719:17\n    │\n719 │         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:734:50\n    │\n734 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n    ‡\n739 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n739 │         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:278:21\n    │\n278 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n    ‡\n739 │         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:739:17\n    │\n739 │         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:267:34\n    │\n267 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n750 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n750 │         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:750:17\n    │\n750 │         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:267:34\n    │\n267 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n757 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n757 │         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:757:17\n    │\n757 │         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:322:17\n    │\n322 │         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:357:17\n    │\n357 │         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":2797,"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 319.68µ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 518.46µ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 478.35µ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 711.01µ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.73ms (0.00ns CPU time)\n\nRan 9 tests for test/frens/IMD6900FrensValidator.t.sol:IMD6900FrensValidatorTest\n[PASS] test_AuthorizationDoesNotOutliveTheSale() (gas: 193381)\n[PASS] test_AuthorizationFromTwoTopLevelCallsIsLost() (gas: 86911)\nLogs:\n  sale authorized in a separate top-level call: blocked\n\n[PASS] test_AuthorizationIsForThatFrenOnly() (gas: 394740)\n[PASS] test_AuthorizationIsForThatOperatorOnly() (gas: 479875)\n[PASS] test_ConduitAloneIsBlocked() (gas: 53311)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 79396)\n[PASS] test_HolderSendsItHerself() (gas: 87104)\n[PASS] test_OnlyTheAuthorizerAuthorizes() (gas: 74631)\n[PASS] test_ZoneAuthorizedSaleGoesThroughInOneFrame() (gas: 124660)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 10.44ms (1.49ms CPU time)\n\nRan 18 tests for test/frens/FrenWorkerGate.t.sol:FrenWorkerGateTest\n[PASS] test_ARefusedMintTakesNothing() (gas: 104998)\n[PASS] test_AnNftCountsOnce_whoeverHoldsItLater() (gas: 151129)\n[PASS] test_CreditsGoWhereTheHolderSays() (gas: 639709)\n[PASS] test_NoGateNoWindow() (gas: 442112)\n[PASS] test_OnlyFrensSpends() (gas: 32613)\n[PASS] test_OnlyTheHolderClaims() (gas: 67063)\n[PASS] test_OpenPublic() (gas: 528980)\n[PASS] test_OwnerMintsBeforeTheOpening() (gas: 2296674)\n[PASS] test_SeatOperatorClaimsTheStrategysNfts() (gas: 138514)\n[PASS] test_TheCreditIsTheMinters() (gas: 576408)\n[PASS] test_WindowClosesAfter420() (gas: 32491871)\n[PASS] test_WindowNeedsACreditPerFren() (gas: 714638)\n[PASS] test_WindowNeverOvershoots() (gas: 36197537)\n[PASS] test_WlAndWorkersShareThe420() (gas: 619421)\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: 751030)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 17.10ms (13.16ms CPU time)\n\nRan 6 tests for test/frens/FrenSwapperAverage.t.sol:FrenSwapperAverageTest\n[PASS] test_AFullBuyMovesItOneStep() (gas: 103298)\n[PASS] test_ASmallBuyMovesItInProportion() (gas: 52688)\n[PASS] test_DustMovesItNextToNothing() (gas: 7581882)\n[PASS] test_FloorRateIsTheLowerReading() (gas: 17216)\n[PASS] test_MovesDownToo() (gas: 52548)\n[PASS] test_OnceABlock() (gas: 91113)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 55.22ms (2.12ms CPU time)\n\nRan 8 tests for test/FrensLaunchReview.t.sol:FrensAuditReviewTest\n[PASS] test_Audit_GovernorCanCloseMintAgain() (gas: 463703)\n[PASS] test_Audit_OwnerOrGovernorCanBlockAllPeerTransfers() (gas: 1372488)\n[PASS] test_Fix_LapsedJobApprovalIsReleasedByTheReveal() (gas: 1034129)\n[PASS] test_Fix_LastFrenOutStaysOutSoFeesHaveAnOwner() (gas: 1171597)\n[PASS] test_Fix_LaunchAdoptsASwapperSeededAtThePoolsPrice() (gas: 1868092)\n[PASS] test_Fix_LaunchRefusesTheWrongArt() (gas: 18811)\n[PASS] test_Fix_LaunchReplacesASwapperSeededOffASkewedPrice() (gas: 2596387)\n[PASS] test_Fix_LaunchsOwnSwapperStartsAtThePoolsPrice() (gas: 236724)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 55.12ms (7.09ms CPU time)\n\nRan 84 tests for test/frens/IMD6900Frens.t.sol:IMD6900FrensTest\n[PASS] test_AMintNeverFailsOnTheSwap() (gas: 946501)\n[PASS] test_BatchCannotRepeatAFren() (gas: 641777)\n[PASS] test_BatchCountIsOneTo69() (gas: 2185636)\n[PASS] test_BatchCountsTheCapsAsItGoes() (gas: 11785283)\n[PASS] test_BatchHoldsPepesForLowTiers() (gas: 12180874)\n[PASS] test_BatchPaysOneJob() (gas: 700293)\n[PASS] test_BatchRevealIsExactlyTheVoucher() (gas: 1012095)\n[PASS] test_BatchRevealsThemAll() (gas: 2254191)\n[PASS] test_BatchSoldOut() (gas: 90765188)\n[PASS] test_BuyTreasuryRespectsMaxPay() (gas: 1807199)\n[PASS] test_BuyingFromTheTreasuryRaisesTheFloor() (gas: 3060520)\n[PASS] test_CurveWhileAboveTheFloor() (gas: 1591127)\n[PASS] test_DearerImd6900RaisesTheFloorPrice() (gas: 2846975)\n[PASS] test_DustEthBuyCannotTakeTheMintsTurn() (gas: 539958)\n[PASS] test_EveryFrenIsOneOfAKind() (gas: 947054)\n[PASS] test_FailedJob_payAnother() (gas: 1168121)\n[PASS] test_FeeEthBuysTheFloor() (gas: 1702668)\n[PASS] test_FloorBuysAreAnyones_onePerBlock() (gas: 1886993)\n[PASS] test_FloorBuysAreCapped() (gas: 55657779)\n[PASS] test_FloorIsReserveOverFrensOut() (gas: 2749300)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 2110486)\n[PASS] test_FrenSentStraightToTheTreasuryCounts() (gas: 2605187)\n[PASS] test_IsACreatorToken() (gas: 41743)\n[PASS] test_JobPaymentIsPinnedToImdsPayee() (gas: 974626)\n[PASS] test_KeeperApprovesTheJobPaidFor() (gas: 653212)\n[PASS] test_KeeperCannotApproveLongLivedPayments() (gas: 434010)\n[PASS] test_LapsedPaymentIsUsedAgain() (gas: 715229)\n[PASS] test_LastFrenOutStaysOut() (gas: 1582490)\n[PASS] test_LaterPartsAgreeOnWhatIsRevealed() (gas: 1347460)\n[PASS] test_LowTierRequestsNeedAPepeLeft() (gas: 11712756)\n[PASS] test_MaxMintGrowsWithTheBag() (gas: 4682103)\n[PASS] test_MaxPayStopsAPriceThatMoved() (gas: 10066140)\n[PASS] test_MaxRequest() (gas: 9919282)\n[PASS] test_MinTierOnlyGoesDown() (gas: 138825)\n[PASS] test_MintMintsAtOnce_unrevealed() (gas: 770570)\n[PASS] test_MintNeedsSealedTraits() (gas: 8627562)\n[PASS] test_MintNeverBelowTheFloor() (gas: 3403449)\n[PASS] test_MintNeverSkipsTheBuyForWantOfGas() (gas: 708859)\n[PASS] test_NoMintIntoTheTreasury() (gas: 1590016)\n[PASS] test_NoRequestForNobody() (gas: 61934)\n[PASS] test_NoTierInsideAFlashLoan() (gas: 687603)\n[PASS] test_NobodyElseApprovesJobs() (gas: 435602)\n[PASS] test_OneRevealPerFren() (gas: 717815)\n[PASS] test_OnlyHoldersRecycle() (gas: 1587638)\n[PASS] test_OnlyRealFrens() (gas: 743528)\n[PASS] test_OnlyTheOwnerSetsTheValidator() (gas: 1060220)\n[PASS] test_OwnerAdjustsTheArtUntilFrozen() (gas: 1552272)\n[PASS] test_PairRules() (gas: 875609)\n[PASS] test_PartsOnlyGoForward() (gas: 1035574)\n[PASS] test_PriceRisesWithMints() (gas: 38121727)\n[PASS] test_PriceTableIsTheCurve() (gas: 37031661)\n[PASS] test_QuoteCountsUnsweptImd() (gas: 3192315)\n[PASS] test_RareTraitsNeedTheTier() (gas: 1366007)\n[PASS] test_RecyclePaysTheFloorAndKeepsIt() (gas: 2892194)\n[PASS] test_RecyclePaysTheWaitingImdToo() (gas: 2719969)\n[PASS] test_ReleaseLapsedJobAfterTheReveal() (gas: 2133093)\n[PASS] test_RequestMintFor() (gas: 1084049)\n[PASS] test_ReserveOnlyLeavesThroughRecycle() (gas: 2826030)\n[PASS] test_RevealFollowsTheFren() (gas: 749295)\n[PASS] test_RevealInParts() (gas: 9744736)\nLogs:\n  gas, minting 69: 2117876\n  gas, revealing 30: 3092889\n\n[PASS] test_RevealNeedsTheRelayersVoucher() (gas: 803728)\n[PASS] test_RevealReleasesALapsedPayment() (gas: 916235)\n[PASS] test_RevealShowsTheFren() (gas: 1189238)\n[PASS] test_RevealsWhenever() (gas: 661569)\n[PASS] test_RoyaltiesPayTheFloor() (gas: 1718930)\n[PASS] test_RoyaltyBpsIsCappedAndOwnerOnly() (gas: 109478)\n[PASS] test_RulesAreFrozenOnceSealed() (gas: 69602)\n[PASS] test_SealNeedsEveryTraitAndTheSupply() (gas: 10522996)\n[PASS] test_SetMaxMint() (gas: 178348)\n[PASS] test_ShortSwapReverts() (gas: 1043815)\n[PASS] test_SwapperMayPullOnlyTheBuy() (gas: 954501)\n[PASS] test_TheOwnerKeepsTheCollection_theGovernorTheMechanics() (gas: 488972)\n[PASS] test_TierCountsWhatIsLeftAfterPaying() (gas: 919870)\n[PASS] test_TierIsTheBestOfTheBag() (gas: 633637)\n[PASS] test_TierIsTheMints() (gas: 1350662)\n[PASS] test_TierThresholdsOwnerOnlyAndOrdered() (gas: 155016)\n[PASS] test_TradesGoPastTheValidator() (gas: 1343397)\n[PASS] test_TraitCapsHold() (gas: 25774957)\n[PASS] test_TreasuryRoundTripCostsTheFloor() (gas: 3239545)\n[PASS] test_UnneededRetryFeedsTheFloor() (gas: 1023226)\n[PASS] test_UnrevealedFrenSellsAtTheFloor() (gas: 1015894)\n[PASS] test_UnrevealedFrensShareTheFloor() (gas: 1451826)\n[PASS] test_UnusedJobFeedsTheFloor() (gas: 697059)\n[PASS] test_VouchersExpire() (gas: 472525)\nSuite result: ok. 84 passed; 0 failed; 0 skipped; finished in 57.57ms (198.66ms CPU time)\n\nRan 3 tests for test/FrensLaunchReview.t.sol:FrensFactoryReviewTest\n[PASS] test_Audit_ConstructorLeavesTheDocumentedTeamSetup() (gas: 25152531)\n[PASS] test_FreshChainCallsOnlyContractsCreatedByTheLaunch() (gas: 28519889)\n[PASS] test_ProtectedFactoryDeploysBothLaunchesInOrder() (gas: 25351455)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 63.76ms (14.63ms CPU time)\n\nRan 12 tests for test/FrensArtFraming.t.sol:FrensArtFramingTest\n[PASS] testFuzz_EntryPastTheIndexIsMissing(uint256) (runs: 256, μ: 13969, ~: 13943)\nLogs:\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129639933\n\n[PASS] testFuzz_UnknownBackgroundIsMissing(uint256) (runs: 256, μ: 35607, ~: 35592)\nLogs:\n  Bound result 17692690092824743488549022085910298461613703346797571331001\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 63.85ms (204.94ms CPU time)\n\nRan 19 tests for test/FrensLaunchFailures.t.sol:FrensLaunchFailuresTest\n[PASS] testFuzz_CheckKnowsAFren(uint24,uint8) (runs: 512, μ: 1936758, ~: 1907628)\nLogs:\n  Bound result 2\n\n[PASS] testFuzz_OtherSaltsLandElsewhere(bytes32) (runs: 256, μ: 34945, ~: 34945)\n[PASS] testFuzz_QuoteIsTheCurveBeforeAnyoneHolds(uint256) (runs: 512, μ: 320534, ~: 318270)\nLogs:\n  Bound result 34\n\n[PASS] test_AnotherOwnerMovesTheFrens() (gas: 34214)\n[PASS] test_CollectionRefusesAShortPriceTable() (gas: 3059925)\n[PASS] test_CollectionWaitsForTheTeamWallet() (gas: 5941920)\n[PASS] test_EveryPriceIsTheTables() (gas: 27629816)\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: 4583570)\n[PASS] test_NoPricePastTheTable() (gas: 41307)\n[PASS] test_OtherSaltSchemeFailsTheLaunch() (gas: 140656)\n[PASS] test_RefusingDeployerFailsTheLaunch() (gas: 103369)\n[PASS] test_ScriptRefusesAnUnplacedChain() (gas: 14119470)\n[PASS] test_SecondLaunchReusesWhatIsPlaced() (gas: 1313470)\n[PASS] test_SilentDeployerFailsTheLaunch() (gas: 93234)\n[PASS] test_WrongArtFailsTheArtLaunch() (gas: 588910)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 85.46ms (228.11ms CPU time)\n\nRan 13 tests for test/FrensPlacement.t.sol:FrensPlacementTest\n[PASS] test_Attributes() (gas: 19594305)\n[PASS] test_CodeIsWhatTheSourcesBuild() (gas: 88334)\n[PASS] test_DeploysOnAFreshChain() (gas: 51532495)\n[PASS] test_EachLaunchFitsOneTransaction() (gas: 105289625)\nLogs:\n  the collection launch: gas, all in: 12308310\n  the art launch: gas, all in: 13605058\n\n[PASS] test_LandsWhereThePlanSays() (gas: 51643509)\n[PASS] test_NewArtReadsBackExactly() (gas: 14173584)\n[PASS] test_PassesTheAdmissionScan() (gas: 87697392)\n[PASS] test_PlanFollowsFromTheCode() (gas: 79539)\n[PASS] test_PlanMatchesTheScript() (gas: 13601623)\n[PASS] test_PriceTableIsTheCurve() (gas: 52824571)\n[PASS] test_RefusesOtherArt() (gas: 12971045)\n[PASS] test_SameAddressesWhoeverRunsIt() (gas: 102213411)\n[PASS] test_TakesWhatSomeonePlacedFirst() (gas: 52211127)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 129.00ms (252.62ms 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: 9)\n\n╭--------------------+-----------------+-------+---------+----------╮\n| Contract           | Selector        | Calls | Reverts | Discards |\n+===================================================================+\n| PlacedFrensHandler | approveJob      | 114   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyFloor        | 110   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyTreasury     | 114   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donate6900      | 126   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donateImd       | 108   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | feeEth          | 138   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mint            | 231   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mintThenRecycle | 104   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | recycle         | 127   | 9       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | retryJob        | 113   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | reveal          | 114   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setParams       | 132   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setRate         | 124   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | transfer        | 123   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | warp            | 142   | 0       | 0        |\n╰--------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 656\n  Bound result 97\n  Bound result 2\n  Bound result 5688\n  Bound result 26\n  Bound result 1\n  Bound result 203\n  Bound result 0\n  Bound result 2326\n  Bound result 1\n  Bound result 396\n  Bound result 2420\n  Bound result 82349680085327934\n  Bound result 1\n  Bound result 5\n  Bound result 1\n  Bound result 4\n  Bound result 7569\n  Bound result 69711352276498070\n  Bound result 1\n  Bound result 400\n  Bound result 3405\n  Bound result 70000000000000000000\n  Bound result 7367\n  Bound result 420\n  Bound result 1\n  Bound result 3\n  Bound result 6\n  Bound result 1\n  Bound result 3200\n  Bound result 4\n  Bound result 7029\n  Bound result 10\n  Bound result 15\n  Bound result 20548\n  Bound result 420\n  Bound result 1984\n  Bound result 0\n  Bound result 1516987330\n  Bound result 9132\n  Bound result 11\n  Bound result 70\n  Bound result 1\n  Bound result 16\n  Bound result 2221\n  Bound result 29\n  Bound result 1\n  Bound result 19140\n  Bound result 707\n  Bound result 1157\n  Bound result 2800\n  Bound result 23\n  Bound result 1\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.68s (2.68s CPU time)\n\nRan 15 test suites in 2.69s (3.23s CPU time): 173 tests passed, 0 failed, 5 skipped (178 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.lowerMinTier(uint8,uint8,uint8)\",\"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\":104,\"README.md\":147,\"foundry.toml\":22,\"launch.json\":21,\"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\":192,\"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\":135,\"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\":249,\"src/frens/FrenWorkerGate.sol\":137,\"src/frens/IMD6900Frens.sol\":1072,\"src/frens/WorkerFrensRenderer.sol\":377,\"test/FrensArtFraming.t.sol\":290,\"test/FrensLaunchFailures.t.sol\":520,\"test/FrensLaunchReview.t.sol\":449,\"test/FrensPlacedInvariants.t.sol\":619,\"test/FrensPlacement.t.sol\":810,\"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\":1710,\"test/frens/IMD6900FrensPermit2.fork.t.sol\":130,\"test/frens/IMD6900FrensValidator.fork.t.sol\":146,\"test/frens/IMD6900FrensValidator.t.sol\":231,\"test/test_art_launch_manifest.py\":52},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"37efd4a24e19086bb6956ff9ce2d46bf32084c875368e18c73513b4febaea74f","verifiedTreeHash":"54e12cfbcf21dd2ff7c0021da42672b13dbd7783","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":179251,"exitCode":0,"name":"build","output":"Compiling 94 files with Solc 0.8.30\nSolc 0.8.30 finished in 179.04s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> test/FrensPlacement.t.sol:778:9:\n    |\n778 |         uint256 s = _find(j, key, 0) + key.length;\n    |         ^^^^^^^^^\nNote: The shadowed declaration is here:\n   --> test/FrensPlacement.t.sol:472:5:\n    |\n472 |     DeployFrens s;\n    |     ^^^^^^^^^^^^^\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/FrenSwapper.sol:71:9\n   │\n71 │         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:67:9\n   │\n67 │         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:70:9\n   │\n70 │         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:69:9\n   │\n69 │         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:68:9\n   │\n68 │         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/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/FrenSwapper.sol:150:16\n    │\n150 │         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/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/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:184:35\n    │\n184 │                 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:192:23\n    │\n192 │         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:192:31\n    │\n192 │         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:194:13\n    │\n194 │             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:198:13\n    │\n198 │             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:200:23\n    │\n200 │         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:200:31\n    │\n200 │         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:221:35\n    │\n221 │                 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:226:27\n    │\n226 │         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:226:35\n    │\n226 │         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:227:26\n    │\n227 │         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:227:34\n    │\n227 │         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:234:35\n    │\n234 │                 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:240:27\n    │\n240 │         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:240:35\n    │\n240 │         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:241:23\n    │\n241 │         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[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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/frens/FrenSwapper.sol:241:31\n    │\n241 │         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:243:9\n    │\n243 │         poolManager.settle{value: ethPaid}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\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[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/frens/IMD6900Frens.sol:953:66\n    │\n953 │         return (ITransferValidator721.validateTransfer.selector, true);\n    │                                                                  ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\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-events-access-control]: `keeper` is changed without an event but is used for access control\n    ╭▸ src/frens/IMD6900Frens.sol:998:36\n    │\n998 │         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:1064:9\n     │\n1064 │         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/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: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[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 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:311:9\n    │\n311 │         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:319:9\n    │\n319 │         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 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 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:310:9\n    │\n310 │         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 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 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 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:318:9\n    │\n318 │         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:333:9\n    │\n333 │         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:370:16\n    │\n370 │         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:375:16\n    │\n375 │         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:384:38\n    │\n384 │             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:406:13\n    │\n406 │             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:369:30\n    │\n369 │         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:843:13\n    │\n843 │         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:974:9\n    │\n974 │         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:568:9\n    │\n568 │         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:853:9\n    │\n853 │         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:968:13\n    │\n968 │             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:564:82\n    │\n564 │         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:630:38\n    │\n630 │             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:622:13\n    │\n622 │         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:650:13\n    │\n650 │             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:639:28\n    │\n639 │             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:641:90\n    │\n641 │             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:634:22\n    │\n634 │         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:672:53\n    │\n672 │         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:696:9\n    │\n696 │         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:722:13\n    │\n722 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours) 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:722:53\n    │\n722 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours) 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:738:9\n    │\n738 │         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:729:25\n    │\n729 │         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:867:9\n    │\n867 │         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:889:9\n    │\n889 │         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:867:9\n    │\n867 │         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:882:9\n    │\n882 │         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:904:9\n    │\n904 │         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:923:9\n    │\n923 │         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:944:35\n    │\n944 │     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:946:9\n    │\n946 │         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:1005:26\n     │\n1005 │     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:1008:9\n     │\n1008 │         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:1014:9\n     │\n1014 │         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:1030:17\n     │\n1030 │                 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:1042:84\n     │\n1042 │             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:1058:9\n     │\n1058 │         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:527:21\n    │\n527 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n528 │         bytes memory sig = _sign(frens, relayerKey, id, c, d);\n529 │         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:537:17\n    │\n537 │         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:682:50\n    │\n682 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n683 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n683 │         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:683:17\n    │\n683 │         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:267:34\n    │\n267 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n696 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n696 │         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:696:17\n    │\n696 │         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:718:50\n    │\n718 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n719 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n719 │         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:719:17\n    │\n719 │         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:734:50\n    │\n734 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n    ‡\n739 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n739 │         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:278:21\n    │\n278 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n    ‡\n739 │         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:739:17\n    │\n739 │         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:267:34\n    │\n267 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n750 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n750 │         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:750:17\n    │\n750 │         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:267:34\n    │\n267 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n757 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n757 │         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:757:17\n    │\n757 │         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:322:17\n    │\n322 │         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:357:17\n    │\n357 │         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":4445,"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 340.97µ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 449.67µ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 484.78µ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 728.88µ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.07ms (0.00ns CPU time)\n\nRan 9 tests for test/frens/IMD6900FrensValidator.t.sol:IMD6900FrensValidatorTest\n[PASS] test_AuthorizationDoesNotOutliveTheSale() (gas: 193381)\n[PASS] test_AuthorizationFromTwoTopLevelCallsIsLost() (gas: 86911)\nLogs:\n  sale authorized in a separate top-level call: blocked\n\n[PASS] test_AuthorizationIsForThatFrenOnly() (gas: 394740)\n[PASS] test_AuthorizationIsForThatOperatorOnly() (gas: 479875)\n[PASS] test_ConduitAloneIsBlocked() (gas: 53311)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 79396)\n[PASS] test_HolderSendsItHerself() (gas: 87104)\n[PASS] test_OnlyTheAuthorizerAuthorizes() (gas: 74631)\n[PASS] test_ZoneAuthorizedSaleGoesThroughInOneFrame() (gas: 124660)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 4.42ms (751.01µs CPU time)\n\nRan 8 tests for test/FrensLaunchReview.t.sol:FrensAuditReviewTest\n[PASS] test_Audit_GovernorCanCloseMintAgain() (gas: 463703)\n[PASS] test_Audit_OwnerOrGovernorCanBlockAllPeerTransfers() (gas: 1372488)\n[PASS] test_Fix_LapsedJobApprovalIsReleasedByTheReveal() (gas: 1034129)\n[PASS] test_Fix_LastFrenOutStaysOutSoFeesHaveAnOwner() (gas: 1171597)\n[PASS] test_Fix_LaunchAdoptsASwapperSeededAtThePoolsPrice() (gas: 1868092)\n[PASS] test_Fix_LaunchRefusesTheWrongArt() (gas: 18811)\n[PASS] test_Fix_LaunchReplacesASwapperSeededOffASkewedPrice() (gas: 2596387)\n[PASS] test_Fix_LaunchsOwnSwapperStartsAtThePoolsPrice() (gas: 236724)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 6.48ms (3.99ms CPU time)\n\nRan 3 tests for test/FrensLaunchReview.t.sol:FrensFactoryReviewTest\n[PASS] test_Audit_ConstructorLeavesTheDocumentedTeamSetup() (gas: 25152531)\n[PASS] test_FreshChainCallsOnlyContractsCreatedByTheLaunch() (gas: 28519889)\n[PASS] test_ProtectedFactoryDeploysBothLaunchesInOrder() (gas: 25351455)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 121.74ms (12.28ms CPU time)\n\nRan 84 tests for test/frens/IMD6900Frens.t.sol:IMD6900FrensTest\n[PASS] test_AMintNeverFailsOnTheSwap() (gas: 946501)\n[PASS] test_BatchCannotRepeatAFren() (gas: 641777)\n[PASS] test_BatchCountIsOneTo69() (gas: 2185636)\n[PASS] test_BatchCountsTheCapsAsItGoes() (gas: 11785283)\n[PASS] test_BatchHoldsPepesForLowTiers() (gas: 12180874)\n[PASS] test_BatchPaysOneJob() (gas: 700293)\n[PASS] test_BatchRevealIsExactlyTheVoucher() (gas: 1012095)\n[PASS] test_BatchRevealsThemAll() (gas: 2254191)\n[PASS] test_BatchSoldOut() (gas: 90765188)\n[PASS] test_BuyTreasuryRespectsMaxPay() (gas: 1807199)\n[PASS] test_BuyingFromTheTreasuryRaisesTheFloor() (gas: 3060520)\n[PASS] test_CurveWhileAboveTheFloor() (gas: 1591127)\n[PASS] test_DearerImd6900RaisesTheFloorPrice() (gas: 2846975)\n[PASS] test_DustEthBuyCannotTakeTheMintsTurn() (gas: 539958)\n[PASS] test_EveryFrenIsOneOfAKind() (gas: 947054)\n[PASS] test_FailedJob_payAnother() (gas: 1168121)\n[PASS] test_FeeEthBuysTheFloor() (gas: 1702668)\n[PASS] test_FloorBuysAreAnyones_onePerBlock() (gas: 1886993)\n[PASS] test_FloorBuysAreCapped() (gas: 55657779)\n[PASS] test_FloorIsReserveOverFrensOut() (gas: 2749300)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 2110486)\n[PASS] test_FrenSentStraightToTheTreasuryCounts() (gas: 2605187)\n[PASS] test_IsACreatorToken() (gas: 41743)\n[PASS] test_JobPaymentIsPinnedToImdsPayee() (gas: 974626)\n[PASS] test_KeeperApprovesTheJobPaidFor() (gas: 653212)\n[PASS] test_KeeperCannotApproveLongLivedPayments() (gas: 434010)\n[PASS] test_LapsedPaymentIsUsedAgain() (gas: 715229)\n[PASS] test_LastFrenOutStaysOut() (gas: 1582490)\n[PASS] test_LaterPartsAgreeOnWhatIsRevealed() (gas: 1347460)\n[PASS] test_LowTierRequestsNeedAPepeLeft() (gas: 11712756)\n[PASS] test_MaxMintGrowsWithTheBag() (gas: 4682103)\n[PASS] test_MaxPayStopsAPriceThatMoved() (gas: 10066140)\n[PASS] test_MaxRequest() (gas: 9919282)\n[PASS] test_MinTierOnlyGoesDown() (gas: 138825)\n[PASS] test_MintMintsAtOnce_unrevealed() (gas: 770570)\n[PASS] test_MintNeedsSealedTraits() (gas: 8627562)\n[PASS] test_MintNeverBelowTheFloor() (gas: 3403449)\n[PASS] test_MintNeverSkipsTheBuyForWantOfGas() (gas: 708859)\n[PASS] test_NoMintIntoTheTreasury() (gas: 1590016)\n[PASS] test_NoRequestForNobody() (gas: 61934)\n[PASS] test_NoTierInsideAFlashLoan() (gas: 687603)\n[PASS] test_NobodyElseApprovesJobs() (gas: 435602)\n[PASS] test_OneRevealPerFren() (gas: 717815)\n[PASS] test_OnlyHoldersRecycle() (gas: 1587638)\n[PASS] test_OnlyRealFrens() (gas: 743528)\n[PASS] test_OnlyTheOwnerSetsTheValidator() (gas: 1060220)\n[PASS] test_OwnerAdjustsTheArtUntilFrozen() (gas: 1552272)\n[PASS] test_PairRules() (gas: 875609)\n[PASS] test_PartsOnlyGoForward() (gas: 1035574)\n[PASS] test_PriceRisesWithMints() (gas: 38121727)\n[PASS] test_PriceTableIsTheCurve() (gas: 37031661)\n[PASS] test_QuoteCountsUnsweptImd() (gas: 3192315)\n[PASS] test_RareTraitsNeedTheTier() (gas: 1366007)\n[PASS] test_RecyclePaysTheFloorAndKeepsIt() (gas: 2892194)\n[PASS] test_RecyclePaysTheWaitingImdToo() (gas: 2719969)\n[PASS] test_ReleaseLapsedJobAfterTheReveal() (gas: 2133093)\n[PASS] test_RequestMintFor() (gas: 1084049)\n[PASS] test_ReserveOnlyLeavesThroughRecycle() (gas: 2826030)\n[PASS] test_RevealFollowsTheFren() (gas: 749295)\n[PASS] test_RevealInParts() (gas: 9744736)\nLogs:\n  gas, minting 69: 2117876\n  gas, revealing 30: 3092889\n\n[PASS] test_RevealNeedsTheRelayersVoucher() (gas: 803728)\n[PASS] test_RevealReleasesALapsedPayment() (gas: 916235)\n[PASS] test_RevealShowsTheFren() (gas: 1189238)\n[PASS] test_RevealsWhenever() (gas: 661569)\n[PASS] test_RoyaltiesPayTheFloor() (gas: 1718930)\n[PASS] test_RoyaltyBpsIsCappedAndOwnerOnly() (gas: 109478)\n[PASS] test_RulesAreFrozenOnceSealed() (gas: 69602)\n[PASS] test_SealNeedsEveryTraitAndTheSupply() (gas: 10522996)\n[PASS] test_SetMaxMint() (gas: 178348)\n[PASS] test_ShortSwapReverts() (gas: 1043815)\n[PASS] test_SwapperMayPullOnlyTheBuy() (gas: 954501)\n[PASS] test_TheOwnerKeepsTheCollection_theGovernorTheMechanics() (gas: 488972)\n[PASS] test_TierCountsWhatIsLeftAfterPaying() (gas: 919870)\n[PASS] test_TierIsTheBestOfTheBag() (gas: 633637)\n[PASS] test_TierIsTheMints() (gas: 1350662)\n[PASS] test_TierThresholdsOwnerOnlyAndOrdered() (gas: 155016)\n[PASS] test_TradesGoPastTheValidator() (gas: 1343397)\n[PASS] test_TraitCapsHold() (gas: 25774957)\n[PASS] test_TreasuryRoundTripCostsTheFloor() (gas: 3239545)\n[PASS] test_UnneededRetryFeedsTheFloor() (gas: 1023226)\n[PASS] test_UnrevealedFrenSellsAtTheFloor() (gas: 1015894)\n[PASS] test_UnrevealedFrensShareTheFloor() (gas: 1451826)\n[PASS] test_UnusedJobFeedsTheFloor() (gas: 697059)\n[PASS] test_VouchersExpire() (gas: 472525)\nSuite result: ok. 84 passed; 0 failed; 0 skipped; finished in 121.85ms (193.40ms CPU time)\n\nRan 6 tests for test/frens/FrenSwapperAverage.t.sol:FrenSwapperAverageTest\n[PASS] test_AFullBuyMovesItOneStep() (gas: 103298)\n[PASS] test_ASmallBuyMovesItInProportion() (gas: 52688)\n[PASS] test_DustMovesItNextToNothing() (gas: 7581882)\n[PASS] test_FloorRateIsTheLowerReading() (gas: 17216)\n[PASS] test_MovesDownToo() (gas: 52548)\n[PASS] test_OnceABlock() (gas: 91113)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 284.79ms (2.42ms CPU time)\n\nRan 18 tests for test/frens/FrenWorkerGate.t.sol:FrenWorkerGateTest\n[PASS] test_ARefusedMintTakesNothing() (gas: 104998)\n[PASS] test_AnNftCountsOnce_whoeverHoldsItLater() (gas: 151129)\n[PASS] test_CreditsGoWhereTheHolderSays() (gas: 639709)\n[PASS] test_NoGateNoWindow() (gas: 442112)\n[PASS] test_OnlyFrensSpends() (gas: 32613)\n[PASS] test_OnlyTheHolderClaims() (gas: 67063)\n[PASS] test_OpenPublic() (gas: 528980)\n[PASS] test_OwnerMintsBeforeTheOpening() (gas: 2296674)\n[PASS] test_SeatOperatorClaimsTheStrategysNfts() (gas: 138514)\n[PASS] test_TheCreditIsTheMinters() (gas: 576408)\n[PASS] test_WindowClosesAfter420() (gas: 32491871)\n[PASS] test_WindowNeedsACreditPerFren() (gas: 714638)\n[PASS] test_WindowNeverOvershoots() (gas: 36197537)\n[PASS] test_WlAndWorkersShareThe420() (gas: 619421)\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: 751030)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 297.15ms (9.10ms CPU time)\n\nRan 13 tests for test/FrensPlacement.t.sol:FrensPlacementTest\n[PASS] test_Attributes() (gas: 19594305)\n[PASS] test_CodeIsWhatTheSourcesBuild() (gas: 88334)\n[PASS] test_DeploysOnAFreshChain() (gas: 51532495)\n[PASS] test_EachLaunchFitsOneTransaction() (gas: 105289625)\nLogs:\n  the collection launch: gas, all in: 12308310\n  the art launch: gas, all in: 13605058\n\n[PASS] test_LandsWhereThePlanSays() (gas: 51643509)\n[PASS] test_NewArtReadsBackExactly() (gas: 14173584)\n[PASS] test_PassesTheAdmissionScan() (gas: 87697392)\n[PASS] test_PlanFollowsFromTheCode() (gas: 79539)\n[PASS] test_PlanMatchesTheScript() (gas: 13601623)\n[PASS] test_PriceTableIsTheCurve() (gas: 52824571)\n[PASS] test_RefusesOtherArt() (gas: 12971045)\n[PASS] test_SameAddressesWhoeverRunsIt() (gas: 102213411)\n[PASS] test_TakesWhatSomeonePlacedFirst() (gas: 52211127)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 802.24ms (220.97ms CPU time)\n\nRan 19 tests for test/FrensLaunchFailures.t.sol:FrensLaunchFailuresTest\n[PASS] testFuzz_CheckKnowsAFren(uint24,uint8) (runs: 512, μ: 1937602, ~: 1907877)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_OtherSaltsLandElsewhere(bytes32) (runs: 256, μ: 34945, ~: 34945)\n[PASS] testFuzz_QuoteIsTheCurveBeforeAnyoneHolds(uint256) (runs: 512, μ: 322680, ~: 333998)\nLogs:\n  Bound result 3\n\n[PASS] test_AnotherOwnerMovesTheFrens() (gas: 34214)\n[PASS] test_CollectionRefusesAShortPriceTable() (gas: 3059925)\n[PASS] test_CollectionWaitsForTheTeamWallet() (gas: 5941920)\n[PASS] test_EveryPriceIsTheTables() (gas: 27629816)\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: 4583570)\n[PASS] test_NoPricePastTheTable() (gas: 41307)\n[PASS] test_OtherSaltSchemeFailsTheLaunch() (gas: 140656)\n[PASS] test_RefusingDeployerFailsTheLaunch() (gas: 103369)\n[PASS] test_ScriptRefusesAnUnplacedChain() (gas: 14119470)\n[PASS] test_SecondLaunchReusesWhatIsPlaced() (gas: 1313470)\n[PASS] test_SilentDeployerFailsTheLaunch() (gas: 93234)\n[PASS] test_WrongArtFailsTheArtLaunch() (gas: 588910)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 832.60ms (486.55ms CPU time)\n\nRan 12 tests for test/FrensArtFraming.t.sol:FrensArtFramingTest\n[PASS] testFuzz_EntryPastTheIndexIsMissing(uint256) (runs: 256, μ: 13973, ~: 13943)\nLogs:\n  Bound result 5827\n\n[PASS] testFuzz_UnknownBackgroundIsMissing(uint256) (runs: 256, μ: 35606, ~: 35592)\nLogs:\n  Bound result 5827\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 832.58ms (245.75ms CPU time)\n\nRan 11 tests for test/WorkerArtOffline.t.sol:WorkerArtOfflineTest\n[PASS] testFuzz_BitmapDecodesToCanvas(uint64,uint256) (runs: 96, μ: 3890444, ~: 3836264)\n[PASS] testFuzz_ConstructorRejectsAnyChangedByte(bool,uint256,uint8) (runs: 256, μ: 422653, ~: 422632)\nLogs:\n  Bound result 3470\n  Bound result 179\n\n[PASS] testFuzz_InvalidTraitsFailWithMissing(uint64,uint256,uint8,uint256) (runs: 256, μ: 2229373, ~: 2631696)\nLogs:\n  Bound result 1\n  Bound result 15\n\n[PASS] testFuzz_MetadataRoundTrip(uint256,uint64,uint256) (runs: 32, μ: 13985757, ~: 13910256)\n[PASS] testFuzz_ReadsRecheckHashes(uint256,uint256,uint8,uint256) (runs: 128, μ: 7774025, ~: 9169953)\nLogs:\n  Bound result 3\n  Bound result 189\n  Bound result 33\n\n[PASS] test_AllPendingReferenceRendersOffline() (gas: 264667932)\n[PASS] test_AllRevealedReferenceRendersOffline() (gas: 100538183)\n[PASS] test_ConstructorOnlyInspectsTheTwoNewChunks() (gas: 3846839)\n[PASS] test_EveryTraitValueCanRender() (gas: 168757424)\n[PASS] test_MetadataAtIntegerEdges() (gas: 28024602)\n[PASS] test_PendingReadsRecheckBothNewChunks() (gas: 41001637)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 832.48ms (3.71s 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      | 121   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyFloor        | 123   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyTreasury     | 96    | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donate6900      | 116   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donateImd       | 134   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | feeEth          | 120   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mint            | 270   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mintThenRecycle | 111   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | recycle         | 125   | 5       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | retryJob        | 112   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | reveal          | 110   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setParams       | 130   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setRate         | 100   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | transfer        | 131   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | warp            | 121   | 0       | 0        |\n╰--------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 0\n  Bound result 232\n  Bound result 347299999999989156\n  Bound result 1936\n  Bound result 32\n  Bound result 12117\n  Bound result 192\n  Bound result 1\n  Bound result 5146\n  Bound result 15\n  Bound result 16\n  Bound result 6\n  Bound result 3\n  Bound result 20\n  Bound result 759154877840634581835584\n  Bound result 1\n  Bound result 2\n  Bound result 23\n  Bound result 968778516037876960612\n  Bound result 0\n  Bound result 18446744073709551615\n  Bound result 2\n  Bound result 3\n  Bound result 1756\n  Bound result 49152\n  Bound result 1244\n  Bound result 0\n  Bound result 0\n  Bound result 707\n  Bound result 154\n  Bound result 144\n  Bound result 1\n  Bound result 0\n  Bound result 17267\n  Bound result 1\n  Bound result 91205\n  Bound result 6333\n  Bound result 17\n  Bound result 32\n  Bound result 1\n  Bound result 29\n  Bound result 3\n  Bound result 150\n  Bound result 24576\n  Bound result 3\n  Bound result 53493871873\n  Bound result 58721657514612981\n  Bound result 0\n  Bound result 470\n  Bound result 725835738\n  Bound result 4969\n  Bound result 2\n  Bound result 21605\n  Bound result 2123777\n  Bound result 779474679717994358772364\n  Bound result 13\n  Bound result 93\n  Bound result 39\n  Bound result 2\n  Bound result 64\n  Bound result 67\n  Bound result 950620\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.77s (2.77s CPU time)\n\nRan 2 tests for test/WorkerArtInvariants.t.sol:WorkerArtInvariantsTest\n[PASS]\nWorkerArtInvariantsTest invariants:\n[PASS] invariant_CodeAndArtNeverChangeThroughCalls\n[PASS] invariant_OnlySuccessfulChunkPaymentsRemain\n WorkerArtInvariantsTest invariants (runs: 64, calls: 3072, reverts: 0)\n\n╭----------------------+----------------+-------+---------+----------╮\n| Contract             | Selector       | Calls | Reverts | Discards |\n+====================================================================+\n| WorkerArtCallHandler | payRenderer    | 1078  | 0       | 0        |\n|----------------------+----------------+-------+---------+----------|\n| WorkerArtCallHandler | sendToChunk    | 992   | 0       | 0        |\n|----------------------+----------------+-------+---------+----------|\n| WorkerArtCallHandler | tryToChangeArt | 1002  | 0       | 0        |\n╰----------------------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 465\n  Bound result 0\n  Bound result 2\n  Bound result 619899614790878354\n  Bound result 4\n  Bound result 2\n  Bound result 247008508171728385\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 3\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 39825100397363602\n  Bound result 2\n  Bound result 4255\n  Bound result 10\n  Bound result 0\n  Bound result 2\n  Bound result 7308\n  Bound result 0\n  Bound result 6002\n  Bound result 5\n  Bound result 3\n  Bound result 2\n  Bound result 1\n  Bound result 2409\n  Bound result 11\n  Bound result 2\n  Bound result 2739622\n  Bound result 7\n  Bound result 1\n  Bound result 2\n  Bound result 1\n  Bound result 1\n  Bound result 96\n  Bound result 0\n  Bound result 1\n  Bound result 9952127479599157\n  Bound result 4\n  Bound result 1\n  Bound result 3\n  Bound result 2\n  Bound result 0\n  Bound result 0\n  Bound result 5479\n  Bound result 2\n  Bound result 996290846129324454\n  Bound result 6\n  Bound result 4\n  Bound result 1\n  Bound result 1\n  Bound result 2\n  Bound result 4\n  Bound result 0\n  Bound result 3\n  Bound result 2\n  Bound result 1\n  Bound result 2\n  Bound result 1\n  Bound result 0\n  Bound result 4145\n  Bound result 0\n  Bound result 0\n  Bound result 129\n  Bound result 1\n  Bound result 2\n  Bound result 1\n  Bound result 0\n  Bound result 909455462\n  Bound result 2\n  Bound result 2\n  Bound result 0\n  Bound result 0\n  Bound result 174251744981456190\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 139692423510523689\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 818387953924411676\n  Bound result 0\n  Bound result 0\n  Bound result 4\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 3\n  Bound result 1\n  Bound result 4\n  Bound result 1\n  Bound result 4\n  Bound result 0\n  Bound result 3\n  Bound result 2\n  Bound result 0\n  Bound result 0\n  Bound result 318391746972384629\n  Bound result 0\n  Bound result 1\n  Bound result 728\n  Bound result 2\n  Bound result 896949558851873025\n  Bound result 4\n  Bound result 1\n  Bound result 495790613314\n  Bound result 10\n  Bound result 1\n  Bound result 0\n  Bound result 4428\n  Bound result 1\n  Bound result 2\n  Bound result 1\n  Bound result 2\n  Bound result 3095\n  Bound result 0\n  Bound result 0\n  Bound result 376237603613764340\n\n[PASS] test_CallSequenceExercisesEveryHandlerAndPaymentBoundary() (gas: 1265634)\nLogs:\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 2\n  Bound result 1000000000000000000\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 2\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 1000000000000000000\n  Bound result 11\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 4.34s (4.34s CPU time)\n\nRan 17 test suites in 4.34s (11.25s CPU time): 186 tests passed, 0 failed, 5 skipped (191 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.lowerMinTier(uint8,uint8,uint8)\",\"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\":104,\"README.md\":147,\"foundry.toml\":22,\"launch.json\":21,\"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\":192,\"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\":135,\"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\":249,\"src/frens/FrenWorkerGate.sol\":137,\"src/frens/IMD6900Frens.sol\":1072,\"src/frens/WorkerFrensRenderer.sol\":377,\"test/FrensArtFraming.t.sol\":290,\"test/FrensLaunchFailures.t.sol\":520,\"test/FrensLaunchReview.t.sol\":449,\"test/FrensPlacedInvariants.t.sol\":619,\"test/FrensPlacement.t.sol\":810,\"test/WorkerArtInvariants.t.sol\":130,\"test/WorkerArtOffline.t.sol\":305,\"test/fixtures/LICENSE\":23,\"test/fixtures/README.md\":9,\"test/fixtures/SwarmArtChunks.sol\":1501,\"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\":1710,\"test/frens/IMD6900FrensPermit2.fork.t.sol\":130,\"test/frens/IMD6900FrensValidator.fork.t.sol\":146,\"test/frens/IMD6900FrensValidator.t.sol\":231,\"test/test_art_launch_manifest.py\":52},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"484ebb51b4eb9eb59d50f8ceedd220aa4d2b801cf501c6409184b9bb19de3b7c","verifiedTreeHash":"0ddc491c0fdf247fd1b1752930b9ad452a0a406f","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":139714,"exitCode":0,"name":"build","output":"Compiling 91 files with Solc 0.8.30\nSolc 0.8.30 finished in 139.51s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> test/FrensPlacement.t.sol:778:9:\n    |\n778 |         uint256 s = _find(j, key, 0) + key.length;\n    |         ^^^^^^^^^\nNote: The shadowed declaration is here:\n   --> test/FrensPlacement.t.sol:472:5:\n    |\n472 |     DeployFrens s;\n    |     ^^^^^^^^^^^^^\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/frens/IMD6900Frens.sol:953:66\n    │\n953 │         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: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[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/frens/FrenSwapper.sol:70:9\n   │\n70 │         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:68:9\n   │\n68 │         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:69:9\n   │\n69 │         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:67:9\n   │\n67 │         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:71:9\n   │\n71 │         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:998:36\n    │\n998 │         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:1064:9\n     │\n1064 │         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/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: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/FrenSwapper.sol:150:16\n    │\n150 │         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/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/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[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[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/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[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[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[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[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[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: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:184:35\n    │\n184 │                 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:192:23\n    │\n192 │         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:192:31\n    │\n192 │         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:194:13\n    │\n194 │             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:198:13\n    │\n198 │             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:200:23\n    │\n200 │         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:200:31\n    │\n200 │         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:221:35\n    │\n221 │                 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:226:27\n    │\n226 │         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:226:35\n    │\n226 │         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:227:26\n    │\n227 │         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:227:34\n    │\n227 │         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:234:35\n    │\n234 │                 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:240:27\n    │\n240 │         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:240:35\n    │\n240 │         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:241:23\n    │\n241 │         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:241:31\n    │\n241 │         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:243:9\n    │\n243 │         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[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 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 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:315:9\n    │\n315 │         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 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:319:9\n    │\n319 │         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:310:9\n    │\n310 │         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 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:311:9\n    │\n311 │         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 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 imdPayTo_,\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:333:9\n    │\n333 │         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:370:16\n    │\n370 │         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:375:16\n    │\n375 │         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:384:38\n    │\n384 │             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:406:13\n    │\n406 │             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:369:30\n    │\n369 │         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:843:13\n    │\n843 │         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:974:9\n    │\n974 │         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:568:9\n    │\n568 │         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:853:9\n    │\n853 │         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:968:13\n    │\n968 │             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:564:82\n    │\n564 │         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:630:38\n    │\n630 │             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:622:13\n    │\n622 │         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:650:13\n    │\n650 │             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:639:28\n    │\n639 │             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:641:90\n    │\n641 │             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:634:22\n    │\n634 │         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:672:53\n    │\n672 │         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:696:9\n    │\n696 │         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:722:13\n    │\n722 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours) 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:722:53\n    │\n722 │         if (deadline > block.timestamp + 1 hours || q.expiresAt > block.timestamp + 1 hours) 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:738:9\n    │\n738 │         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:729:25\n    │\n729 │         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:867:9\n    │\n867 │         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:889:9\n    │\n889 │         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:867:9\n    │\n867 │         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:882:9\n    │\n882 │         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:904:9\n    │\n904 │         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:923:9\n    │\n923 │         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:944:35\n    │\n944 │     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:946:9\n    │\n946 │         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:1005:26\n     │\n1005 │     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:1008:9\n     │\n1008 │         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:1014:9\n     │\n1014 │         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:1030:17\n     │\n1030 │                 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:1042:84\n     │\n1042 │             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:1058:9\n     │\n1058 │         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:527:21\n    │\n527 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n528 │         bytes memory sig = _sign(frens, relayerKey, id, c, d);\n529 │         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:537:17\n    │\n537 │         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:682:50\n    │\n682 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n683 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n683 │         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:683:17\n    │\n683 │         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:267:34\n    │\n267 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n696 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n696 │         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:696:17\n    │\n696 │         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:718:50\n    │\n718 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n719 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n719 │         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:719:17\n    │\n719 │         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:734:50\n    │\n734 │         (bytes32 pd,) = frens.approveJob(id, 42, block.timestamp + 600, _quote());\n    │                                                  ━━━━━━━━━━━━━━━\n    ‡\n739 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n739 │         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:278:21\n    │\n278 │         uint256 d = block.timestamp + 1 hours;\n    │                     ━━━━━━━━━━━━━━━\n    ‡\n739 │         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:739:17\n    │\n739 │         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:267:34\n    │\n267 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n750 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n750 │         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:750:17\n    │\n750 │         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:267:34\n    │\n267 │         frens.approveJob(id, id, block.timestamp + 600, _quote());\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n757 │         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:261:13\n    │\n261 │             block.timestamp + 600\n    │             ━━━━━━━━━━━━━━━\n    ‡\n757 │         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:757:17\n    │\n757 │         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:322:17\n    │\n322 │         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:357:17\n    │\n357 │         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":2773,"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 265.70µ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 401.47µ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 412.79µ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 553.18µ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.19ms (0.00ns CPU time)\n\nRan 9 tests for test/frens/IMD6900FrensValidator.t.sol:IMD6900FrensValidatorTest\n[PASS] test_AuthorizationDoesNotOutliveTheSale() (gas: 193381)\n[PASS] test_AuthorizationFromTwoTopLevelCallsIsLost() (gas: 86911)\nLogs:\n  sale authorized in a separate top-level call: blocked\n\n[PASS] test_AuthorizationIsForThatFrenOnly() (gas: 394740)\n[PASS] test_AuthorizationIsForThatOperatorOnly() (gas: 479875)\n[PASS] test_ConduitAloneIsBlocked() (gas: 53311)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 79396)\n[PASS] test_HolderSendsItHerself() (gas: 87104)\n[PASS] test_OnlyTheAuthorizerAuthorizes() (gas: 74631)\n[PASS] test_ZoneAuthorizedSaleGoesThroughInOneFrame() (gas: 124660)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 9.68ms (838.74µs CPU time)\n\nRan 84 tests for test/frens/IMD6900Frens.t.sol:IMD6900FrensTest\n[PASS] test_AMintNeverFailsOnTheSwap() (gas: 946501)\n[PASS] test_BatchCannotRepeatAFren() (gas: 641777)\n[PASS] test_BatchCountIsOneTo69() (gas: 2185636)\n[PASS] test_BatchCountsTheCapsAsItGoes() (gas: 11785283)\n[PASS] test_BatchHoldsPepesForLowTiers() (gas: 12180874)\n[PASS] test_BatchPaysOneJob() (gas: 700293)\n[PASS] test_BatchRevealIsExactlyTheVoucher() (gas: 1012095)\n[PASS] test_BatchRevealsThemAll() (gas: 2254191)\n[PASS] test_BatchSoldOut() (gas: 90765188)\n[PASS] test_BuyTreasuryRespectsMaxPay() (gas: 1807199)\n[PASS] test_BuyingFromTheTreasuryRaisesTheFloor() (gas: 3060520)\n[PASS] test_CurveWhileAboveTheFloor() (gas: 1591127)\n[PASS] test_DearerImd6900RaisesTheFloorPrice() (gas: 2846975)\n[PASS] test_DustEthBuyCannotTakeTheMintsTurn() (gas: 539958)\n[PASS] test_EveryFrenIsOneOfAKind() (gas: 947054)\n[PASS] test_FailedJob_payAnother() (gas: 1168121)\n[PASS] test_FeeEthBuysTheFloor() (gas: 1702668)\n[PASS] test_FloorBuysAreAnyones_onePerBlock() (gas: 1886993)\n[PASS] test_FloorBuysAreCapped() (gas: 55657779)\n[PASS] test_FloorIsReserveOverFrensOut() (gas: 2749300)\n[PASS] test_FloorMovesSkipTheValidator() (gas: 2110486)\n[PASS] test_FrenSentStraightToTheTreasuryCounts() (gas: 2605187)\n[PASS] test_IsACreatorToken() (gas: 41743)\n[PASS] test_JobPaymentIsPinnedToImdsPayee() (gas: 974626)\n[PASS] test_KeeperApprovesTheJobPaidFor() (gas: 653212)\n[PASS] test_KeeperCannotApproveLongLivedPayments() (gas: 434010)\n[PASS] test_LapsedPaymentIsUsedAgain() (gas: 715229)\n[PASS] test_LastFrenOutStaysOut() (gas: 1582490)\n[PASS] test_LaterPartsAgreeOnWhatIsRevealed() (gas: 1347460)\n[PASS] test_LowTierRequestsNeedAPepeLeft() (gas: 11712756)\n[PASS] test_MaxMintGrowsWithTheBag() (gas: 4682103)\n[PASS] test_MaxPayStopsAPriceThatMoved() (gas: 10066140)\n[PASS] test_MaxRequest() (gas: 9919282)\n[PASS] test_MinTierOnlyGoesDown() (gas: 138825)\n[PASS] test_MintMintsAtOnce_unrevealed() (gas: 770570)\n[PASS] test_MintNeedsSealedTraits() (gas: 8627562)\n[PASS] test_MintNeverBelowTheFloor() (gas: 3403449)\n[PASS] test_MintNeverSkipsTheBuyForWantOfGas() (gas: 708859)\n[PASS] test_NoMintIntoTheTreasury() (gas: 1590016)\n[PASS] test_NoRequestForNobody() (gas: 61934)\n[PASS] test_NoTierInsideAFlashLoan() (gas: 687603)\n[PASS] test_NobodyElseApprovesJobs() (gas: 435602)\n[PASS] test_OneRevealPerFren() (gas: 717815)\n[PASS] test_OnlyHoldersRecycle() (gas: 1587638)\n[PASS] test_OnlyRealFrens() (gas: 743528)\n[PASS] test_OnlyTheOwnerSetsTheValidator() (gas: 1060220)\n[PASS] test_OwnerAdjustsTheArtUntilFrozen() (gas: 1552272)\n[PASS] test_PairRules() (gas: 875609)\n[PASS] test_PartsOnlyGoForward() (gas: 1035574)\n[PASS] test_PriceRisesWithMints() (gas: 38121727)\n[PASS] test_PriceTableIsTheCurve() (gas: 37031661)\n[PASS] test_QuoteCountsUnsweptImd() (gas: 3192315)\n[PASS] test_RareTraitsNeedTheTier() (gas: 1366007)\n[PASS] test_RecyclePaysTheFloorAndKeepsIt() (gas: 2892194)\n[PASS] test_RecyclePaysTheWaitingImdToo() (gas: 2719969)\n[PASS] test_ReleaseLapsedJobAfterTheReveal() (gas: 2133093)\n[PASS] test_RequestMintFor() (gas: 1084049)\n[PASS] test_ReserveOnlyLeavesThroughRecycle() (gas: 2826030)\n[PASS] test_RevealFollowsTheFren() (gas: 749295)\n[PASS] test_RevealInParts() (gas: 9744736)\nLogs:\n  gas, minting 69: 2117876\n  gas, revealing 30: 3092889\n\n[PASS] test_RevealNeedsTheRelayersVoucher() (gas: 803728)\n[PASS] test_RevealReleasesALapsedPayment() (gas: 916235)\n[PASS] test_RevealShowsTheFren() (gas: 1189238)\n[PASS] test_RevealsWhenever() (gas: 661569)\n[PASS] test_RoyaltiesPayTheFloor() (gas: 1718930)\n[PASS] test_RoyaltyBpsIsCappedAndOwnerOnly() (gas: 109478)\n[PASS] test_RulesAreFrozenOnceSealed() (gas: 69602)\n[PASS] test_SealNeedsEveryTraitAndTheSupply() (gas: 10522996)\n[PASS] test_SetMaxMint() (gas: 178348)\n[PASS] test_ShortSwapReverts() (gas: 1043815)\n[PASS] test_SwapperMayPullOnlyTheBuy() (gas: 954501)\n[PASS] test_TheOwnerKeepsTheCollection_theGovernorTheMechanics() (gas: 488972)\n[PASS] test_TierCountsWhatIsLeftAfterPaying() (gas: 919870)\n[PASS] test_TierIsTheBestOfTheBag() (gas: 633637)\n[PASS] test_TierIsTheMints() (gas: 1350662)\n[PASS] test_TierThresholdsOwnerOnlyAndOrdered() (gas: 155016)\n[PASS] test_TradesGoPastTheValidator() (gas: 1343397)\n[PASS] test_TraitCapsHold() (gas: 25774957)\n[PASS] test_TreasuryRoundTripCostsTheFloor() (gas: 3239545)\n[PASS] test_UnneededRetryFeedsTheFloor() (gas: 1023226)\n[PASS] test_UnrevealedFrenSellsAtTheFloor() (gas: 1015894)\n[PASS] test_UnrevealedFrensShareTheFloor() (gas: 1451826)\n[PASS] test_UnusedJobFeedsTheFloor() (gas: 697059)\n[PASS] test_VouchersExpire() (gas: 472525)\nSuite result: ok. 84 passed; 0 failed; 0 skipped; finished in 66.59ms (199.00ms CPU time)\n\nRan 3 tests for test/FrensLaunchReview.t.sol:FrensFactoryReviewTest\n[PASS] test_Audit_ConstructorLeavesTheDocumentedTeamSetup() (gas: 25152531)\n[PASS] test_FreshChainCallsOnlyContractsCreatedByTheLaunch() (gas: 28519889)\n[PASS] test_ProtectedFactoryDeploysBothLaunchesInOrder() (gas: 25351455)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 69.83ms (18.55ms CPU time)\n\nRan 18 tests for test/frens/FrenWorkerGate.t.sol:FrenWorkerGateTest\n[PASS] test_ARefusedMintTakesNothing() (gas: 104998)\n[PASS] test_AnNftCountsOnce_whoeverHoldsItLater() (gas: 151129)\n[PASS] test_CreditsGoWhereTheHolderSays() (gas: 639709)\n[PASS] test_NoGateNoWindow() (gas: 442112)\n[PASS] test_OnlyFrensSpends() (gas: 32613)\n[PASS] test_OnlyTheHolderClaims() (gas: 67063)\n[PASS] test_OpenPublic() (gas: 528980)\n[PASS] test_OwnerMintsBeforeTheOpening() (gas: 2296674)\n[PASS] test_SeatOperatorClaimsTheStrategysNfts() (gas: 138514)\n[PASS] test_TheCreditIsTheMinters() (gas: 576408)\n[PASS] test_WindowClosesAfter420() (gas: 32491871)\n[PASS] test_WindowNeedsACreditPerFren() (gas: 714638)\n[PASS] test_WindowNeverOvershoots() (gas: 36197537)\n[PASS] test_WlAndWorkersShareThe420() (gas: 619421)\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: 751030)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 69.82ms (23.18ms CPU time)\n\nRan 8 tests for test/FrensLaunchReview.t.sol:FrensAuditReviewTest\n[PASS] test_Audit_GovernorCanCloseMintAgain() (gas: 463703)\n[PASS] test_Audit_OwnerOrGovernorCanBlockAllPeerTransfers() (gas: 1372488)\n[PASS] test_Fix_LapsedJobApprovalIsReleasedByTheReveal() (gas: 1034129)\n[PASS] test_Fix_LastFrenOutStaysOutSoFeesHaveAnOwner() (gas: 1171597)\n[PASS] test_Fix_LaunchAdoptsASwapperSeededAtThePoolsPrice() (gas: 1868092)\n[PASS] test_Fix_LaunchRefusesTheWrongArt() (gas: 18811)\n[PASS] test_Fix_LaunchReplacesASwapperSeededOffASkewedPrice() (gas: 2596387)\n[PASS] test_Fix_LaunchsOwnSwapperStartsAtThePoolsPrice() (gas: 236724)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 78.49ms (4.10ms CPU time)\n\nRan 19 tests for test/FrensLaunchFailures.t.sol:FrensLaunchFailuresTest\n[PASS] testFuzz_CheckKnowsAFren(uint24,uint8) (runs: 512, μ: 1934364, ~: 1907609)\nLogs:\n  Bound result 2\n\n[PASS] testFuzz_OtherSaltsLandElsewhere(bytes32) (runs: 256, μ: 34945, ~: 34945)\n[PASS] testFuzz_QuoteIsTheCurveBeforeAnyoneHolds(uint256) (runs: 512, μ: 315242, ~: 310726)\nLogs:\n  Bound result 1\n\n[PASS] test_AnotherOwnerMovesTheFrens() (gas: 34214)\n[PASS] test_CollectionRefusesAShortPriceTable() (gas: 3059925)\n[PASS] test_CollectionWaitsForTheTeamWallet() (gas: 5941920)\n[PASS] test_EveryPriceIsTheTables() (gas: 27629816)\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: 4583570)\n[PASS] test_NoPricePastTheTable() (gas: 41307)\n[PASS] test_OtherSaltSchemeFailsTheLaunch() (gas: 140656)\n[PASS] test_RefusingDeployerFailsTheLaunch() (gas: 103369)\n[PASS] test_ScriptRefusesAnUnplacedChain() (gas: 14119470)\n[PASS] test_SecondLaunchReusesWhatIsPlaced() (gas: 1313470)\n[PASS] test_SilentDeployerFailsTheLaunch() (gas: 93234)\n[PASS] test_WrongArtFailsTheArtLaunch() (gas: 588910)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 82.90ms (292.00ms CPU time)\n\nRan 6 tests for test/frens/FrenSwapperAverage.t.sol:FrenSwapperAverageTest\n[PASS] test_AFullBuyMovesItOneStep() (gas: 103298)\n[PASS] test_ASmallBuyMovesItInProportion() (gas: 52688)\n[PASS] test_DustMovesItNextToNothing() (gas: 7581882)\n[PASS] test_FloorRateIsTheLowerReading() (gas: 17216)\n[PASS] test_MovesDownToo() (gas: 52548)\n[PASS] test_OnceABlock() (gas: 91113)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 90.35ms (3.59ms CPU time)\n\nRan 13 tests for test/FrensPlacement.t.sol:FrensPlacementTest\n[PASS] test_Attributes() (gas: 19594305)\n[PASS] test_CodeIsWhatTheSourcesBuild() (gas: 88334)\n[PASS] test_DeploysOnAFreshChain() (gas: 51532495)\n[PASS] test_EachLaunchFitsOneTransaction() (gas: 105289625)\nLogs:\n  the collection launch: gas, all in: 12308310\n  the art launch: gas, all in: 13605058\n\n[PASS] test_LandsWhereThePlanSays() (gas: 51643509)\n[PASS] test_NewArtReadsBackExactly() (gas: 14173584)\n[PASS] test_PassesTheAdmissionScan() (gas: 87697392)\n[PASS] test_PlanFollowsFromTheCode() (gas: 79539)\n[PASS] test_PlanMatchesTheScript() (gas: 13601623)\n[PASS] test_PriceTableIsTheCurve() (gas: 52824571)\n[PASS] test_RefusesOtherArt() (gas: 12971045)\n[PASS] test_SameAddressesWhoeverRunsIt() (gas: 102213411)\n[PASS] test_TakesWhatSomeonePlacedFirst() (gas: 52211127)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 90.51ms (210.51ms CPU time)\n\nRan 12 tests for test/FrensArtFraming.t.sol:FrensArtFramingTest\n[PASS] testFuzz_EntryPastTheIndexIsMissing(uint256) (runs: 256, μ: 13973, ~: 13943)\nLogs:\n  Bound result 8223903049349761552842750\n\n[PASS] testFuzz_UnknownBackgroundIsMissing(uint256) (runs: 256, μ: 35607, ~: 35592)\nLogs:\n  Bound result 136260045236951616229\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 90.60ms (224.32ms 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      | 119   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyFloor        | 105   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | buyTreasury     | 138   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donate6900      | 127   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | donateImd       | 124   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | feeEth          | 128   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mint            | 254   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | mintThenRecycle | 110   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | recycle         | 105   | 4       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | retryJob        | 99    | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | reveal          | 125   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setParams       | 116   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | setRate         | 130   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | transfer        | 123   | 0       | 0        |\n|--------------------+-----------------+-------+---------+----------|\n| PlacedFrensHandler | warp            | 117   | 0       | 0        |\n╰--------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 6983\n  Bound result 19292\n  Bound result 3\n  Bound result 13913\n  Bound result 106992148071919268\n  Bound result 65861\n  Bound result 75000000000000000\n  Bound result 5318\n  Bound result 222\n  Bound result 2\n  Bound result 5000\n  Bound result 4594638\n  Bound result 2\n  Bound result 1\n  Bound result 1602\n  Bound result 2\n  Bound result 14182\n  Bound result 1\n  Bound result 281778117873996529099182\n  Bound result 15902\n  Bound result 106676573152954759\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 19785\n  Bound result 1\n  Bound result 1\n  Bound result 19241\n  Bound result 2835583808\n  Bound result 7126\n  Bound result 0\n  Bound result 1\n  Bound result 19301\n  Bound result 37\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 244\n  Bound result 600\n  Bound result 9802\n  Bound result 1\n  Bound result 37725976053921046\n  Bound result 2\n  Bound result 4\n  Bound result 16467\n  Bound result 1\n  Bound result 7\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 752\n  Bound result 0\n  Bound result 32210714461238394\n  Bound result 12932\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.66s (2.66s CPU time)\n\nRan 15 test suites in 2.67s (3.32s CPU time): 173 tests passed, 0 failed, 5 skipped (178 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.lowerMinTier(uint8,uint8,uint8)\",\"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\":104,\"README.md\":147,\"foundry.toml\":22,\"launch.json\":21,\"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\":192,\"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\":135,\"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\":249,\"src/frens/FrenWorkerGate.sol\":137,\"src/frens/IMD6900Frens.sol\":1072,\"src/frens/WorkerFrensRenderer.sol\":377,\"test/FrensArtFraming.t.sol\":290,\"test/FrensLaunchFailures.t.sol\":520,\"test/FrensLaunchReview.t.sol\":449,\"test/FrensPlacedInvariants.t.sol\":619,\"test/FrensPlacement.t.sol\":810,\"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\":1710,\"test/frens/IMD6900FrensPermit2.fork.t.sol\":130,\"test/frens/IMD6900FrensValidator.fork.t.sol\":146,\"test/frens/IMD6900FrensValidator.t.sol\":231,\"test/test_art_launch_manifest.py\":52},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"f5a419f5c98c2492ca7d97a38b718e29f4f75c07fe0b3db58c1283c9108bd19c","verifiedTreeHash":"6576345185199b4b4bbbea3e408a6739482087ac","verifierVersion":"0.1.0+ad90ce4c"}]}