{"assessments":[],"deployments":[],"fuzz":[],"identity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"interpretation":"Records acceptance and evidence. Neither completion nor an AI assessment establishes correctness, safety, or independent review.","jobId":"5a662510-983c-4f91-adf1-b8a3c23e456b","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"83f58a003fe3d7213ac173fa3581c847503cec19196bfd4364303ce6e5d3a249","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":"92b18502b6cb762ec9c6fe5e7dfbd9e50060a7114badc0dd33ee286dee039983","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":"8b1d6ba909a53dd374699f9c63f50ce76cd3e89f5e1c91ecb16c41baf58e49d8","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":"efe7bcf9cf450bffb437b93c0b2feb44293750815bc13423c1fe830c0b3a58db","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":"aa6a4c5f17bbafda860c9976bf7e4e2a3a36814a2e01192e3992b49032fa6746","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":"448ee4b4154ea3269273b43c97171aa1ea30c1b6cd1ce3b3351ca75eb3bab954","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":"f0982afa903db0265f9a9986128f6d43b645863c239854a8c1a1a3edc9a3a7b0","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":"050b5d8f2c18bbd7e8fd6cb1ce52fea5d02538b23ab4c88a15cf39f843411a06","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"86cec4a167ab2d8fc2f138a762e2fb006d3fc7b9d18daa29af1d7dca2fbb02c5","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 part 3 of 3 of the IMD6900 Frens on-chain art, exactly as it is in the repository, in this order: FrenArtChunk5, FrenArtChunk6, FrenArtChunk7 (src/FrenArtChunks.sol, no constructor arguments), then FrenRenderer (src/FrenRenderer.sol) with seven address arguments in this order: 0xa92dAcfF6d6fcC218ADe20eD24857376BD8eBE81 (FrenArtChunk1, IMD launch #819), 0x9666A481e20F1dB59EEbD6c43D11Ae3505468c92 (FrenArtChunk2, #819), 0x71BdEB749b3ee428730eBB3E9b34B03A99D82356 (FrenArtChunk3, #838), 0x0C344484D960B8474a1EdcB5A5128e8D9C9F6B4d (FrenArtChunk4, #838), then $contract:FrenArtChunk5, $contract:FrenArtChunk6, $contract:FrenArtChunk7. The chunks' code is generated art data (a STOP byte, then the bytes; FrenArtChunk7 framed with a PUSH32 byte before every 32 bytes so it reads as PUSH data only: README.md, test_ChunksPassTheAdmissionScan). Do not change the chunks, FrenArtIndex or the argument order. The renderer's constructor only stores the seven addresses (it reads nothing, so it deploys on a fresh chain where the earlier chunks don't exist: test_DeploysWithoutTheEarlierChunks); every read checks its chunk's code hash and reverts BadArt on anything else. FrenRenderer has no owner and nothing to configure; test/OnChainChunks.fork.t.sol draws the reference frens through the four chunks already deployed.","parentJobId":null,"planHash":"32f1f2052ce8c5dba56988a4807dd4d7eed70158ea29ccbd0471860b38108377","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"5a662510-983c-4f91-adf1-b8a3c23e456b","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-868-frenartchunk5-frenartchunk6-frenartchunk"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51186","feedbackHash":"2a98a56f0d28c749e7180628afc518dbc18695c4fb92bf6e7879885f3824ef24","nodeKey":"adapt_contract_project","submissionHash":"83f58a003fe3d7213ac173fa3581c847503cec19196bfd4364303ce6e5d3a249","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51423","feedbackHash":"e3143d8769b40cb99f1df88f77c7fe85c8e0cbdd8698ee9e802e1863351c00d2","nodeKey":"audit_economics","submissionHash":"92b18502b6cb762ec9c6fe5e7dfbd9e50060a7114badc0dd33ee286dee039983","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51142","feedbackHash":"718beb707628c8b361a749e3fc5971d95b42fa0a58817937679e95878da3227c","nodeKey":"audit_flow","submissionHash":"8b1d6ba909a53dd374699f9c63f50ce76cd3e89f5e1c91ecb16c41baf58e49d8","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51538","feedbackHash":"c89e478aa847f0355c4dd7763072b74727ec130f0d58463cfe6fd18125eb09c4","nodeKey":"audit_imported_code","submissionHash":"efe7bcf9cf450bffb437b93c0b2feb44293750815bc13423c1fe830c0b3a58db","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52222","feedbackHash":"1103f51d814261fd69b9511526f25729ee3de114395a599717d1de5cc5f5c023","nodeKey":"audit_judge","submissionHash":"aa6a4c5f17bbafda860c9976bf7e4e2a3a36814a2e01192e3992b49032fa6746","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51227","feedbackHash":"5c5ddd06c4b682d5eeaa1af29667196d040fee1b131003e3108bddeb3b47da0e","nodeKey":"audit_math","submissionHash":"448ee4b4154ea3269273b43c97171aa1ea30c1b6cd1ce3b3351ca75eb3bab954","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51166","feedbackHash":"83d36b79c23d15a3c308cb29a0ede479946f69d0584e5aa4ef657042edaa22be","nodeKey":"audit_permissions","submissionHash":"f0982afa903db0265f9a9986128f6d43b645863c239854a8c1a1a3edc9a3a7b0","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51750","feedbackHash":"91d1ec2a54a3752f9210167908db58a3b15fe110cf45ff829643eda46b0e4e34","nodeKey":"manifest","submissionHash":"050b5d8f2c18bbd7e8fd6cb1ce52fea5d02538b23ab4c88a15cf39f843411a06","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51598","feedbackHash":"9fe7ce1f345017c66b975aa0e265e6c426fa4a85119ba79dbbdf90c2f7f91229","nodeKey":"write_foundry_tests","submissionHash":"86cec4a167ab2d8fc2f138a762e2fb006d3fc7b9d18daa29af1d7dca2fbb02c5","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"a47ddec21953a2b904e705d73332a75c5d722189692f711a5851c346b7b335f8","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"89853bc1fc213696170861f28a0e9ddbd7eeb73318d5bcd804d1d71056cd9f6b","device":"e0f2f45a1b346346","findings":[],"hash":"050b5d8f2c18bbd7e8fd6cb1ce52fea5d02538b23ab4c88a15cf39f843411a06","nodeId":"d4bd9d0f-1a3c-4857-9efd-f5feaea16e8e","outcome":"completed","summary":"Created [launch.json](/root/.identitymd/work/5a662510-983c-4f91-adf1-b8a3c23e456b/d4bd9d0f-1a3c-4857-9efd-f5feaea16e8e/launch.json) with the exact deployment order and constructor arguments.\n\nValidation and `forge build` passed. `forge test`: 9 passed, 0 failed, 2 fork checks skipped.\n\nOnly `launch.json` changed outside scratch artifacts.","treeHash":"7c1071d8cbfa0e44c43d763aae1929d22b5e0a2c","usage":{"cachedInputTokens":464512,"inputTokens":49099,"model":"gpt-6-astra","outputTokens":3539,"runtime":"codex","turns":4,"wallClockMs":203348}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"11e4593175677f7e","findings":[{"citation":"resolved","description":"The documented combo layout uses bits 0-22 (character 0-1, face 2-5, eye 6-7, coat 8-9, shirt 10-12, hat 13-14, background 15-18, item 19-22). Every out-of-art value inside those fields is rejected with Missing() (face 13-15, character 3, coat 3, shirt 6-7, hat 3, background 10-15), but bit 23 of the uint24 is never masked, checked or used, so canvas/bmp/tokenURI/attributes accept it and produce output identical to the combo without it. Measured in a scratch test: tokenURI(1, 0x3AAD91, 42) and tokenURI(1, 0xBAAD91, 42) return byte-identical strings. The vendored reference renderer (test/ref/FrenRendererRef.sol) behaves the same, so this is a shared property and not a regression of the chunk renderer; it is reported so the consumer that assigns combos knows that a combo with bit 23 set is accepted rather than refused like every other out-of-layout value. Nothing in the renderer's own math is affected: every index derived from a combo is bounds-checked before it reaches FrenArtIndex.INDEX, FACE_TABLE or TABLES.","line":239,"path":"src/FrenRenderer.sol","reproduction":"Deploy the seven chunks and FrenRenderer(c1..c7). Call canvas(0x3AAD91, 42) and canvas(0xBAAD91, 42) (second = first | 1<<23). Expected if bit 23 were treated like any other out-of-layout value: the second call reverts Missing(). Actual: both calls succeed and return identical canvases; tokenURI and attributes for the two combos are byte-identical as well.","severity":"info","snippet":"        uint256 ch = combo & 3;\n        uint256 face = (combo >> 2) & 15;\n        uint256 bg = (combo >> 15) & 15;\n        if (ch > 2 || face >= FACES || bg >= 10) revert Missing();","title":"combo bit 23 is outside the trait layout and silently ignored: two different combos render the same token"}],"hash":"448ee4b4154ea3269273b43c97171aa1ea30c1b6cd1ce3b3351ca75eb3bab954","nodeId":"d9b00c43-adc9-4021-bab2-e886ae949e95","outcome":"completed","summary":"The review is complete. The findings file is written and valid, the scratch tests are removed, and the tree has no stray files (the findings file is ignored by a global rule, which is why plain status shows nothing).\n\n**Outcome:** the math in the renderer holds. One info-level note was recorded, no defects of medium severity or above.\n\n**What I checked in my area (Math Precision, Boundary, Numerical Gap)**\n\n- **Index decoding and framed reads.** Decoded all 70 index entries. Every entry sits inside its chunk, and for the two framed chunks the computed frame bounds land exactly on the chunk code sizes. The divide-before-multiply slither flagged is correct block arithmetic.\n- **Drawing math.** Clamping of runs to the canvas, signed offsets, and row bounds are safe. Every layer file is well-formed: rows sum exactly to their width, no zero-count runs, data consumed exactly. All ten backgrounds are 120x120 at the origin, so the 37-value window fully covers the 84-pixel canvas.\n- **Bitmap header and grey conversion.** Header fields fit their byte widths. Luminance never exceeds 255 and the dimming cannot overflow.\n- **Byte-equality sweep.** Every face of every character, every coat, hat, item and background, plus extreme seeds and token ids, matched the vendored reference renderer byte for byte.\n- **Out-of-art combos.** Every out-of-range field value reverts with Missing as intended.\n- **Gas boundary.** All 2222 token ids render pendingURI under 30M in isolated call frames. The worst is about 26.95M. An earlier 37M reading was my own harness's memory growth, not the renderer.\n- **External boundary.** The four deployed mainnet chunks carry exactly the code hashes the index demands, and the repository's fork test passes against them.\n\n**Finding recorded (info):** combo bit 23 is outside the trait layout and is silently ignored, so combos 0x3AAD91 and 0xBAAD91 render identically. The reference renderer behaves the same, so it is a shared property rather than a regression.\n\n**Coverage rows:** constructor, tokenURI, pendingURI, unrevealed, attributes, bmp, canvas all `holds`; the chunk constructors `holds` (return the literal art, hashes match). The slither uninitialized-local and missing-zero-check leads are intentional and not defects. Nothing in my area was left unreached. The launch gas script needs python3, which this worker lacks, so I did not rerun it.","treeHash":null,"usage":{"cachedInputTokens":1805564,"inputTokens":610,"model":"claude-fable-5-1","outputTokens":27813,"runtime":"claude","turns":33,"wallClockMs":668684}},{"artifacts":[],"attempt":1,"bundleHash":"2b10594191533026a62a9d5f03a54c65fa0ea070bec22899ccb73d867503836f","device":"ccf14607d159904b","findings":[],"hash":"83f58a003fe3d7213ac173fa3581c847503cec19196bfd4364303ce6e5d3a249","nodeId":"e3115c0b-a08a-47f1-a15b-65403f5ff2dd","outcome":"completed","summary":"Implemented launch-3 preparation, preserving the art, index, argument order, and renderer behavior.\n\n- `forge build`: passed.\n- `forge test`: 9 passed; 2 optional fork suites skipped.\n- Protected harness: passed.\n- Local CREATE2 rehearsal: **14,179,144 gas**, below the limit with a 5% reserve.\n\n[ADAPTATION.md](/root/.identitymd/work/5a662510-983c-4f91-adf1-b8a3c23e456b/e3115c0b-a08a-47f1-a15b-65403f5ff2dd/ADAPTATION.md) documents every change, audit disposition, and remaining integration check.","treeHash":"e7e252def6ede9437f2872f209e496870a732cef","usage":{"cachedInputTokens":3076608,"inputTokens":92677,"model":"gpt-6-astra","outputTokens":27445,"runtime":"codex","turns":10,"wallClockMs":981005}},{"artifacts":[],"attempt":1,"bundleHash":"a20e0a566854c7f84fcaf4a4cf6217d979e9436b796edb718c71b4b1bc8b566a","device":"dbf9fa8b755f8ed6","findings":[],"hash":"86cec4a167ab2d8fc2f138a762e2fb006d3fc7b9d18daa29af1d7dca2fbb02c5","nodeId":"bbd95caa-5105-4dbc-97d0-847ae08d5ab2","outcome":"completed","summary":"Added deployment, art-integrity, corruption, invalid-input, reference-rendering, and ETH invariant tests. All changes are under `test/`.\n\n- `forge build`: passed; existing lint warnings remain.\n- `forge test`: 22 passed, 0 failed, 2 optional fork suites skipped.\n- Invariants: 6,144 calls, zero handler reverts.\n\nNo new defects reproduced.","treeHash":"ccf264f79978d1a7077c3efb205acac2c7e6a0f9","usage":{"cachedInputTokens":2233472,"inputTokens":103155,"model":"gpt-6-astra","outputTokens":15157,"runtime":"codex","turns":8,"wallClockMs":476071}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"e57a8e639cccfbab","findings":[{"citation":"resolved","description":"pendingURI draws PENDING_FRAMES (4) full frens plus the wall and Base64-encodes a 4x84x84 bitmap twice. Measured through a fresh-frame meter contract on this toolchain (forge 1.8.3, solc 0.8.37 auto-detected; the figures with solc 0.8.26 are within 0.1%), the inner call costs between 23,922,977 and 26,952,180 gas across all 2222 token ids (worst: id 1985), and pendingURI(1) costs 25,442,528. Ethereum mainnet caps a transaction at 2^24 = 16,777,216 gas (EIP-7825, live since Fusaka; README.md line 78 states the same cap), so no on-chain caller can ever execute pendingURI in a transaction, and 1990 of the 2222 ids also exceed a 25M eth_call allowance that several RPC providers apply. tokenURI is unaffected: its worst case over a greedy search of every background, face, item, hat, coat and seed window is 10,930,538 gas (combo 0x6B241D, seed 8480) and the audit sample tokenURI(1, 0xbd59c, 42) costs 9,007,860, both under the cap. The README already documents the ~25.5M sample and the 30M budget; this finding adds the full-range figures (the sample is not the worst id) and the transaction-cap consequence. It is an integration limit rather than a correctness defect: the renderer only reads, so it only affects consumers that call pendingURI from a contract or through a capped RPC. No change to the art or output is implied; if the author wants pendingURI reachable from a transaction, PENDING_FRAMES or the double Base64 pass would have to shrink, which changes the agreed output and is a scope decision.","line":76,"path":"src/FrenRenderer.sol","reproduction":"Deploy FrenArtChunk1..7 and FrenRenderer(c1..c7) locally (as test/FrenRenderer.t.sol setUp does). From a separate contract (so the measuring frame's memory does not grow), run `(bool ok,) = address(r).staticcall{gas: 16_777_216}(abi.encodeCall(FrenRenderer.pendingURI, (1)))`: ok == false (out of gas). With gas 25_000_000: ok == false. With gas 40_000_000: ok == true and gasleft delta == 25,442,528. Looping id = 1..2222 with a 40M cap: 0 failures, max 26,952,180 at id 1985, min 23,922,977, 1990 ids above 25M, 0 above 27M. Expected per the deployment notes: a view readable within the chain's per-transaction limit; actual: never executable in a transaction, and above 25M for 90% of ids.","severity":"low","snippet":"    function pendingURI(uint256 tokenId) external view returns (string memory) {","title":"pendingURI cannot execute inside any Ethereum transaction: every token id needs more than the EIP-7825 2^24 gas cap, and 1990 of 2222 ids need more than 25M"},{"citation":"resolved","description":"attributes is a public pure entry point that maps each combo field to a name list with _name, which returns \"\" when the index is past the list end (line 178). canvas and _fren revert Missing() for the same out-of-range fields (lines 242, 249, 281), and tokenURI calls bmp before attributes, so through tokenURI the revert always wins. A direct caller of attributes (an off-chain indexer or a future consumer composing its own JSON) gets syntactically valid metadata with empty values for Face 13-15, Character 3, Coat 3, Shirt 6-7, Hat 3 and Background 10-15, rather than an error. The vendored reference renderer (test/ref/FrenRendererRef.sol line 172) behaves identically, and the brief asks for the renderer exactly as it is, so this is recorded for the author's awareness, not as a required change.","line":127,"path":"src/FrenRenderer.sol","reproduction":"Call r.attributes(uint24(13 << 2)) on a deployed FrenRenderer: it returns `[{\"trait_type\":\"Character\",\"value\":\"Cyborg Pepe\"},{\"trait_type\":\"Face\",\"value\":\"\"},...]` with an empty Face value. Call r.canvas(uint24(13 << 2), 0) with the same combo: it reverts Missing(). Expected (if the two entry points are meant to agree): attributes reverts or the consumer never passes such a combo; actual: silent empty string.","severity":"info","snippet":"    function attributes(uint24 combo) public pure returns (string memory) {","title":"attributes() returns empty trait names for combos outside the art instead of reverting like canvas()"}],"hash":"8b1d6ba909a53dd374699f9c63f50ce76cd3e89f5e1c91ecb16c41baf58e49d8","nodeId":"9d1963df-abf7-4e47-9635-8e14df08a8d4","outcome":"completed","summary":"The findings file is in place and excluded by the harness's own exclude list, which is why the tree reads clean. Scratch files are gone. Nothing else is needed.\n\n## Review result\n\nI found no correctness or safety defect in the launch-3 control flow. The findings file holds one low and one info item, both with reproductions.\n\n**What I verified**\n\n- **Chunk hashes on mainnet.** All four static constructor addresses hold code whose keccak matches the index's hash table exactly (sizes 24001, 24139, 23479, 21187 bytes), fetched through a public RPC. The renderer's four static arguments are therefore correct for Ethereum.\n- **Read path.** The index decoding in `_entry`, the framed-chunk unframing arithmetic, the exact end-of-code boundary for chunk 7's palette, and every index into the tables and face table are bounded by the combo checks in `canvas` and `_fren`. Outputs match the reference renderer byte for byte in the existing tests.\n- **Fresh-chain deploy.** The constructor only stores addresses. With the earlier chunks absent, every read reverts BadArt, as the existing test shows.\n- **Compiler independence.** The hash check, admission scan and reference equality pass under solc 0.8.26, 0.8.30 and 0.8.37. The renderer embeds its constants as PUSH32 data, so the forbidden-opcode walk cannot land on hash bytes regardless of layout.\n- **Slither leads.** Divide-before-multiply is the intended frame arithmetic. The uninitialized locals are deliberate zeros. The missing zero checks are required by the fresh-chain deploy. None is a finding.\n\n**Findings**\n\n1. **Low.** pendingURI costs 23.9M to 26.95M gas across all 2222 ids, above the 2^24 per-transaction cap for every id and above 25M for 1990 ids. It can only be served by eth_call with a large allowance. The README documents the sample; this adds the full range and the transaction-cap consequence.\n2. **Info.** The public `attributes` function returns empty trait names for combos outside the art while `canvas` reverts. The reference behaves the same.\n\n**Coverage**\n\n| Entry point | Status | Reason |\n|---|---|---|\n| constructor | holds | stores seven addresses, reads nothing |\n| tokenURI | holds | worst case 10.93M gas, guarded by canvas |\n| pendingURI | finding 1 | gas above the transaction cap |\n| unrevealed | holds | combos generated in range |\n| attributes | finding 2 | empty names, matches reference |\n| bmp, canvas | holds | all field ranges checked before reads |\n| chunk getters, faceLayers, shadow | holds | constants and immutables |\n| FrenArtChunk5..7 | holds | STOP then data, hashes match the index |\n\nNot reached: the deployment service's real factory overhead against the gas ceiling, which only its simulation can confirm.","treeHash":null,"usage":{"cachedInputTokens":2107964,"inputTokens":642,"model":"claude-fable-5-1","outputTokens":44497,"runtime":"claude","turns":39,"wallClockMs":1199516}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"56e50117311155be","findings":[{"citation":"resolved","description":"Economic/integration limit, measured rather than a code defect: the README documents pendingURI(1) at about 25.5M gas and says a 25M eth_call allowance is insufficient and that the sample is not an upper bound. Sweeping every token id 1..2222 with a flat-memory staticcall (return data not copied) gives: minimum 23,900,016, maximum 26,929,219 at tokenId 1985, 1,965 ids above 25M, none above 30M. The documented sample pendingURI(1) measures 25,419,581 and still succeeds under a 26M cap, while tokenId 1985 does not. tokenURI worst case sampled over every character/face/hat/item on the heaviest background and over all backgrounds/window offsets is 10,928,964 (combo 7026397). The renderer is immutable and has no owner, so a consumer or RPC whose eth_call cap sits between 25M and 27M will show some unrevealed frens as broken metadata while the sample id works. The design (four stacked greyed frames, triple base64) is the requested one; this is a number to carry into the README's RPC guidance and the consumer's integration test, not a change to the art.","line":76,"path":"src/FrenRenderer.sol","reproduction":"Deploy FrenArtChunk1..7 and FrenRenderer(c1..c7) (as test/FrenRenderer.t.sol setUp does). Then: (bool ok,) = address(r).staticcall{gas: 26_000_000}(abi.encodeCall(FrenRenderer.pendingURI, (1985))) -> ok == false (out of gas); the same call with gas 27_000_000 -> ok == true; address(r).staticcall{gas: 26_000_000}(abi.encodeCall(FrenRenderer.pendingURI, (1))) -> ok == true. Expected per README: 'about 25.5M for pendingURI(1)'; actual collection maximum: 26,929,219 gas at tokenId 1985, measured with assembly staticcall(gas(), r, in, insz, 0, 0) and gas() before/after, --gas-limit raised for the 2,222-id loop.","severity":"info","snippet":"    function pendingURI(uint256 tokenId) external view returns (string memory) {","title":"pendingURI worst case across the collection is 26.93M gas (token 1985), above the documented 25.5M sample; 1,965 of 2,222 ids exceed 25M"},{"citation":"resolved","description":"Invariant note (one fren <-> one combo). canvas() reverts Missing for ch > 2, face >= 13, bg >= 10, coat > 2, shirt > 5 and _fren reverts for hat > 2, but bit 23 of the 24-bit combo is masked out by '& 15' on the item field (and by attributes()'s identical mask), so combo X and combo X | (1 << 23) pass validation and produce byte-identical tokenURI, canvas and attributes output. The renderer itself holds no state, so nothing is lost here; the consequence sits with any consumer that stores combos and treats distinct combo values as distinct frens (uniqueness checks, rarity counts): it could accept two tokens with different combos that are the same picture. The vendored reference renderer (test/ref/FrenRendererRef.sol) has the same behaviour, and the brief requires byte-for-byte equivalence with it, so rejecting bit 23 in this renderer would break test_SameAsTheLaunchRenderer unless the reference changes too; the actionable item is for the consumer to mask or reject bit 23 before storing a combo.","line":283,"path":"src/FrenRenderer.sol","reproduction":"With the renderer deployed as in test/FrenRenderer.t.sol setUp: uint24 c0 = 1779030 (character 2, face 5, eye 1, coat 1, shirt 1, hat 1, bg 6, item 3); uint24 c1 = c0 | (1 << 23) = 10167638. r.tokenURI(7, c0, 7) == r.tokenURI(7, c1, 7) (identical strings), r.canvas(c0, 7) == r.canvas(c1, 7), r.attributes(c0) == r.attributes(c1). Expected if combos are meant to be canonical: c1 reverts Missing like bg = 10 does (r.canvas(10 << 15, 0) reverts). Actual: both succeed with the same output.","severity":"info","snippet":"        uint256 item = (combo >> 19) & 15;","title":"Combo bit 23 is ignored: two distinct uint24 combos render the same fren and attributes, while every other out-of-range trait value is rejected"}],"hash":"92b18502b6cb762ec9c6fe5e7dfbd9e50060a7114badc0dd33ee286dee039983","nodeId":"1a554e75-5db0-49bb-b762-53d3b59dc6d5","outcome":"completed","summary":"The review is complete. `.imd-findings.json` holds two informational findings and no defects of low severity or higher. No tracked file was changed, and the scratch tests were removed.\n\n**Outcome.** Within the Economic Security, Invariant and Flow Gap guides, launch 3 as specified is sound. The renderer holds no funds, has no owner, makes no external calls other than code reads, and fails closed on any chunk whose code hash differs. The main economic risk for this kind of contract is a permanently broken dependency, and I verified that it does not exist here.\n\n**What I verified**\n- **Live mainnet chunks 1 to 4.** Over a public RPC, the four static addresses in the brief have exactly the code hashes and sizes in `FrenArtIndex.CHUNK_HASHES`. The mainnet fork test also passed against them.\n- **Art integrity.** I regenerated the index, framing flags, size and hash tables, and all seven chunk byte strings from the art data independently. All match the committed sources byte for byte. Every one of the 69 layers has well-formed run-length data, and every index entry lies inside its chunk.\n- **Exhaustive rendering.** Every character, face, eye, coat, shirt, hat and item value draws. All 10 backgrounds draw at all 1,369 window offsets. All 2,222 token ids render pendingURI. No valid input reverts.\n- **Launch 3 itself.** The protected admission test passed with the exact four initcodes and predicted CREATE2 addresses. The local Anvil receipt check used 14.18M gas against the 16.78M cap. A one-byte-different chunk is refused on every palette-bearing read.\n- **Slither leads.** All eleven were checked and rejected. The divide-before-multiply is intentional frame arithmetic verified by the reference tests. The uninitialized locals are intentional zeros. Missing zero-checks only make a misdeployed renderer fail closed with BadArt.\n\n**The two informational findings**\n- **pendingURI gas ceiling.** The collection-wide maximum is 26.93M gas at token 1985, so it fails under a 26M eth_call cap while the README's sample id 1 passes. 1,965 of 2,222 ids exceed 25M. None exceed 30M. This refines the README's documented integration limit rather than changing code.\n- **Combo bit 23 is ignored.** Combos 1779030 and 10167638 render identically with identical attributes, while other out-of-range trait values revert. The reference renderer behaves the same and byte equivalence is required, so this is a note for the consumer to mask bit 23 before storing combos.\n\n**Coverage.** All entry points traced and holding: constructor, tokenURI, pendingURI, unrevealed, attributes, bmp, canvas, and the chunk constructors. Nothing in my area was left unreached. Not verifiable here: the deployer's real factory overhead and policy gas ceiling, and the not-yet-deployed consumer contract.","treeHash":null,"usage":{"cachedInputTokens":1333957,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":36348,"runtime":"claude","turns":41,"wallClockMs":1303306}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"357c46e3781993d4","findings":[{"citation":"resolved","description":"Merged from audit_flow (low) and audit_economics (info): same root cause, one finding. pendingURI draws PENDING_FRAMES (4) random frens over the machine wall and Base64-encodes a 4x84x84 bitmap twice (the BMP inside the SVG, the SVG inside the JSON). Reproduced on this toolchain (forge 1.8.3, solc 0.8.26, via-IR, 200 runs) with a fresh-frame meter contract (assembly staticcall, no return-data copy) over every id 1..2222: minimum 23,900,005, maximum 26,929,208 at tokenId 1985, 1965 ids above 25,000,000, none above 27,000,000, none failing at 40M. pendingURI(1), the README's documented sample, costs 25,419,570, so it is not the upper bound. Consequences: (a) no on-chain caller can ever execute pendingURI inside a transaction, since every id exceeds Ethereum's 2^24 = 16,777,216 per-transaction cap (EIP-7825; README line 78 states the cap); (b) an RPC whose eth_call allowance sits between 25M and 27M serves the sample id but fails about 88% of unrevealed tokens, and a 26M allowance serves id 1 and fails id 1985; (c) a 30M allowance covers every id. tokenURI is unaffected: the sample tokenURI(1, 0xbd59c, 42) costs 9,003,013 and test_TokenURIIsCheapEnoughToRead passes. This is an integration limit, not a correctness defect: the renderer only reads, its output must stay byte-identical to the vendored reference (test_SameAsTheLaunchRenderer), and the brief deploys it exactly as it is, so no code change is implied. The actionable item is the README's RPC guidance (lines 35-38), which should carry the measured collection-wide ceiling (about 27M, worst id 1985) and state that a contract can never call pendingURI in a transaction, so the consumer's metadata path is eth_call only. Slither's divide-before-multiply (line 364-370, framed offset arithmetic) and uninitialized-local leads were checked and are not defects: the frame math is exact (off%32 + len <= frames*32 by construction) and the zero-initialised locals are intended.","line":76,"path":"src/FrenRenderer.sol","reproduction":"Deploy FrenArtChunk1..7 and FrenRenderer(c1..c7) locally as test/FrenRenderer.t.sol setUp does. From a separate meter contract run `ok := staticcall(cap, r, add(input, 32), mload(input), 0, 0)` with input = abi.encodeCall(FrenRenderer.pendingURI, (id)) and measure gasleft() before/after. Measured: id 1 with cap 16,777,216 -> ok=false; cap 25,000,000 -> ok=false; cap 26,000,000 -> ok=true, used 25,419,570. id 1985 with cap 26,000,000 -> ok=false (out of gas); cap 27,000,000 -> ok=true, used 26,929,208. Sweep id=1..2222 with cap 40,000,000 (forge --gas-limit 70000000000): failures 0, max 26,929,208 at id 1985, min 23,900,005, 1965 ids above 25M, 0 above 27M. Expected per README lines 35-38: about 25.5M for pendingURI(1) and 'not an upper bound'. Actual ceiling across the collection: 26,929,208; every id is above the 2^24 transaction gas cap.","severity":"low","snippet":"    function pendingURI(uint256 tokenId) external view returns (string memory) {","title":"pendingURI needs 23.9M-26.93M gas for every token id: above the 2^24 transaction cap for all ids, above 25M for 1965 of 2222, and the README's 25.5M sample (id 1) is not the ceiling"},{"citation":"resolved","description":"Merged from audit_math and audit_economics: same root cause, one finding. The documented combo layout covers bits 0-22 (character 0-1, face 2-5, eye 6-7, coat 8-9, shirt 10-12, hat 13-14, background 15-18, item 19-22). canvas (line 242, 249) and _fren (line 281) reject every out-of-art value inside those fields with Missing(), and testFuzz_InvalidTraitsRevertMissing covers them, but bit 23 of the uint24 is masked away by `(combo >> 19) & 15` (line 283) and by the identical mask in attributes (line 147) and is never checked. So two distinct uint24 combos produce the same fren. The renderer holds no state and nothing is mis-drawn; the vendored reference renderer (test/ref/FrenRendererRef.sol) behaves identically, and the brief requires byte-for-byte equivalence with it, so rejecting bit 23 here would break test_SameAsTheLaunchRenderer unless the reference changed too. Recorded so the consumer that assigns and stores combos (IMD6900Frens, unverified per README) masks or rejects bit 23 before treating distinct combo values as distinct frens (uniqueness or rarity counts). No change to this launch is implied.","line":242,"path":"src/FrenRenderer.sol","reproduction":"With FrenRenderer deployed over the seven chunks: r.tokenURI(1, 0x3AAD91, 42) == r.tokenURI(1, 0xBAAD91, 42) (second combo = first | 1 << 23), r.canvas(0x3AAD91, 42) == r.canvas(0xBAAD91, 42), r.attributes(0x3AAD91) == r.attributes(0xBAAD91); also r.tokenURI(7, 1779030, 7) == r.tokenURI(7, 10167638, 7). Contrast: r.canvas(uint24(10 << 15), 0) reverts Missing() (background 10), while r.canvas(uint24(1 << 23), 0) succeeds and equals r.canvas(0, 0). Expected, if the combo were canonical: the bit-23 combo reverts Missing like every other out-of-layout value; actual: it is accepted and renders the same picture as the combo without it. Verified in a scratch Foundry test on this tree (all assertions pass).","severity":"info","snippet":"        if (ch > 2 || face >= FACES || bg >= 10) revert Missing();","title":"Combo bit 23 is neither used nor rejected: combo X and X | (1 << 23) render byte-identical tokenURI, canvas and attributes while every other out-of-art field value reverts Missing"},{"citation":"resolved","description":"From audit_flow, reproduced. attributes is public pure and maps each combo field to a name via _name, which returns an empty string when the index is past the end of the list (line 178). canvas and _fren revert Missing() for the same out-of-range fields (lines 242, 249, 281), and tokenURI calls bmp before attributes so through tokenURI the revert always wins. A direct caller of attributes (an off-chain indexer, a consumer composing its own JSON) receives syntactically valid metadata with empty values for Face 13-15, Character 3, Coat 3, Shirt 6-7, Hat 3 and Background 10-15 rather than an error. The vendored reference renderer (test/ref/FrenRendererRef.sol line 218) behaves identically and the brief requires byte-equivalence, so this is for the author's and consumer's awareness, not a required change.","line":178,"path":"src/FrenRenderer.sol","reproduction":"With FrenRenderer deployed: r.attributes(uint24(13 << 2)) returns exactly `[{\"trait_type\":\"Character\",\"value\":\"Cyborg Pepe\"},{\"trait_type\":\"Face\",\"value\":\"\"},{\"trait_type\":\"Eye\",\"value\":\"Green\"},{\"trait_type\":\"Coat\",\"value\":\"White\"},{\"trait_type\":\"Shirt\",\"value\":\"Blue\"},{\"trait_type\":\"Hat\",\"value\":\"None\"},{\"trait_type\":\"Item\",\"value\":\"None\"},{\"trait_type\":\"Background\",\"value\":\"Matrix Green\"}]` (empty Face). r.canvas(uint24(13 << 2), 0) with the same combo reverts Missing(). Expected if the entry points are meant to agree: attributes reverts too; actual: silent empty string. Verified in a scratch Foundry test on this tree.","severity":"info","snippet":"        return \"\";","title":"attributes() returns an empty trait name for combos outside the art instead of reverting as canvas()/tokenURI do"}],"hash":"aa6a4c5f17bbafda860c9976bf7e4e2a3a36814a2e01192e3992b49032fa6746","nodeId":"10a84a2b-1914-4907-a4a1-4dc6bb38dc89","outcome":"completed","summary":"The findings file is in place and excluded from git by the harness, the scratch test is removed, and the working tree has no other changes. The review is complete.\n\n**Outcome.** Three findings, none above low, written to `.imd-findings.json`. No critical or high finding exists, so no proof test was attached. The launch is sound as specified.\n\n**Specialist findings, settled.**\n- **pendingURI gas (low, merged from audit_flow and audit_economics).** Reproduced with a fresh-frame meter over all 2222 ids on forge 1.8.3 / solc 0.8.26. Worst case is token 1985 at 26,929,208 gas, minimum 23,900,005, 1965 ids above 25M, none above 27M, and every id above the 2^24 transaction cap. The README's 25.5M sample is therefore not the ceiling. The only actionable item is the README's RPC guidance, since the output must stay byte-identical to the reference.\n- **Combo bit 23 ignored (info, merged from audit_math and audit_economics).** Reproduced: tokenURI, canvas and attributes are identical for a combo with and without bit 23, while background 10 reverts. Shared with the reference renderer, so the consumer must mask it, not this launch.\n- **attributes() returns empty names (info, audit_flow).** Reproduced: face index 13 yields an empty Face value while canvas reverts Missing. Same in the reference.\n\n**Static-analysis leads.** The divide-before-multiply in the framed read is exact frame arithmetic. The uninitialized locals are intentional zero defaults. The missing zero checks are by design and covered by the all-zero-addresses test.\n\n**My own pass, with evidence.**\n- The four static addresses in the manifest resolve on Ethereum to code hashes matching FrenArtIndex exactly. I checked them with cast against a public RPC, and the mainnet fork test drew every reference bitmap through them.\n- The local Anvil launch rehearsal reproduced 14,179,144 gas for the whole launch, under the 2^24 cap with a 2.6M margin. The deployer's own factory overhead and policy ceiling remain its check.\n- The manifest matches the brief in contract order, argument order and checksummed addresses. All 22 offline tests pass.\n- Entry points: constructor, tokenURI, pendingURI, unrevealed, bmp, canvas, attributes and the immutable getters all hold. The renderer has no storage, owner, payable path, external call or forbidden opcode, and every art read authenticates the full chunk code hash before copying. The framed read of chunk 7 is bounds-exact by construction and verified by the palette test at an unaligned offset.\n\n**Not verified.** The deployment service's exact gas ceiling and the intended consumer contract, which the README already marks as unverified.","treeHash":null,"usage":{"cachedInputTokens":844547,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":24434,"runtime":"claude","turns":32,"wallClockMs":602590}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"866bf60e68b081d9","findings":[{"citation":"resolved","description":"The test's gasleft() deltas around the ImdStyleArtLaunches calls do not include the cost forge charges for contract creation under the current toolchain (forge 1.8.3): a scratch test measuring `new FrenArtChunk5()` through an external call reports 24,616 gas for a 19,403-byte contract whose code-deposit cost alone is 3,880,600 gas. With -vv the suite logs launch 1 = 1,000,622, launch 2 = 957,524, launch 3 = 1,310,335 gas, while the two launches already on mainnet used 11,290,222 (tx 0x7b6503...012d, launch #819) and 10,569,418 (tx 0xcbee71...0903, launch #838) gas. Launch 3 deployed on a local anvil (prague) costs 4,321,217 + 4,761,604 + 2,014,840 + 3,098,917 = 14,196,578 gas across four transactions, i.e. about 14.2-14.6M in one factory transaction: roughly 85-87% of the 2^24 = 16,777,216 cap. The launch still fits, but the assertion `total < TX_CAP * 95 / 100` cannot fail for any plausible chunk size and therefore does not protect the guarantee the README states ('each of the three launches fits one transaction under EIP-7825's cap: about 10.7M, 10.0M and 13.9M gas'). The adapter should not rely on this test when checking launch 3 against the policy gas ceiling; use a real deployment simulation (anvil / the deployer's simulation) instead. No change to src/ is needed.","line":147,"path":"test/FrenRenderer.t.sol","reproduction":"Run `forge test --match-test test_LaunchesFitTransactions -vv` (also with --isolate): logs show 'launch 3 gas (with calldata): 1310335' and the test passes. Compare with `cast receipt 0x7b6503966cc85ffbf267a50625e47392554d6a30d47fa5bff884e0bc7110012d gasUsed` on Ethereum = 11290222 for launch 1, which the same test reports as 1000622. Expected: the measured total approximates the on-chain cost (~11.3M, ~10.6M, ~14.3M). Actual: ~1.0M, ~0.96M, ~1.3M, so a launch far above the cap would still pass.","severity":"low","snippet":"            uint256 total = launchGas[i] + 21_000 + 16 * launchInitBytes[i];","title":"test_LaunchesFitTransactions measures ~1/10 of the real launch gas, so its EIP-7825 guard is vacuous"},{"citation":"resolved","description":"Measured in a scratch test on the chunk renderer: pendingURI(1) = 25,486,903 gas (reference renderer: 25,307,608), tokenURI(1, 0xbd59c, 42) = 9,058,559 gas (reference: 9,021,984); the repo's own test logs tokenURI = 10,318,372 for combo 0x6a2a4d. Both functions are what the frens contract will return from its tokenURI, so marketplaces and wallets fetch them through eth_call. Several public RPC endpoints cap eth_call gas at 25-30M, so pendingURI (and tokenURI on providers with lower caps) can fail to render there even though nothing is wrong on chain. This matches the launch renderer byte for byte (test_SameAsTheLaunchRenderer) and is not a regression introduced by the chunk packing; it is reported so the team can confirm their metadata path's RPC gas limits before pointing the frens here.","line":76,"path":"src/FrenRenderer.sol","reproduction":"Deploy FrenArtChunk1..7 and FrenRenderer(c1..c7) locally; measure `g = gasleft(); r.pendingURI(1); g - gasleft()` -> 25,486,903. Expected by users of a 25M-gas eth_call endpoint: a data URI. Actual: out-of-gas revert on that endpoint; the same call succeeds with a 30M+ gas allowance.","severity":"info","snippet":"    function pendingURI(uint256 tokenId) external view returns (string memory) {","title":"pendingURI costs ~25.5M gas per eth_call; tokenURI ~9-10.3M"},{"citation":"resolved","description":"After commit 18a3cc9 the constructor only stores the seven addresses and every read checks the chunk's code hash in _entry (line 332). The contract-level NatSpec still describes the old behaviour, and script/art/derive_renderer.py (line 25) regenerates the same sentence, so re-running the script keeps the stale text. A reader (or the adapter) relying on the header would expect a wrong-address deployment to fail at construction; it does not: FrenRenderer(address(1), ..., address(7)) deploys fine and only reverts BadArt on the first read.","line":10,"path":"src/FrenRenderer.sol","reproduction":"new FrenRenderer(address(1), address(2), address(3), address(4), address(5), address(6), address(7)) succeeds (see test_DeploysWithoutTheEarlierChunks, which deploys against mainnet addresses that do not exist on the test chain). Expected per the NatSpec: constructor reverts. Actual: deploys; `tokenURI(1, 0, 0)` then reverts BadArt.","severity":"info","snippet":"///         its code hash when this is deployed:","title":"Renderer NatSpec says chunks are checked 'when this is deployed'; the constructor checks nothing"},{"citation":"resolved","description":"FrenRenderer reads INDEX, CHUNK_HASHES, FRAMED, TABLES, FACE_TABLE, FACE_LAYERS and SHADOW; nothing reads CHUNK_SIZES (grep over src/ finds only its definition). The code-hash check in _entry subsumes a size check, so this is not a security issue, but the generated comment is misleading documentation of what the renderer enforces. Values for reference: 0x5dc1=24001, 0x5e4b=24139, 0x5bb7=23479, 0x52c3=21187, 0x4bcb=19403, 0x539d=21405, 0x22f0=8944, which do match the mainnet code sizes of chunks 1-4 and the local chunks 5-7.","line":22,"path":"src/FrenArtIndex.sol","reproduction":"`grep -n CHUNK_SIZES src/*.sol` returns only src/FrenArtIndex.sol:23. Expected per the comment: a read path in FrenRenderer comparing p.code.length to the entry. Actual: none; a chunk of the wrong size is rejected only through the codehash comparison at src/FrenRenderer.sol:332.","severity":"info","snippet":"    /// @dev each chunk's code size, 2 bytes big-endian: the renderer checks the chunks it's given","title":"FrenArtIndex.CHUNK_SIZES is unused; its comment claims the renderer checks it"},{"citation":"resolved","description":"The protected harness (Contracts.protected.t.sol, test_applicationRuntimeIsPresentBoundedAndHasNoEscapeOpcodes) runs the forbidden-opcode scan over every application in the launch, including FrenRenderer's runtime, while the repo test only loops over the seven chunks. FrenRenderer's runtime compiled with solc 0.8.26 / via-ir / 200 runs is 14,096 bytes and currently scans clean (its 8 raw 0xf4, 12 raw 0xf2 and 63 raw 0xff bytes all sit inside PUSH data), so the launch is not blocked today. Because the renderer's bytes change with any compiler or settings change, and the scan's result depends on PUSH alignment, the repository has no test that would catch a future renderer build that the admission scan refuses, as happened with chunks 4 and 7 (launch #820).","line":181,"path":"test/FrenRenderer.t.sol","reproduction":"Read out/FrenRenderer.sol/FrenRenderer.json deployedBytecode and run the protected scan: 0 hits at 14,096 bytes with solc 0.8.26. Running `forge test --match-test test_ChunksPassTheAdmissionScan` scans only chunks[0..6]; replacing FrenRenderer with a build containing a stray 0xff at an instruction boundary would still pass this test and be parked by admission.","severity":"info","snippet":"            bytes memory code = chunks[k].code;","title":"test_ChunksPassTheAdmissionScan does not scan FrenRenderer, which the admission scan also checks"},{"citation":"resolved","description":"On 2026-10-07 eth_getCode for 0x69004fEd3d8a34FFA952d15A128f74D8340fa79d returns 0x on Ethereum mainnet (nonce 0, balance 0), and also on Base and Arbitrum One. The consumer of this renderer, and therefore the exact interface it will be called through (tokenURI(uint256,uint24,uint256) and pendingURI(uint256), selectors 0x12b57c38 and 0x973c89a7), could not be verified against a deployed contract; only the vendored test/ref/FrenRendererRef.sol copy was available. If the frens are deployed later at a different address or with a different renderer interface, this renderer cannot be adjusted (no owner, nothing configurable), so the team should confirm the frens contract's renderer interface before launch 3.","line":25,"path":"README.md","reproduction":"`cast code --rpc-url https://ethereum-rpc.publicnode.com 0x69004fEd3d8a34FFA952d15A128f74D8340fa79d` -> 0x; `cast nonce` -> 0. Expected per README: the IMD6900Frens contract code. Actual: empty account.","severity":"info","snippet":"The frens contract (`IMD6900Frens`, 0x69004fEd3d8a34FFA952d15A128f74D8340fa79d on Ethereum) draws through whatever","title":"README names IMD6900Frens at 0x69004fEd...fa79d 'on Ethereum', but no code exists there"},{"citation":"resolved","description":"Read in full: src/FrenRenderer.sol, src/FrenArtIndex.sol, src/FrenArtChunks.sol (structure, constructors and generator script/art/chunks.py; the 328 KB of hex art data was verified by behaviour, not by eye), script/art/derive_renderer.py, test/FrenRenderer.t.sol, test/OnChainChunks.fork.t.sol, test/OnChainRenderer.fork.t.sol, test/ref/FrenRendererRef.sol (interface and read path), README.md, foundry.toml, and the pinned Contracts.protected.t.sol and Pashov guide. Verified: (1) `forge test` offline, 8/8 pass; (2) mainnet code of chunks 1-4 at the brief's addresses: sizes 24001/24139/23479/21187 and keccak256 equal to FrenArtIndex.CHUNK_HASHES entries 0-3; (3) test/OnChainChunks.fork.t.sol passes against a public mainnet RPC; (4) a scratch test comparing canvas() for all 39 character/face layers, all 16 item values, all 3 coats, 3 hats, 4 eyes, 6 shirts, 10 backgrounds and 40 seeds, plus pendingURI for 22 token ids, byte for byte against FrenRendererRef: all equal; (5) FrenRenderer runtime (14,096 bytes, solc 0.8.26) passes the protected forbidden-opcode scan; it contains no DELEGATECALL/CALLCODE/SELFDESTRUCT, no owner, no storage writes, no payable or state-changing functions, no proxy or initializer; (6) the constructor only stores addresses and deploys on a chain without chunks 1-4; (7) every read path goes through _entry, whose codehash check rejects swapped or foreign chunks (test_DrawsOnlyThisArt) and whose framed-read arithmetic for chunks 4 and 7 (first = off/32, frames = (off+len-1)/32 - first + 1, code length 1 + frames*33) is consistent with the generator. Index bounds: canvas/_fren bound character<=2, face<13, coat<=2, shirt<=5, hat<=2, item<=15, bg<=9, so _entry is only called with i in 0..69 (69 = palette). Not reachable: the IMD6900Frens contract (no code at the README address on any chain checked), the IMD factory/policy gas ceiling for launch 3 (the deployer simulates it; local estimate 14.2-14.6M gas), and compiler builds other than solc 0.8.26. No funds, custody or access-control surfaces exist in these contracts, so the economic, access-control and reentrancy passes of the guide found nothing applicable.","line":25,"path":"src/FrenRenderer.sol","reproduction":"n/a: coverage statement. Commands used: `forge test`; `MAINNET_RPC_URL=https://ethereum-rpc.publicnode.com forge test --match-path test/OnChainChunks.fork.t.sol`; `cast code`/`cast keccak` for the four chunk addresses; a scratch Foundry test iterating every layer against FrenRendererRef (removed after the run).","severity":"info","snippet":"contract FrenRenderer {","title":"Review coverage: what was read, verified and not reachable"}],"hash":"efe7bcf9cf450bffb437b93c0b2feb44293750815bc13423c1fe830c0b3a58db","nodeId":"3ad923ec-85d0-4af6-bc86-b89e42046c3f","outcome":"completed","summary":"The review is complete. The findings are in `.imd-findings.json` at the repository root, no source files were changed, and my scratch tests were removed.\n\n**Verdict: no defects in the contracts themselves.** FrenRenderer and the chunks behave as the brief describes. I found one low-severity test problem and several informational notes.\n\n**What I verified**\n\n- The offline suite passes, 8 of 8 tests.\n- Chunks 1 to 4 on Ethereum mainnet at the brief's addresses have exactly the code sizes and keccak256 hashes that FrenArtIndex stores. The mainnet fork test passes against a public RPC.\n- A scratch test compared `canvas` and `pendingURI` against the vendored reference renderer across every one of the 69 layers, every trait value and 40 seeds. All output was byte for byte identical.\n- FrenRenderer's runtime passes the protected forbidden-opcode scan. It has no owner, no storage writes, no state-changing functions, no proxy, initializer, DELEGATECALL, CALLCODE or SELFDESTRUCT.\n- The constructor only stores addresses and deploys on a chain without the earlier chunks. Every read goes through the code-hash check, and the framed-read arithmetic for chunks 4 and 7 matches the generator.\n\n**Findings written**\n\n- **Low:** test_LaunchesFitTransactions is vacuous under forge 1.8.3. It logs about 1.3M gas for launch 3, while a local deployment costs about 14.2M and the two mainnet launches used 11.3M and 10.6M. Launch 3 still fits the 16.8M cap at roughly 85 to 87 percent, but the test cannot catch an overflow of it.\n- **Info:** pendingURI costs about 25.5M gas per eth_call and tokenURI 9 to 10.3M, which some RPC endpoints cap below.\n- **Info:** the renderer's NatSpec and the CHUNK_SIZES comment describe checks the code no longer performs, and the derive script regenerates the stale text.\n- **Info:** the repo's admission-scan test skips FrenRenderer, which the protected harness does scan.\n- **Info:** the README names the IMD6900Frens contract at an address with no code on mainnet, Base or Arbitrum, so the consumer interface could not be verified on chain.\n- **Info:** a coverage statement listing what was read, what was verified, and what was unreachable: the frens contract, the launch policy gas ceiling, and compiler builds other than solc 0.8.26.","treeHash":null,"usage":{"cachedInputTokens":966574,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":30446,"runtime":"claude","turns":36,"wallClockMs":897208}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"45aa937328087de3","findings":[],"hash":"f0982afa903db0265f9a9986128f6d43b645863c239854a8c1a1a3edc9a3a7b0","nodeId":"71f3c138-a246-4082-af1b-adc125fd0c1a","outcome":"completed","summary":"The review is complete and the findings file holds an empty list. Nothing in the assigned area produced a substantiated defect.\n\n**What I checked and verified**\n\n- **Permission surface.** The three chunk contracts and the renderer have no owner, no role, no initializer, no `receive` or `fallback`, no `payable` function, no `msg.sender` read, and no DELEGATECALL or SELFDESTRUCT. Every renderer function is `view` or `pure`. The constructor only stores seven immutables and reads nothing, so the factory being `msg.sender` traps nothing. There is no writer of any storage slot after deployment, so the inconsistent-guard, escalation and unprotected-init checks from the Access Control guide have no target.\n- **Trust boundary on the constructor inputs.** The only trust the renderer places is in the seven addresses, and `_entry` at `src/FrenRenderer.sol:354` rejects any chunk whose full code hash is not the pinned one before copying a byte. A wrong, empty, swapped or copied-and-altered chunk reverts `BadArt` on every read. The local suite confirms this for swapped and stranger addresses and for a fresh chain where chunks 1–4 are absent.\n- **The four static mainnet addresses.** I fetched the code at all four from a public Ethereum RPC and hashed it. All four hashes match `FrenArtIndex.CHUNK_HASHES` byte for byte, with code sizes 24001, 24139, 23479 and 21187. I also ran the fork test against mainnet and it passed, so the renderer deployed with these arguments draws the reference art.\n- **Asymmetry.** The paired surfaces are revealed vs pending rendering, framed vs unframed chunk reads, and `canvas` vs `_fren` slot computation. Validation sits in `canvas` and `_fren` before any index read, and the unrevealed path generates only in-range combos. Both read branches bound their copy by the pinned hash and a length check. The byte-for-byte reference comparison tests pass for both URI paths.\n- **Static analysis leads.** The uninitialized locals are intentional zero starts, and the divide-before-multiply is the 32-to-33 frame alignment, which I checked satisfies `off % 32 + len <= frames * 32` for every offset and length. None is a defect.\n\n**Coverage rows**\n\n| Entry point | Result | Reason |\n|---|---|---|\n| FrenArtChunk5/6/7 constructor | holds | no arguments, returns fixed bytes, no sender dependency |\n| FrenArtChunk5/6/7 runtime | holds | STOP at pc 0, PUSH32 frames in chunk 7 never execute, admission scan passes |\n| FrenRenderer constructor | holds | stores seven immutables only, no reads, no owner |\n| tokenURI, pendingURI, unrevealed | holds | view only, every art read hash-checked |\n| bmp, canvas, attributes | holds | view or pure, inputs bounded before index reads |\n| chunk1..7, faceLayers, shadow getters | holds | immutables and constants |\n| receive / fallback | holds | none exist, value calls revert |\n\n**Notes that are not findings.** The length checks reverting `Missing` inside `_entry` are unreachable once the hash matches, which is harmless redundancy. The roughly 25.5M gas cost of `pendingURI` is already documented by the earlier audit and the README and is outside my area. The protected harness was not re-run here. Launch 3 has no manifest in the tree yet, so the `$contract` references could only be checked against the README recipe, which matches the brief.","treeHash":null,"usage":{"cachedInputTokens":571087,"inputTokens":226,"model":"claude-fable-5-1","outputTokens":14196,"runtime":"claude","turns":27,"wallClockMs":317950}}],"verification":[{"checks":[{"durationMs":33520,"exitCode":0,"name":"build","output":"Compiling 37 files with Solc 0.8.30\nSolc 0.8.30 finished in 33.36s\nCompiler run successful!\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/FrenRenderer.sol:354:33\n    │\n354 │         if (p.codehash != want) revert BadArt(); // exactly the art FrenArtIndex was generated from, or nothing\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/FrenRenderer.sol:357:48\n    │\n357 │             if (p.code.length < 1 + off + len) 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/FrenRenderer.sol:366:56\n    │\n366 │         if (p.code.length < 1 + (first + frames) * 33) 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/FrenRenderer.sol:281:22\n    │\n281 │         if (hat > 2) revert Missing();\n    │                      ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/FrenRenderer.sol:94:60\n   │\n94 │         _draw(back, _entry(faceLayers + 20 + PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, 0);\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/FrenRenderer.sol:94:80\n   │\n94 │         _draw(back, _entry(faceLayers + 20 + PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, 0);\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/FrenRenderer.sol:101:23\n    │\n101 │             _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[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/FrenRenderer.sol:168:54\n    │\n168 │                     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/FrenRenderer.sol:167:21\n    │\n167 │                 if (k == i) {\n    │                     ━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/FrenRenderer.sol:228:35\n    │\n228 │                 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/FrenRenderer.sol:258:50\n    │\n258 │         _draw(cv, _entry(faceLayers + 20 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), slot, 0);\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/FrenRenderer.sol:258:70\n    │\n258 │         _draw(cv, _entry(faceLayers + 20 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), slot, 0);\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/FrenRenderer.sol:278:79\n    │\n278 │         _draw(cv, _entry(uint8(FrenArtIndex.FACE_TABLE[ch * FACES + face])), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:278:92\n    │\n278 │         _draw(cv, _entry(uint8(FrenArtIndex.FACE_TABLE[ch * FACES + face])), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:279:36\n    │\n279 │         _draw(cv, _entry(f + ch), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:279:49\n    │\n279 │         _draw(cv, _entry(f + ch), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:282:55\n    │\n282 │         if (hat != 0) _draw(cv, _entry(f + 2 + hat), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:282:68\n    │\n282 │         if (hat != 0) _draw(cv, _entry(f + 2 + hat), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:284:57\n    │\n284 │         if (item != 0) _draw(cv, _entry(f + 4 + item), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:284:70\n    │\n284 │         if (item != 0) _draw(cv, _entry(f + 4 + item), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:298:25\n    │\n298 │             int256 yy = int256(y) + dy;\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/FrenRenderer.sol:299:40\n    │\n299 │             bool row = yy >= 0 && yy < int256(N);\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/FrenRenderer.sol:307:32\n    │\n307 │                     int256 a = int256(x) + dx;\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/FrenRenderer.sol:308:36\n    │\n308 │                     int256 b = a + int256(n);\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/FrenRenderer.sol:310:29\n    │\n310 │                     if (b > int256(N)) b = int256(N);\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/FrenRenderer.sol:310:44\n    │\n310 │                     if (b > int256(N)) b = int256(N);\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/FrenRenderer.sol:311:36\n    │\n311 │                     uint256 base = uint256(yy) * N;\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/FrenRenderer.sol:313:35\n    │\n313 │                         cv[base + uint256(xx)] = bytes1(uint8(c));\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/FrenRenderer.sol:313:57\n    │\n313 │                         cv[base + uint256(xx)] = bytes1(uint8(c));\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/FrenRenderer.sol:330:32\n    │\n330 │             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/FrenRenderer.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\n","passed":true},{"durationMs":2036,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/OnChainChunks.fork.t.sol:OnChainChunksForkTest\n[SKIP] test_swarmChunksDrawTheReference() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 392.41µs (153.25µs CPU time)\n\nRan 1 test for test/OnChainRenderer.fork.t.sol:OnChainRendererForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 444.12µs (0.00ns CPU time)\n\nRan 9 tests for test/FrenRenderer.t.sol:FrenRendererTest\n[PASS] test_ChunksPassTheAdmissionScan() (gas: 227222821)\n[PASS] test_DeploysWithoutTheEarlierChunks() (gas: 89210273)\n[PASS] test_DrawsOnlyThisArt() (gas: 18853827)\n[PASS] test_MatchesTheReference() (gas: 38350839)\n[PASS] test_MetadataFitsThirtyMillionGas() (gas: 69385870)\nLogs:\n  tokenURI sample gas: 9010095\n  pendingURI sample gas: 25444895\n\n[PASS] test_RejectsCombosOutsideTheArt() (gas: 11172205)\n[PASS] test_RendererPassesTheAdmissionScan() (gas: 6154680)\n[PASS] test_SameAsTheLaunchRenderer() (gas: 1039369535)\n[PASS] test_TokenURIIsCheapEnoughToRead() (gas: 10324401)\nLogs:\n  tokenURI gas: 10318372\n\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 1.88s (2.59s CPU time)\n\nRan 3 test suites in 1.88s (1.88s CPU time): 9 tests passed, 0 failed, 2 skipped (11 total tests)\n","passed":true},{"durationMs":108,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[],\"files\":{\".gitignore\":4,\"ADAPTATION.md\":58,\"README.md\":90,\"foundry.toml\":22,\"launch.json\":30,\"script/art/chunks.py\":120,\"script/art/data/expected.json\":32,\"script/art/data/facetable.bin\":2,\"script/art/data/layers/bg0.bin\":59,\"script/art/data/layers/bg1.bin\":44,\"script/art/data/layers/bg2.bin\":44,\"script/art/data/layers/bg3.bin\":4,\"script/art/data/layers/bg4.bin\":2,\"script/art/data/layers/bg5.bin\":1,\"script/art/data/layers/bg6.bin\":6,\"script/art/data/layers/bg7.bin\":13,\"script/art/data/layers/bg8.bin\":20,\"script/art/data/layers/bg9.bin\":4,\"script/art/data/layers/coat0.bin\":10,\"script/art/data/layers/coat1.bin\":10,\"script/art/data/layers/coat2.bin\":10,\"script/art/data/layers/face0.bin\":36,\"script/art/data/layers/face1.bin\":29,\"script/art/data/layers/face10.bin\":34,\"script/art/data/layers/face11.bin\":33,\"script/art/data/layers/face12.bin\":36,\"script/art/data/layers/face13.bin\":19,\"script/art/data/layers/face14.bin\":20,\"script/art/data/layers/face15.bin\":20,\"script/art/data/layers/face16.bin\":19,\"script/art/data/layers/face17.bin\":20,\"script/art/data/layers/face18.bin\":19,\"script/art/data/layers/face19.bin\":21,\"script/art/data/layers/face2.bin\":34,\"script/art/data/layers/face20.bin\":20,\"script/art/data/layers/face21.bin\":19,\"script/art/data/layers/face22.bin\":19,\"script/art/data/layers/face23.bin\":20,\"script/art/data/layers/face24.bin\":20,\"script/art/data/layers/face25.bin\":19,\"script/art/data/layers/face26.bin\":23,\"script/art/data/layers/face27.bin\":23,\"script/art/data/layers/face28.bin\":23,\"script/art/data/layers/face29.bin\":23,\"script/art/data/layers/face3.bin\":31,\"script/art/data/layers/face30.bin\":23,\"script/art/data/layers/face31.bin\":23,\"script/art/data/layers/face32.bin\":26,\"script/art/data/layers/face33.bin\":24,\"script/art/data/layers/face34.bin\":23,\"script/art/data/layers/face35.bin\":22,\"script/art/data/layers/face36.bin\":22,\"script/art/data/layers/face37.bin\":24,\"script/art/data/layers/face38.bin\":22,\"script/art/data/layers/face4.bin\":31,\"script/art/data/layers/face5.bin\":29,\"script/art/data/layers/face6.bin\":29,\"script/art/data/layers/face7.bin\":28,\"script/art/data/layers/face8.bin\":29,\"script/art/data/layers/face9.bin\":30,\"script/art/data/layers/hat0.bin\":3,\"script/art/data/layers/hat1.bin\":5,\"script/art/data/layers/item0.bin\":2,\"script/art/data/layers/item1.bin\":1,\"script/art/data/layers/item10.bin\":1,\"script/art/data/layers/item11.bin\":1,\"script/art/data/layers/item12.bin\":1,\"script/art/data/layers/item13.bin\":1,\"script/art/data/layers/item14.bin\":3,\"script/art/data/layers/item2.bin\":3,\"script/art/data/layers/item3.bin\":1,\"script/art/data/layers/item4.bin\":1,\"script/art/data/layers/item5.bin\":2,\"script/art/data/layers/item6.bin\":3,\"script/art/data/layers/item7.bin\":1,\"script/art/data/layers/item8.bin\":1,\"script/art/data/layers/item9.bin\":1,\"script/art/data/manifest.json\":329,\"script/art/data/palette.bin\":7,\"script/art/data/tables.bin\":2,\"script/art/derive_renderer.py\":115,\"script/check_launch_gas.py\":135,\"src/FrenArtChunks.sol\":2313,\"src/FrenArtIndex.sol\":34,\"src/FrenRenderer.sol\":377,\"test/FrenRenderer.t.sol\":263,\"test/LaunchGasFactory.sol\":20,\"test/OnChainChunks.fork.t.sol\":36,\"test/OnChainRenderer.fork.t.sol\":79,\"test/ref/FrenRendererRef.sol\":369},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"050b5d8f2c18bbd7e8fd6cb1ce52fea5d02538b23ab4c88a15cf39f843411a06","verifiedTreeHash":"7c1071d8cbfa0e44c43d763aae1929d22b5e0a2c","verifierVersion":"0.1.0+7471272e"},{"checks":[{"durationMs":39073,"exitCode":0,"name":"build","output":"Compiling 37 files with Solc 0.8.30\nSolc 0.8.30 finished in 38.94s\nCompiler run successful!\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/FrenRenderer.sol:354:33\n    │\n354 │         if (p.codehash != want) revert BadArt(); // exactly the art FrenArtIndex was generated from, or nothing\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/FrenRenderer.sol:357:48\n    │\n357 │             if (p.code.length < 1 + off + len) 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/FrenRenderer.sol:366:56\n    │\n366 │         if (p.code.length < 1 + (first + frames) * 33) 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/FrenRenderer.sol:281:22\n    │\n281 │         if (hat > 2) revert Missing();\n    │                      ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/FrenRenderer.sol:94:60\n   │\n94 │         _draw(back, _entry(faceLayers + 20 + PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, 0);\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/FrenRenderer.sol:94:80\n   │\n94 │         _draw(back, _entry(faceLayers + 20 + PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, 0);\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/FrenRenderer.sol:101:23\n    │\n101 │             _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[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/FrenRenderer.sol:167:21\n    │\n167 │                 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/FrenRenderer.sol:168:54\n    │\n168 │                     bytes memory out = new bytes(j - start);\n    │                                                      ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/FrenRenderer.sol:228:35\n    │\n228 │                 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/FrenRenderer.sol:258:50\n    │\n258 │         _draw(cv, _entry(faceLayers + 20 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), slot, 0);\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/FrenRenderer.sol:258:70\n    │\n258 │         _draw(cv, _entry(faceLayers + 20 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), slot, 0);\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/FrenRenderer.sol:278:79\n    │\n278 │         _draw(cv, _entry(uint8(FrenArtIndex.FACE_TABLE[ch * FACES + face])), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:278:92\n    │\n278 │         _draw(cv, _entry(uint8(FrenArtIndex.FACE_TABLE[ch * FACES + face])), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:279:36\n    │\n279 │         _draw(cv, _entry(f + ch), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:279:49\n    │\n279 │         _draw(cv, _entry(f + ch), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:282:55\n    │\n282 │         if (hat != 0) _draw(cv, _entry(f + 2 + hat), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:282:68\n    │\n282 │         if (hat != 0) _draw(cv, _entry(f + 2 + hat), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:284:57\n    │\n284 │         if (item != 0) _draw(cv, _entry(f + 4 + item), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:284:70\n    │\n284 │         if (item != 0) _draw(cv, _entry(f + 4 + item), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:298:25\n    │\n298 │             int256 yy = int256(y) + dy;\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/FrenRenderer.sol:299:40\n    │\n299 │             bool row = yy >= 0 && yy < int256(N);\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/FrenRenderer.sol:307:32\n    │\n307 │                     int256 a = int256(x) + dx;\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/FrenRenderer.sol:308:36\n    │\n308 │                     int256 b = a + int256(n);\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/FrenRenderer.sol:310:29\n    │\n310 │                     if (b > int256(N)) b = int256(N);\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/FrenRenderer.sol:310:44\n    │\n310 │                     if (b > int256(N)) b = int256(N);\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/FrenRenderer.sol:311:36\n    │\n311 │                     uint256 base = uint256(yy) * N;\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/FrenRenderer.sol:313:35\n    │\n313 │                         cv[base + uint256(xx)] = bytes1(uint8(c));\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/FrenRenderer.sol:313:57\n    │\n313 │                         cv[base + uint256(xx)] = bytes1(uint8(c));\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/FrenRenderer.sol:330:32\n    │\n330 │             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/FrenRenderer.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\n","passed":true},{"durationMs":1990,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/OnChainChunks.fork.t.sol:OnChainChunksForkTest\n[SKIP] test_swarmChunksDrawTheReference() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 248.64µs (98.52µs CPU time)\n\nRan 1 test for test/OnChainRenderer.fork.t.sol:OnChainRendererForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.37ms (0.00ns CPU time)\n\nRan 9 tests for test/FrenRenderer.t.sol:FrenRendererTest\n[PASS] test_ChunksPassTheAdmissionScan() (gas: 227222821)\n[PASS] test_DeploysWithoutTheEarlierChunks() (gas: 89210273)\n[PASS] test_DrawsOnlyThisArt() (gas: 18853827)\n[PASS] test_MatchesTheReference() (gas: 38350839)\n[PASS] test_MetadataFitsThirtyMillionGas() (gas: 69385870)\nLogs:\n  tokenURI sample gas: 9010095\n  pendingURI sample gas: 25444895\n\n[PASS] test_RejectsCombosOutsideTheArt() (gas: 11172205)\n[PASS] test_RendererPassesTheAdmissionScan() (gas: 6154680)\n[PASS] test_SameAsTheLaunchRenderer() (gas: 1039369535)\n[PASS] test_TokenURIIsCheapEnoughToRead() (gas: 10324401)\nLogs:\n  tokenURI gas: 10318372\n\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 1.89s (2.53s CPU time)\n\nRan 3 test suites in 1.89s (1.89s CPU time): 9 tests passed, 0 failed, 2 skipped (11 total tests)\n","passed":true},{"durationMs":66,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[],\"files\":{\".gitignore\":4,\"ADAPTATION.md\":58,\"README.md\":90,\"foundry.toml\":22,\"script/art/chunks.py\":120,\"script/art/data/expected.json\":32,\"script/art/data/facetable.bin\":2,\"script/art/data/layers/bg0.bin\":59,\"script/art/data/layers/bg1.bin\":44,\"script/art/data/layers/bg2.bin\":44,\"script/art/data/layers/bg3.bin\":4,\"script/art/data/layers/bg4.bin\":2,\"script/art/data/layers/bg5.bin\":1,\"script/art/data/layers/bg6.bin\":6,\"script/art/data/layers/bg7.bin\":13,\"script/art/data/layers/bg8.bin\":20,\"script/art/data/layers/bg9.bin\":4,\"script/art/data/layers/coat0.bin\":10,\"script/art/data/layers/coat1.bin\":10,\"script/art/data/layers/coat2.bin\":10,\"script/art/data/layers/face0.bin\":36,\"script/art/data/layers/face1.bin\":29,\"script/art/data/layers/face10.bin\":34,\"script/art/data/layers/face11.bin\":33,\"script/art/data/layers/face12.bin\":36,\"script/art/data/layers/face13.bin\":19,\"script/art/data/layers/face14.bin\":20,\"script/art/data/layers/face15.bin\":20,\"script/art/data/layers/face16.bin\":19,\"script/art/data/layers/face17.bin\":20,\"script/art/data/layers/face18.bin\":19,\"script/art/data/layers/face19.bin\":21,\"script/art/data/layers/face2.bin\":34,\"script/art/data/layers/face20.bin\":20,\"script/art/data/layers/face21.bin\":19,\"script/art/data/layers/face22.bin\":19,\"script/art/data/layers/face23.bin\":20,\"script/art/data/layers/face24.bin\":20,\"script/art/data/layers/face25.bin\":19,\"script/art/data/layers/face26.bin\":23,\"script/art/data/layers/face27.bin\":23,\"script/art/data/layers/face28.bin\":23,\"script/art/data/layers/face29.bin\":23,\"script/art/data/layers/face3.bin\":31,\"script/art/data/layers/face30.bin\":23,\"script/art/data/layers/face31.bin\":23,\"script/art/data/layers/face32.bin\":26,\"script/art/data/layers/face33.bin\":24,\"script/art/data/layers/face34.bin\":23,\"script/art/data/layers/face35.bin\":22,\"script/art/data/layers/face36.bin\":22,\"script/art/data/layers/face37.bin\":24,\"script/art/data/layers/face38.bin\":22,\"script/art/data/layers/face4.bin\":31,\"script/art/data/layers/face5.bin\":29,\"script/art/data/layers/face6.bin\":29,\"script/art/data/layers/face7.bin\":28,\"script/art/data/layers/face8.bin\":29,\"script/art/data/layers/face9.bin\":30,\"script/art/data/layers/hat0.bin\":3,\"script/art/data/layers/hat1.bin\":5,\"script/art/data/layers/item0.bin\":2,\"script/art/data/layers/item1.bin\":1,\"script/art/data/layers/item10.bin\":1,\"script/art/data/layers/item11.bin\":1,\"script/art/data/layers/item12.bin\":1,\"script/art/data/layers/item13.bin\":1,\"script/art/data/layers/item14.bin\":3,\"script/art/data/layers/item2.bin\":3,\"script/art/data/layers/item3.bin\":1,\"script/art/data/layers/item4.bin\":1,\"script/art/data/layers/item5.bin\":2,\"script/art/data/layers/item6.bin\":3,\"script/art/data/layers/item7.bin\":1,\"script/art/data/layers/item8.bin\":1,\"script/art/data/layers/item9.bin\":1,\"script/art/data/manifest.json\":329,\"script/art/data/palette.bin\":7,\"script/art/data/tables.bin\":2,\"script/art/derive_renderer.py\":115,\"script/check_launch_gas.py\":135,\"src/FrenArtChunks.sol\":2313,\"src/FrenArtIndex.sol\":34,\"src/FrenRenderer.sol\":377,\"test/FrenRenderer.t.sol\":263,\"test/LaunchGasFactory.sol\":20,\"test/OnChainChunks.fork.t.sol\":36,\"test/OnChainRenderer.fork.t.sol\":79,\"test/ref/FrenRendererRef.sol\":369},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":36046,"exitCode":0,"name":"slither","output":"[medium/medium] divide-before-multiply at src/FrenRenderer.sol:337: FrenRenderer._entry(uint256) (src/FrenRenderer.sol#337-376) performs a multiplication on the result of a division:\n[medium/medium] uninitialized-local at src/FrenRenderer.sol:91: FrenRenderer.unrevealed(uint256).none (src/FrenRenderer.sol#91) is a local variable never initialized\n[medium/medium] uninitialized-local at src/FrenRenderer.sol:163: FrenRenderer._name(uint256,string).start (src/FrenRenderer.sol#163) is a local variable never initialized\n[medium/medium] uninitialized-local at src/FrenRenderer.sol:164: FrenRenderer._name(uint256,string).k (src/FrenRenderer.sol#164) is a local variable never initialized\n[low/medium] missing-zero-check at src/FrenRenderer.sol:55: FrenRenderer.constructor(address,address,address,address,address,address,address).c4 (src/FrenRenderer.sol#55) lacks a zero-check on :\n[low/medium] missing-zero-check at src/FrenRenderer.sol:55: FrenRenderer.constructor(address,address,address,address,address,address,address).c1 (src/FrenRenderer.sol#55) lacks a zero-check on :\n[low/medium] missing-zero-check at src/FrenRenderer.sol:55: FrenRenderer.constructor(address,address,address,address,address,address,address).c2 (src/FrenRenderer.sol#55) lacks a zero-check on :\n[low/medium] missing-zero-check at src/FrenRenderer.sol:55: FrenRenderer.constructor(address,address,address,address,address,address,address).c6 (src/FrenRenderer.sol#55) lacks a zero-check on :\n[low/medium] missing-zero-check at src/FrenRenderer.sol:55: FrenRenderer.constructor(address,address,address,address,address,address,address).c7 (src/FrenRenderer.sol#55) lacks a zero-check on :\n[low/medium] missing-zero-check at src/FrenRenderer.sol:55: FrenRenderer.constructor(address,address,address,address,address,address,address).c5 (src/FrenRenderer.sol#55) lacks a zero-check on :\n[low/medium] missing-zero-check at src/FrenRenderer.sol:55: FrenRenderer.constructor(address,address,address,address,address,address,address).c3 (src/FrenRenderer.sol#55) lacks a zero-check on :","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"83f58a003fe3d7213ac173fa3581c847503cec19196bfd4364303ce6e5d3a249","verifiedTreeHash":"e7e252def6ede9437f2872f209e496870a732cef","verifierVersion":"0.1.0+7471272e"},{"checks":[{"durationMs":37031,"exitCode":0,"name":"build","output":"Compiling 39 files with Solc 0.8.30\nSolc 0.8.30 finished in 36.91s\nCompiler run successful!\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/FrenRenderer.sol:354:33\n    │\n354 │         if (p.codehash != want) revert BadArt(); // exactly the art FrenArtIndex was generated from, or nothing\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/FrenRenderer.sol:357:48\n    │\n357 │             if (p.code.length < 1 + off + len) 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/FrenRenderer.sol:366:56\n    │\n366 │         if (p.code.length < 1 + (first + frames) * 33) 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/FrenRenderer.sol:281:22\n    │\n281 │         if (hat > 2) revert Missing();\n    │                      ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/FrenRenderer.sol:94:60\n   │\n94 │         _draw(back, _entry(faceLayers + 20 + PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, 0);\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/FrenRenderer.sol:94:80\n   │\n94 │         _draw(back, _entry(faceLayers + 20 + PENDING_BG), -int256(seed % 37), -int256((seed >> 8) % 37), none, 0);\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/FrenRenderer.sol:101:23\n    │\n101 │             _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[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/FrenRenderer.sol:168:54\n    │\n168 │                     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/FrenRenderer.sol:167:21\n    │\n167 │                 if (k == i) {\n    │                     ━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/FrenRenderer.sol:228:35\n    │\n228 │                 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/FrenRenderer.sol:258:50\n    │\n258 │         _draw(cv, _entry(faceLayers + 20 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), slot, 0);\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/FrenRenderer.sol:258:70\n    │\n258 │         _draw(cv, _entry(faceLayers + 20 + bg), -int256(seed % 37), -int256((seed >> 8) % 37), slot, 0);\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/FrenRenderer.sol:278:79\n    │\n278 │         _draw(cv, _entry(uint8(FrenArtIndex.FACE_TABLE[ch * FACES + face])), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:278:92\n    │\n278 │         _draw(cv, _entry(uint8(FrenArtIndex.FACE_TABLE[ch * FACES + face])), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:279:36\n    │\n279 │         _draw(cv, _entry(f + ch), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:279:49\n    │\n279 │         _draw(cv, _entry(f + ch), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:282:55\n    │\n282 │         if (hat != 0) _draw(cv, _entry(f + 2 + hat), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:282:68\n    │\n282 │         if (hat != 0) _draw(cv, _entry(f + 2 + hat), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:284:57\n    │\n284 │         if (item != 0) _draw(cv, _entry(f + 4 + item), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:284:70\n    │\n284 │         if (item != 0) _draw(cv, _entry(f + 4 + item), -int256(CX), -int256(CY), slot, 0);\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/FrenRenderer.sol:298:25\n    │\n298 │             int256 yy = int256(y) + dy;\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/FrenRenderer.sol:299:40\n    │\n299 │             bool row = yy >= 0 && yy < int256(N);\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/FrenRenderer.sol:307:32\n    │\n307 │                     int256 a = int256(x) + dx;\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/FrenRenderer.sol:308:36\n    │\n308 │                     int256 b = a + int256(n);\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/FrenRenderer.sol:310:29\n    │\n310 │                     if (b > int256(N)) b = int256(N);\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/FrenRenderer.sol:310:44\n    │\n310 │                     if (b > int256(N)) b = int256(N);\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/FrenRenderer.sol:311:36\n    │\n311 │                     uint256 base = uint256(yy) * N;\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/FrenRenderer.sol:313:35\n    │\n313 │                         cv[base + uint256(xx)] = bytes1(uint8(c));\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/FrenRenderer.sol:313:57\n    │\n313 │                         cv[base + uint256(xx)] = bytes1(uint8(c));\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/FrenRenderer.sol:330:32\n    │\n330 │             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/FrenRenderer.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\n","passed":true},{"durationMs":1756,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/OnChainChunks.fork.t.sol:OnChainChunksForkTest\n[SKIP] test_swarmChunksDrawTheReference() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 281.30µs (113.54µs CPU time)\n\nRan 1 test for test/OnChainRenderer.fork.t.sol:OnChainRendererForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 393.15µs (0.00ns CPU time)\n\nRan 3 tests for test/FrenArtCalls.invariant.t.sol:FrenArtCallsInvariantTest\n[PASS] invariant_TransfersAreConservedAndArtCannotChange() (runs: 128, calls: 6144, reverts: 0)\n\n╭--------------------+----------------------+-------+---------+----------╮\n| Contract           | Selector             | Calls | Reverts | Discards |\n+========================================================================+\n| FrenArtCallHandler | attemptConfiguration | 2047  | 0       | 0        |\n|--------------------+----------------------+-------+---------+----------|\n| FrenArtCallHandler | callChunk            | 2079  | 0       | 0        |\n|--------------------+----------------------+-------+---------+----------|\n| FrenArtCallHandler | sendToRenderer       | 2018  | 0       | 0        |\n╰--------------------+----------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 2\n  Bound result 2\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 204\n  Bound result 39\n  Bound result 0\n  Bound result 97295420806240020727\n  Bound result 2\n  Bound result 1\n  Bound result 2\n  Bound result 1046256597276361135\n  Bound result 0\n  Bound result 8\n  Bound result 1\n  Bound result 2\n  Bound result 62082664060247146927\n  Bound result 15\n  Bound result 0\n  Bound result 1\n  Bound result 2\n  Bound result 4234\n  Bound result 0\n  Bound result 3\n  Bound result 2\n  Bound result 7919\n  Bound result 2\n  Bound result 0\n  Bound result 166477\n  Bound result 40\n  Bound result 2\n  Bound result 52\n  Bound result 0\n  Bound result 0\n  Bound result 2\n  Bound result 1\n  Bound result 12379\n  Bound result 49\n  Bound result 0\n  Bound result 2\n  Bound result 193\n  Bound result 84\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 2\n  Bound result 3\n  Bound result 1\n  Bound result 2\n  Bound result 0\n  Bound result 2\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 2\n  Bound result 10747\n  Bound result 28\n  Bound result 1\n  Bound result 3\n  Bound result 1\n  Bound result 2\n  Bound result 33975440406521496096\n  Bound result 25\n  Bound result 0\n  Bound result 2\n  Bound result 11668\n  Bound result 95\n  Bound result 2\n  Bound result 3022911079367574561\n  Bound result 0\n  Bound result 0\n  Bound result 2\n  Bound result 1\n  Bound result 2089076379\n  Bound result 76\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 2\n  Bound result 42\n  Bound result 14\n  Bound result 2\n  Bound result 3\n  Bound result 1\n  Bound result 40548052783811154444\n  Bound result 1\n  Bound result 49152\n  Bound result 2\n  Bound result 2\n  Bound result 2\n  Bound result 9694\n  Bound result 1\n  Bound result 0\n  Bound result 26\n  Bound result 18\n  Bound result 1\n  Bound result 8944\n  Bound result 1\n  Bound result 0\n  Bound result 85095205951151468370\n  Bound result 91\n  Bound result 0\n  Bound result 3\n  Bound result 0\n  Bound result 2\n  Bound result 13677\n  Bound result 36\n  Bound result 2\n  Bound result 1\n  Bound result 22\n  Bound result 15\n  Bound result 0\n  Bound result 2\n  Bound result 54\n  Bound result 69\n  Bound result 1\n  Bound result 7334\n  Bound result 1\n  Bound result 2\n  Bound result 2222\n  Bound result 96\n  Bound result 0\n  Bound result 1\n  Bound result 2\n  Bound result 9676\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 13833\n  Bound result 2\n  Bound result 3120\n\n[PASS] test_LaunchConstructorsRejectValue() (gas: 1644330)\nLogs:\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 2\n  Bound result 2\n  Bound result 1\n  Bound result 0\n\n[PASS] test_ZeroOneWeiAndFullBalanceCalls() (gas: 1075519)\nLogs:\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 2\n  Bound result 2\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 96\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 99999999999999999998\n  Bound result 4\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 4\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 96\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 99999999999999999998\n  Bound result 4\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 4\n  Bound result 2\n  Bound result 2\n  Bound result 0\n  Bound result 0\n  Bound result 2\n  Bound result 2\n  Bound result 1\n  Bound result 96\n  Bound result 2\n  Bound result 1\n  Bound result 2\n  Bound result 1\n  Bound result 2\n  Bound result 2\n  Bound result 99999999999999999998\n  Bound result 4\n  Bound result 2\n  Bound result 2\n  Bound result 0\n  Bound result 4\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 672.29ms (671.13ms CPU time)\n\nRan 7 tests for test/FrenArtIntegrity.t.sol:FrenArtIntegrityTest\n[PASS] testFuzz_AnyChangedByteInvalidatesTheWholeChunk(uint256,uint256,uint8) (runs: 256, μ: 1544033, ~: 146535)\nLogs:\n  Bound result 5\n  Bound result 8376\n  Bound result 74\n\n[PASS] test_AllZeroAddressesDeployButArtReadsFail() (gas: 3295543)\n[PASS] test_EveryChunkRejectsMissingTruncatedExtendedOrSubstitutedCode() (gas: 93547818)\n[PASS] test_EveryEntryMatchesOriginalBinary() (gas: 2086438)\n[PASS] test_LaunchThreeRuntimeBytesAndFraming() (gas: 12960532)\n[PASS] test_PaletteIsCheckedEvenWhenCanvasDoesNotUseChunkSeven() (gas: 38373087)\n[PASS] test_StopFrameMarkersAndUnusedPaddingAreAuthenticated() (gas: 34647037)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 1.65s (632.32ms CPU time)\n\nRan 12 tests for test/FrenRenderer.t.sol:FrenRendererTest\n[PASS] testFuzz_InvalidTraitsRevertMissing(uint256,uint256,uint256,uint256) (runs: 128, μ: 4048951, ~: 47965)\nLogs:\n  Bound result 1\n  Bound result 14\n\n[PASS] testFuzz_ValidArtMatchesReference(uint256,uint256) (runs: 64, μ: 13557663, ~: 13351044)\n[PASS] test_ChunksPassTheAdmissionScan() (gas: 227257144)\n[PASS] test_DeploysWithoutTheEarlierChunks() (gas: 102797025)\n[PASS] test_DrawsOnlyThisArt() (gas: 18853827)\n[PASS] test_MatchesTheReference() (gas: 38350839)\n[PASS] test_MetadataFitsThirtyMillionGas() (gas: 69385870)\nLogs:\n  tokenURI sample gas: 9010095\n  pendingURI sample gas: 25444895\n\n[PASS] test_ReferenceAtTraitSeedAndTokenIdEdges() (gas: 277573114)\n[PASS] test_RejectsCombosOutsideTheArt() (gas: 11172652)\n[PASS] test_RendererPassesTheAdmissionScan() (gas: 6225129)\n[PASS] test_SameAsTheLaunchRenderer() (gas: 1039379045)\n[PASS] test_TokenURIIsCheapEnoughToRead() (gas: 10324401)\nLogs:\n  tokenURI gas: 10318372\n\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 1.65s (4.97s CPU time)\n\nRan 5 test suites in 1.66s (3.98s CPU time): 22 tests passed, 0 failed, 2 skipped (24 total tests)\n","passed":true},{"durationMs":77,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[],\"files\":{\".gitignore\":4,\"ADAPTATION.md\":58,\"README.md\":90,\"foundry.toml\":22,\"script/art/chunks.py\":120,\"script/art/data/expected.json\":32,\"script/art/data/facetable.bin\":2,\"script/art/data/layers/bg0.bin\":59,\"script/art/data/layers/bg1.bin\":44,\"script/art/data/layers/bg2.bin\":44,\"script/art/data/layers/bg3.bin\":4,\"script/art/data/layers/bg4.bin\":2,\"script/art/data/layers/bg5.bin\":1,\"script/art/data/layers/bg6.bin\":6,\"script/art/data/layers/bg7.bin\":13,\"script/art/data/layers/bg8.bin\":20,\"script/art/data/layers/bg9.bin\":4,\"script/art/data/layers/coat0.bin\":10,\"script/art/data/layers/coat1.bin\":10,\"script/art/data/layers/coat2.bin\":10,\"script/art/data/layers/face0.bin\":36,\"script/art/data/layers/face1.bin\":29,\"script/art/data/layers/face10.bin\":34,\"script/art/data/layers/face11.bin\":33,\"script/art/data/layers/face12.bin\":36,\"script/art/data/layers/face13.bin\":19,\"script/art/data/layers/face14.bin\":20,\"script/art/data/layers/face15.bin\":20,\"script/art/data/layers/face16.bin\":19,\"script/art/data/layers/face17.bin\":20,\"script/art/data/layers/face18.bin\":19,\"script/art/data/layers/face19.bin\":21,\"script/art/data/layers/face2.bin\":34,\"script/art/data/layers/face20.bin\":20,\"script/art/data/layers/face21.bin\":19,\"script/art/data/layers/face22.bin\":19,\"script/art/data/layers/face23.bin\":20,\"script/art/data/layers/face24.bin\":20,\"script/art/data/layers/face25.bin\":19,\"script/art/data/layers/face26.bin\":23,\"script/art/data/layers/face27.bin\":23,\"script/art/data/layers/face28.bin\":23,\"script/art/data/layers/face29.bin\":23,\"script/art/data/layers/face3.bin\":31,\"script/art/data/layers/face30.bin\":23,\"script/art/data/layers/face31.bin\":23,\"script/art/data/layers/face32.bin\":26,\"script/art/data/layers/face33.bin\":24,\"script/art/data/layers/face34.bin\":23,\"script/art/data/layers/face35.bin\":22,\"script/art/data/layers/face36.bin\":22,\"script/art/data/layers/face37.bin\":24,\"script/art/data/layers/face38.bin\":22,\"script/art/data/layers/face4.bin\":31,\"script/art/data/layers/face5.bin\":29,\"script/art/data/layers/face6.bin\":29,\"script/art/data/layers/face7.bin\":28,\"script/art/data/layers/face8.bin\":29,\"script/art/data/layers/face9.bin\":30,\"script/art/data/layers/hat0.bin\":3,\"script/art/data/layers/hat1.bin\":5,\"script/art/data/layers/item0.bin\":2,\"script/art/data/layers/item1.bin\":1,\"script/art/data/layers/item10.bin\":1,\"script/art/data/layers/item11.bin\":1,\"script/art/data/layers/item12.bin\":1,\"script/art/data/layers/item13.bin\":1,\"script/art/data/layers/item14.bin\":3,\"script/art/data/layers/item2.bin\":3,\"script/art/data/layers/item3.bin\":1,\"script/art/data/layers/item4.bin\":1,\"script/art/data/layers/item5.bin\":2,\"script/art/data/layers/item6.bin\":3,\"script/art/data/layers/item7.bin\":1,\"script/art/data/layers/item8.bin\":1,\"script/art/data/layers/item9.bin\":1,\"script/art/data/manifest.json\":329,\"script/art/data/palette.bin\":7,\"script/art/data/tables.bin\":2,\"script/art/derive_renderer.py\":115,\"script/check_launch_gas.py\":135,\"src/FrenArtChunks.sol\":2313,\"src/FrenArtIndex.sol\":34,\"src/FrenRenderer.sol\":377,\"test/FrenArtCalls.invariant.t.sol\":164,\"test/FrenArtIntegrity.t.sol\":173,\"test/FrenRenderer.t.sol\":305,\"test/LaunchGasFactory.sol\":20,\"test/OnChainChunks.fork.t.sol\":36,\"test/OnChainRenderer.fork.t.sol\":79,\"test/ref/FrenRendererRef.sol\":369},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"86cec4a167ab2d8fc2f138a762e2fb006d3fc7b9d18daa29af1d7dca2fbb02c5","verifiedTreeHash":"ccf264f79978d1a7077c3efb205acac2c7e6a0f9","verifierVersion":"0.1.0+7471272e"}]}