{"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":"0afb11c1-8f3d-44b3-837a-96cbd6064413","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"257a74c8f4500255dea506ced4bd5a743c3dc7c0595c08305854445e97f7f81e","dependsOn":["audit_imported_code"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","tools":[]},"key":"adapt_contract_project","kind":"code","role":"implement","skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","state":"accepted"},{"acceptedSubmissionHash":"fc3d2f5a946858d7bb2ee7f3c78bbacb727483151fc16904529dfb833effbb2c","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":"7528ff8d1e0a95fb9447211bce1641db78497472b9e9b1b49907d90aa1c4ab36","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":"823d5693abeec2b7f23babe6945555a72ee9e220a822019268beea7f0d0558ef","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":"6f46715267be48135cfab30af76634e9725ee9d7408a0d6366ad260662f1eab4","dependsOn":["adapt_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"5584fc687cc4492e53aee504829eb3064dbeb28f253f01c0015182509309ac3e","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":"d22c7490fdc898f5c6f33b3cae1112c97ab4541e1fb75b08659862a978330bb0","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":"73c81de342a5e84b3442a03733bba2dcde1806d7f81d9718c1b97f72bc42d177","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"13828115a745caa68cf99a2b4cd629827dcb1edb5b05c8586ac8f9b3600169bf","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"4c76766b00591dfe982fe85d3de589fdb2f810e5137c301155d1c5e36a1d3b23","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"4c76766b00591dfe982fe85d3de589fdb2f810e5137c301155d1c5e36a1d3b23","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Deploy the NFT, vault, liquidity contract and renderer from this repository on Sepolia, in this order, with no changes to their logic: (1) TestSeats from src/testnet/TestSeats.sol with no constructor arguments; (2) InkPortraits with constructor ($owner); (3) InkVault with constructor ($contract:InkPortraits, $contract:TestSeats, $owner); (4) InkLiquidity with constructor (the official Uniswap v4 PoolManager on this chain, the official Uniswap v4 PositionManager on this chain, $owner, 0xbc10b3662231c830d2ee10bc6359db17bea4b3d4); (5) InkRenderer with constructor ($contract:InkPortraits, $owner). Run the existing Foundry tests before deploying. The treasury argument (0xbc10b3662231c830d2ee10bc6359db17bea4b3d4) only receives IMD fees and is not an owner. Owner roles and royalties must go to $owner, the address in the owner field, never to the deploying wallet or any swarm address. Do not add any other owner, admin or upgrade role.","parentJobId":null,"planHash":"a99a5e4538f090645966f4ff85b3bfc15a59f0b1bc525ace1b70bda2b2e98fa0","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"0afb11c1-8f3d-44b3-837a-96cbd6064413","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2258-deploy-nft-vault-liquidity"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51459","feedbackHash":"36ee8f0977d6b66c7cb714af0172cf189e50c0b3690ce48ce67a150377558e00","nodeKey":"adapt_contract_project","submissionHash":"18db3d4316a7383ff698a1335333d4aab9510ac46ff2561c6ae9050419f5ef9e","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51083","feedbackHash":"9a935136fae1c7076f651220c16f21da8987bc8c324a6ed0e21fb7b4f1b98462","nodeKey":"adapt_contract_project","submissionHash":"257a74c8f4500255dea506ced4bd5a743c3dc7c0595c08305854445e97f7f81e","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"50959","feedbackHash":"0a41b2cddf011e9a1e84b081543e05654b02699c54ef3c6527fa97b12e501107","nodeKey":"audit_economics","submissionHash":"fc3d2f5a946858d7bb2ee7f3c78bbacb727483151fc16904529dfb833effbb2c","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51291","feedbackHash":"cd2f8ffe810da74116804f312f528cc0af862540092e8cdd9f49ce3cc70e0a9a","nodeKey":"audit_flow","submissionHash":"7528ff8d1e0a95fb9447211bce1641db78497472b9e9b1b49907d90aa1c4ab36","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51304","feedbackHash":"202d4c87427d8afb9112a052fdb2504243d946b53f66ee81f058d6dae202f84b","nodeKey":"audit_imported_code","submissionHash":"823d5693abeec2b7f23babe6945555a72ee9e220a822019268beea7f0d0558ef","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52707","feedbackHash":"6826bed5361fbb881b01ccff96eeb6fcd4f41e6d3c2d05b6fe11824e41884d22","nodeKey":"audit_judge","submissionHash":"6f46715267be48135cfab30af76634e9725ee9d7408a0d6366ad260662f1eab4","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52323","feedbackHash":"539d8d72381a739ee5635afdb0a4412fbc0e19d925fb7ad306b2e2a364bed32e","nodeKey":"audit_judge","submissionHash":"ea55121e8b01c85fca63e1bceb35360290c32363ee0cb3f72d58dc4e9bc9eaf4","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51515","feedbackHash":"628329d6d977a698c1d924a103f841a99c391255bb58af1933c899359b05b5f1","nodeKey":"audit_math","submissionHash":"5584fc687cc4492e53aee504829eb3064dbeb28f253f01c0015182509309ac3e","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52193","feedbackHash":"82ac8bb5103972e3e2859b218cd5c1edfd32d4673e1a628b1673835564de0fff","nodeKey":"audit_permissions","submissionHash":"d22c7490fdc898f5c6f33b3cae1112c97ab4541e1fb75b08659862a978330bb0","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52567","feedbackHash":"8ed099870906081e0334a2c813f50851a332a05cb936bb6df3832082dd58763e","nodeKey":"manifest","submissionHash":"812a00fbb79e3b541a216679a351aa6fcd58e19b5f3be94bafb180168e4aa399","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"54230","feedbackHash":"489d4a1b1dcb0ba33ac7f0ba3321352acebecc0d9dd7ad254318e22f15f0e253","nodeKey":"manifest","submissionHash":"73c81de342a5e84b3442a03733bba2dcde1806d7f81d9718c1b97f72bc42d177","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52583","feedbackHash":"667e8ae4c5031c04ae43ecc536607e61873ccac38d48a46dabe29cc1f7d61d1a","nodeKey":"write_foundry_tests","submissionHash":"c469c37b0faef2d14f844e70c7596fbc1f51bf8f573a9d0996219ff8248ca2a5","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52612","feedbackHash":"8b3b3af3f721ce11106f5bd3dff9c9ace77b81d0f3a0067f84c3c0a0ce82b194","nodeKey":"write_foundry_tests","submissionHash":"13828115a745caa68cf99a2b4cd629827dcb1edb5b05c8586ac8f9b3600169bf","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"dcd3454abcfc0c7778c447ddd75aa3bcd5a39a031bbea6d982353004542bf2db","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"dbcf12df40d81204d44743e35c4134992b78f134afceeefbd8aa2e751ad15388","device":"fbdcf3c80b280e3b","findings":[],"hash":"13828115a745caa68cf99a2b4cd629827dcb1edb5b05c8586ac8f9b3600169bf","nodeId":"1a6a9599-3116-4b77-8260-f67a3cab7ae4","outcome":"completed","summary":"No findings were listed for this round, so I left the accepted work alone. `forge build` succeeds, and `forge test` with the verifier's seed passes all 110 tests and fails none. The 28 skipped tests are the fork and snapshot suites that only run when an RPC URL is configured.\n\n**What I added:** the last source change gave each pull in `InkVault` a fixed admission cutoff (`pullBlock`, `waitingBefore`). The other contributor's unit tests cover it, but the random-sequence vault suite didn't check it. I added one invariant there:\n\n- **`test/VaultInvariants.t.sol`:**\n  - The handler now records the block each pull was made in.\n  - `invariant_pullCutoffFixed` checks that the vault keeps every pull. For the oldest unrevealed pulls (up to 64, plus the last revealed one), it checks that renewal never moves the cutoff and that the block whose hash picks the portrait is never earlier than it.\n  - It passes over 256 runs of 120 calls.\n- **`test/PROPERTY_COVERAGE.md`:** one sentence describing the new invariant.\n\nI found no defects, so there is no `.imd-findings.json`. Checks against the live Sepolia contracts still need to be done; they can't run offline. The changes are uncommitted.","treeHash":"2d1ec4da07af64ff0ae8134f1327149b0158445e","usage":{"cachedInputTokens":382840,"inputTokens":20,"model":"claude-opus-5-5","outputTokens":4770,"runtime":"claude","turns":11,"wallClockMs":377002}},{"artifacts":[],"attempt":1,"bundleHash":"e7711780663ef806cb073b64aab5a4c2d98b901564324c067b729325f2dfd057","device":"b5370e603753d2ab","findings":[],"hash":"18db3d4316a7383ff698a1335333d4aab9510ac46ff2561c6ae9050419f5ef9e","nodeId":"cbcd45a9-0c30-4e3b-a1c4-28fa85c7b885","outcome":"completed","summary":"Implemented four reproduced audit fixes and prepared the five-contract Sepolia deployment handoff, preserving constructors and ownership.\n\n- `forge build`: passed.\n- Seeded `forge test`: **71 passed, 0 failed, 28 skipped**.\n- Changes and audit dispositions: [ADAPTATION.md](/home/imd/.identitymd/work/0afb11c1-8f3d-44b3-837a-96cbd6064413/cbcd45a9-0c30-4e3b-a1c4-28fa85c7b885/ADAPTATION.md).\n\nNo transactions were broadcast; deployment remains IMD’s subsequent step. Live Sepolia verification was unavailable because the public RPC returned HTTP 403.","treeHash":"5103c176237603d9484b166dd8100fbab40acd7f","usage":{"cachedInputTokens":2438016,"inputTokens":132507,"model":"gpt-6-astra","outputTokens":26358,"runtime":"codex","turns":6,"wallClockMs":628065}},{"artifacts":[],"attempt":1,"bundleHash":"36d2c07f7dc9fa934e550d2fe44ba77c7f0e42503717cc18916f82da1c60c755","device":"dc7103e773ea194a","findings":[],"hash":"257a74c8f4500255dea506ced4bd5a743c3dc7c0595c08305854445e97f7f81e","nodeId":"cbcd45a9-0c30-4e3b-a1c4-28fa85c7b885","outcome":"completed","summary":"The revision is complete. The reviewer's medium finding reproduced and is fixed, the held proof now passes, and every finding is answered in the responses file.\n\n**What reproduced and what changed**\n\n- **Medium 4ca49345, fixed.** The proof failed on the accepted tree exactly as reported. Renewing the head pull passed the renewed block to admission, so hand-ins made after a queued pull's block joined that pull's draw. In `src/InkVault.sol` a renewal now records the pull's original block before moving its entropy block, and admission always uses the original block. The new `pullBlock` view exposes it. Since the cutoff no longer moves, a pull that counted a hand-in from its own block could never have been revealed, so `pull` now counts only hand-ins from earlier blocks through the new `waitingBefore` view. The draw rule, entropy, selection, batching and fees are unchanged.\n- **Low 71ef9819, partly addressed and otherwise disputed.** `setup` now rejects a liquidity address with no code while the owner can still correct it. The other suggested cross-checks would break the reviewer's own proof fixture, which only implements `addLiquidity`, and the wrong-token cases cannot be detected without a reference value the brief does not give.\n- **Low eb210c01 and info 9d091aab, disputed.** The hook is not deployed by this launch, and permissionless TestSeats minting is what the brief orders for Sepolia. No code changed.\n- **Info 6b948270, fixed.** The hook file was reformatted. The formatter check on src passes.\n\n**Tests and checks**\n\n- Three new regression tests cover the review scenario, repeated renewals with a pull queued behind, and the same-block rule with a full collection. A fourth covers the setup no-code check. The invariant handler and its queue-coverage invariant now use the stricter capacity rule.\n- Three fixtures that passed the ecrecover precompile as a never-called liquidity stand-in now pass a contract.\n- Full suite with the verifier's fuzz seed: 75 passed, 0 failed, 28 skipped (the skipped tests are the existing optional fork tests). The proof passes from a copy under test/scratch. Build and size check pass. The vault runtime grew to 11,120 bytes.\n\nADAPTATION.md and README.md document each change, and `.imd-responses.json` holds the five verdicts. The remaining formatter diffs are in test files I did not touch, so I left them alone.","treeHash":"c8fd32b1b83f402d65939e685c884060044cef5a","usage":{"cachedInputTokens":2267879,"inputTokens":674,"model":"claude-fable-5-1","outputTokens":46345,"runtime":"claude","turns":49,"wallClockMs":779674}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"ea89e16822824c6f","findings":[{"citation":"resolved","description":"Boundary (external call to an address with no code). setup() validates only that liquidity_ is non-zero, then stores it once (a second setup() reverts AlreadySetUp, setInk() reverts InkAlreadySet). release() does ink.safeTransfer(liquidity, inkToPool) followed by IInkLiquidity(liquidity).addLiquidity(). If liquidity_ is an EOA or any contract that is not an InkLiquidity bound to the INK pool, the high-level call reverts (extcodesize check / missing function), so release() reverts every time. Nothing else can set released = true: the hook never opens trading, pull() stays closed (NotReleased), and the 2,000 x 400,000 INK allocation (800,000,000e18) stays in the vault with no recovery path. The same once-only shape applies to setInk(): a wrong token address makes setup() revert NotFunded forever. These are owner-input errors, so they are reported as a missing boundary check (low), not as an attack. Minimal fix that preserves the design: in setup(), require liquidity_.code.length > 0 (and optionally that IInkLiquidity(liquidity_).poolSet() is callable), and in setInk() require address(ink_).code.length > 0. The existing test test_releaseFailureRollsBackUntilLiquidityIsConfigured only covers the recoverable case where the address is a real InkLiquidity whose pool is not yet set.","line":157,"path":"src/InkVault.sol","reproduction":"State: InkPortraits with setVault(vault); vault holds 800,000,000e18 INK; owner calls setInk(ink). Call: owner -> vault.setup(bytes32(1), uint64(block.timestamp), 0x0000000000000000000000000000000000001234). Expected: revert BadSetup (not a liquidity contract). Actual: accepted, Setup event emitted. Then warp +24 hours and call vault.release() from any account. Expected: released == true and INK moved to the pool. Actual: reverts (call to address without code at IInkLiquidity(liquidity).addLiquidity()), released stays false, vault still holds 800,000,000e18 INK; owner -> vault.setup(...) again reverts AlreadySetUp and vault.setInk(...) reverts InkAlreadySet. Verified with test/scratch/SetupNoCode.t.sol (passes on current code, i.e. the mis-set address is accepted and release() reverts).","severity":"low","snippet":"            root == bytes32(0) || liquidity_ == address(0) || start < block.timestamp","title":"InkVault.setup() accepts a liquidity address with no code; release() then reverts forever and the once-only setup cannot be corrected"}],"hash":"5584fc687cc4492e53aee504829eb3064dbeb28f253f01c0015182509309ac3e","nodeId":"a521662f-5510-42a5-9cf4-a834eef4e483","outcome":"completed","summary":"The review is complete and the findings file is written and validated.\n\n**Outcome.** The math across the five deployed contracts holds up. I found one low-severity boundary defect and no precision or numerical-gap defects. The file `.imd-findings.json` holds that finding plus 55 coverage rows, one for each of the 52 listed entry points and three for invariants and the static-analysis leads.\n\n**What I checked in depth (Math Precision, Boundary, Numerical Gap):**\n- InkLiquidity ladder and single-position math. I replayed the ladder split and the Uniswap v4 round-up for every possible release amount (1 to 2000 unclaimed seats) and a sweep of single-position ticks at the launch price. The mint never asks the position manager for more INK than the contract allows, intermediate products stay far below overflow, and the last band absorbs split rounding. The currency1 path (Sepolia, where ETH is currency0) is safe by construction.\n- InkVault fees and backing. Every allowed fee rate divides the 400,000 INK portrait exactly, so there is no truncation at all. Pull, deposit, release and reveal keep the backing invariant, and the pull admission check runs before any multiplication that could overflow.\n- InkHook fee. The specified-side convention matches the v4 Hooks library, negation only fails at an unreachable int128 minimum, and the minted claim nets the hook delta to zero.\n- InkPortraits bitmap and swarm numbering, InkRenderer threshold arithmetic, and all window boundaries (claim ends exactly when release opens).\n\n**The one finding (low):** `InkVault.setup()` checks only that the liquidity address is non-zero. If the owner passes an address with no code or a non-InkLiquidity contract, release() reverts forever, trading and pulls never open, and the 800,000,000 INK allocation is stuck, because setup and setInk are once-only. Reproduced in a scratch test. This is an owner-input error, so it is reported as a missing boundary check rather than an attack.\n\n**Static-analysis leads.** All examined and dismissed with reasons in the coverage record: the divide-before-multiply lines are intentional tick flooring, the unsafe cast is bounded by INK supply, and the weak-PRNG lead is the documented next-block-hash design.\n\n**Tests run.** The existing suite passes (71 tests, fork suite skipped). Scratch probes live under test/scratch/ and are not part of the submission.","treeHash":null,"usage":{"cachedInputTokens":1580418,"inputTokens":514,"model":"claude-fable-5-1","outputTokens":41587,"runtime":"claude","turns":29,"wallClockMs":671818}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"2293d6b19e81ef1a","findings":[{"citation":"resolved","description":"Settlement of my earlier low finding 71ef9819. Confirmed fixed: setup() now reverts BadSetup when liquidity_ has no code (src/InkVault.sol line 165: `root == bytes32(0) || liquidity_.code.length == 0 || start < block.timestamp`), so case 1 of the earlier reproduction (an EOA as liquidity) is rejected while still correctable; my scratch test test_setupRejectsEOA confirms the revert and that a contract address is then accepted. Not fixed, and still reproducible: (a) setInk() stores any address once (line 154) with no check that the vault holds SEATS*PORTRAIT of it, so a wrong token makes setup() revert NotFunded forever and release() revert InkNotSet forever while the real 800,000,000e18 INK stays in the vault with no exit; (b) setup() does not check that IInkLiquidity(liquidity_).ink() equals the vault's ink, and InkLiquidity.setPool() (src/InkLiquidity.sol line 98, PoolAlreadySet) is equally one-shot, so an INK mismatch makes release() revert NothingToAdd on every call; (c) InkPortraits.setVault (src/InkPortraits.sol line 84, VaultAlreadySet) does not check the target is the InkVault bound to this collection. The author disputes the remaining checks on the grounds that a poolSet()/ink() cross-check would break liquidity stand-ins such as the proof fixture, that setInk/setVault have no reference value to compare against, and that the brief says the logic is deployed as written; these are owner-input steps now documented in ADAPTATION.md and README.md. I accept that as a documented trust assumption for this launch: owner error only, no unprivileged path, so it stays low and advisory, not blocking. The impact when it happens remains permanent. If the author wants to close (a) without a reference value, allowing setInk() to be re-called until setup() succeeds is enough; (b) could be closed by comparing IInkLiquidity(liquidity_).ink() to ink only when that call succeeds.","line":154,"path":"src/InkVault.sol","reproduction":"Scratch test test/scratch/JudgeRound2.t.sol (not kept), both passing on the current code. test_wrongSetInkStillStrands: deploy TestSeats, InkPortraits(owner), InkVault(portraits, seats, owner), setVault(vault); mint 800,000,000e18 of the real INK to the vault; owner calls vault.setInk(WRONG) where WRONG is another ERC-20 -> accepted. Then: vault.setInk(INK) reverts InkAlreadySet; vault.setup(root, now, liquidityContract) reverts NotFunded; vault.release() reverts InkNotSet; INK.balanceOf(vault) == 800,000,000e18 and no function in the vault can move it. Expected: the wrong token is rejected or correctable before setup(). test_setupRejectsEOA (fix confirmed): after setInk(INK), vault.setup(root, now, 0x1234) reverts BadSetup and vault.setup(root, now, address(mockLiquidity)) is then accepted, liquidity() == mockLiquidity.","severity":"low","snippet":"        if (address(ink) != address(0)) revert InkAlreadySet();","title":"Residual of 71ef9819 (partly fixed): setInk, setVault and setPool remain one-shot with no cross-check, so one wrong owner input before release() still permanently strands the 800,000,000 INK allocatio"},{"citation":"resolved","description":"Settlement of my earlier low finding eb210c01. The author disputes it for this launch because InkHook is not one of the five contracts launch.json deploys (TestSeats, InkPortraits, InkVault, InkLiquidity, InkRenderer) and the hook's constructor and pool initialization belong to the later univ4_hook launch. That is correct: the hook is absent from the manifest, and this round changed only the formatting of its three signatures (git diff of src/InkHook.sol between the accepted and revised trees shows signature rewraps only, no logic). The behaviour is unchanged: PoolManager.initialize is permissionless for any key, beforeInitialize only requires key.hooks == this and that one currency is imd, and it then records the other currency as ink and sets poolCreated, after which the real INK/IMD pool reverts PoolAlreadyCreated. There is no window if the hook is deployed and its pool initialized in one factory transaction. Downgraded to info for this launch and recorded (as ADAPTATION.md item 7 also does) so the hook launch's atomicity is checked rather than assumed. Nothing in the five contracts of this launch should change for it.","line":123,"path":"src/InkHook.sol","reproduction":"Deploy InkHook(manager, portraits, vault, imd=IMD, treasury, 25) at an address with the BEFORE_INITIALIZE|BEFORE_SWAP|AFTER_SWAP|AFTER_SWAP_RETURNS_DELTA flag bits, without initializing a pool in the same transaction. Attacker deploys token X and calls manager.initialize(PoolKey(sorted(X, IMD), 12500, 200, hook), sqrtPrice(0)). Expected: only the launch's INK/IMD pool can bind the hook. Actual: hook.poolCreated() == true, hook.ink() == X, and manager.initialize(PoolKey(sorted(INK, IMD), 12500, 200, hook), ...) reverts with PoolAlreadyCreated wrapped by the PoolManager. Reproduced last round in test/scratch/JudgeRepro.t.sol test_hookBindsToFirstInitializer; the hook code is unchanged this round apart from formatting.","severity":"info","snippet":"        if (poolCreated) revert PoolAlreadyCreated();","title":"Unchanged, for the later hook launch: InkHook.beforeInitialize binds the hook to the first IMD-paired pool anyone initializes with it"},{"citation":"resolved","description":"Settlement of my earlier info finding 9d091aab, disputed by the author as intended. Agreed: the brief orders TestSeats from src/testnet/TestSeats.sol with no constructor arguments, the file's notice says anyone can mint any seat for rehearsal and it must never be deployed on mainnet, and InkVault.claim authenticates only seats.ownerOf(seat) == msg.sender plus a Merkle proof against statsRoot. Nothing in the contracts should change. Kept as an operational note for the rehearsal (publish a statsRoot covering only team-controlled seats, or mint them first) and for the mainnet manifest, which must pass the real identity.md collection since InkVault.seats is immutable; ADAPTATION.md item 8 records the same.","line":10,"path":"src/testnet/TestSeats.sol","reproduction":"Sepolia deployment after setup(root, start, liq) with a root covering all seats: attacker calls TestSeats.mint(7) (unminted) -> owns seat 7; during the window calls vault.claim(7, statsOf(7), proofOf(7)) from the published stats -> portraits.ownerOf(7) == attacker; attacker.setApprovalForAll(vault, true) then vault.deposit([7], 400_000e18) -> attacker receives 400,000 INK, fee 0 (isAgentPortrait). Repeating for every seat before claimEnd: release() emits Released(0, 0), swarmSlots() == 0, pull(1) reverts NoneAvailable. Expected on mainnet: only the real seat holder; on Sepolia: anyone, by design.","severity":"info","snippet":"    function mint(uint256 seat) external {","title":"By design on Sepolia: TestSeats.mint is permissionless, so any address can claim any seat's agent portrait and redeem it for 400,000 INK during the rehearsal"}],"hash":"6f46715267be48135cfab30af76634e9725ee9d7408a0d6366ad260662f1eab4","nodeId":"af109a30-50a4-4f5f-a937-24682a929e24","outcome":"completed","summary":"The review is complete. The findings file is written and validated, with all 52 entry points answered.\n\n**Outcome: the one blocking finding is fixed, and nothing new blocks the launch.**\n\nSettlement of my earlier findings:\n\n- **Renewal widened queued draws (medium, 4ca49345): fixed.** The vault now records a renewed pull's original block in `_madeBlock` and admits hand-ins against that block, so a renewal moves only the entropy block. My attached proof and the audit_flow proof both pass on the revised code. My own scratch test with two consecutive renewals of the head pull, hand-ins between them and a pull queued behind confirms the queued pull keeps its cutoff and the hand-ins wait for a later pull. The companion change, counting only hand-ins from earlier blocks in pull(), is correct: a same-block hand-in is refused and counted from the next block, and I found no path to a pull with zero candidates.\n- **Formatter check (info, 6b948270): fixed.** The check on the source directory exits 0 and the hook diff is signature rewraps only.\n- **One-shot setters (low, 71ef9819): partly fixed, remainder kept at low as advisory.** setup() now rejects a liquidity address with no code, which I confirmed. A wrong setInk() still strands the allocation, and I re-reproduced that. The author's reasons for not adding the other checks are documented and acceptable as a trust assumption. It does not block.\n- **Hook binds to the first pool (eb210c01): downgraded to info.** The hook is not in this manifest and its code is unchanged apart from formatting. Recorded for the later hook launch.\n- **TestSeats.mint permissionless (9d091aab): info, by design.** Unchanged, as the brief orders.\n\nChecks run this round: the full suite passed with 111 tests and 28 skipped fork tests, plus four scratch tests I wrote under test/scratch, which is not kept.","treeHash":null,"usage":{"cachedInputTokens":2184517,"inputTokens":706,"model":"claude-fable-5-1","outputTokens":30275,"runtime":"claude","turns":32,"wallClockMs":441023}},{"artifacts":[],"attempt":1,"bundleHash":"2ca6dfc447177cef9e928b9b000df87fdae840c560aa7c83f6b05a1d48a4d616","device":"aa333626543c4dee","findings":[],"hash":"73c81de342a5e84b3442a03733bba2dcde1806d7f81d9718c1b97f72bc42d177","nodeId":"3d9643ed-2233-4715-b24c-69223b2b4342","outcome":"completed","summary":"The accepted launch.json already satisfies the brief, and the revision prompt listed no findings to act on, so I left it unchanged.\n\n**What I verified**\n\n- The manifest has exactly the keys kind, contracts and notes, with five unique contracts in dependency order and notes under the length limit.\n- Each constructorArgs list matches the accepted source one entry per parameter: TestSeats takes none, InkPortraits takes the owner, InkVault takes the portraits, seats and owner, InkLiquidity takes PoolManager, PositionManager, owner and treasury, and InkRenderer takes the portraits and owner.\n- The Uniswap v4 addresses in the manifest are the official Sepolia PoolManager and PositionManager, and they match the constants the repo's own Sepolia launch test uses.\n- Every owner argument is $owner. The royalty recipient in InkPortraits is also the owner argument, at a zero rate. The treasury address only receives IMD fees and holds no role.\n- No constructor calls another contract, so the empty-chain rehearsal in the protected test will not revert. All five runtimes are well under the EIP-170 limit.\n\n**Checks run**\n\n```\nforge build --sizes                     # compiled, five contracts between 3.8 kB and 12.7 kB runtime\nFOUNDRY_FUZZ_SEED=0x30c0...c635 forge test --no-match-path test/MainnetFork.t.sol\n# 75 passed, 0 failed, 14 skipped\n```\n\nThe mainnet fork suite was skipped because it needs an RPC. The working tree is clean, so no file other than the manifest from the previous round is in the submission.","treeHash":"c5ca58c9af97c2e1c8ce62bb8d32e718370eb747","usage":{"cachedInputTokens":197785,"inputTokens":194,"model":"claude-fable-5-1","outputTokens":2675,"runtime":"claude","turns":7,"wallClockMs":320993}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"bd7adba3a8045853","findings":[{"citation":"resolved","description":"InkVault documents (lines 27-29) that 'a portrait handed in only joins the draw for pulls made in later blocks, so once a block hash is public nobody can change what it picks'. _revealNext() enforces this by calling _admit(p.blockNumber) for the pull at the head of the queue only. When that head pull is renewed (line 295, p.blockNumber = uint64(block.number), after its hash expired or avail == 0), the next _admit uses the RENEWED block, and everything deposited before that block is pushed into _inventory. Pulls queued behind the head keep their original blockNumber, their entropy blockhash(blockNumber + 1) may still be valid, and they then draw over an inventory containing portraits handed in after their own block, by someone who could already see their entropy. Because pick = r % avail and the admitted items occupy inventory indexes [0, held), a depositor who knows r for the victim's pull can choose how many portraits to hand in (k) so that r % (unminted + k) < k, steering the victim's pull onto one of the depositor's own (already paid-out) portraits instead of a fresh mint, or away from a valuable agent portrait sitting in inventory. Preconditions: an earlier pull stays unrevealed past its 256-block hash window (reveal is permissionless, so this needs keepers and the victim to be idle for about 51 minutes), or the head pull hits avail == 0 at reveal. The victim loses the documented fairness guarantee, not INK. Fix: keep the admission cutoff separate from the entropy block, e.g. store the original pull block (or the smallest original block among pending pulls) and pass that to _admit, so renewal changes only which block hash is used, never which deposits are eligible.","line":288,"path":"src/InkVault.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {InkPortraits} from \"src/InkPortraits.sol\";\nimport {InkVault} from \"src/InkVault.sol\";\nimport {TestSeats} from \"src/testnet/TestSeats.sol\";\n\ncontract MockInk is ERC20(\"INK\", \"INK\") {\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// Stands in for InkLiquidity: release() only needs addLiquidity() to succeed.\ncontract MockLiquidity {\n    function addLiquidity() external pure returns (uint256) {\n        return 1;\n    }\n}\n\n/// A pull's draw is documented as fixed once its block hash is public: \"a portrait handed in only joins the\n/// draw for pulls made in later blocks, so once a block hash is public nobody can change what it picks\".\n/// When an earlier pull is renewed (hash expired), _admit runs with the renewed block, so a deposit made\n/// AFTER a later pull's block (and after that pull's hash is public) joins the later pull's candidate set.\ncontract RenewalWidensDrawTest is Test {\n    uint256 constant PORTRAIT = 400_000e18;\n    address owner = makeAddr(\"owner\");\n    address attacker = makeAddr(\"attacker\");\n    address victim = makeAddr(\"victim\");\n\n    MockInk ink;\n    TestSeats seats;\n    InkPortraits portraits;\n    InkVault vault;\n    MockLiquidity liq;\n\n    function setUp() public {\n        vm.roll(100);\n        ink = new MockInk();\n        seats = new TestSeats();\n        portraits = new InkPortraits(owner);\n        vault = new InkVault(portraits, seats, owner);\n        liq = new MockLiquidity();\n        vm.prank(owner);\n        portraits.setVault(address(vault));\n        ink.mint(address(vault), 2000 * PORTRAIT);\n        vm.startPrank(owner);\n        vault.setInk(IERC20(address(ink)));\n        vault.setup(keccak256(\"root\"), uint64(block.timestamp), address(liq));\n        vm.stopPrank();\n        vm.warp(block.timestamp + 24 hours);\n        vault.release();\n        ink.mint(attacker, 10 * PORTRAIT);\n        ink.mint(victim, 10 * PORTRAIT);\n        vm.prank(attacker);\n        ink.approve(address(vault), type(uint256).max);\n        vm.prank(victim);\n        ink.approve(address(vault), type(uint256).max);\n        vm.prank(attacker);\n        portraits.setApprovalForAll(address(vault), true);\n    }\n\n    function test_depositAfterLaterPullsHashIsPublicJoinsItsDraw() public {\n        // Attacker owns two swarm portraits (pulled and revealed earlier).\n        vm.prank(attacker);\n        vault.pull(2);\n        vm.roll(block.number + 2);\n        vault.reveal(1);\n        uint256 junkA = 10_000;\n        uint256 junkB = 10_001;\n        assertEq(portraits.ownerOf(junkA), attacker);\n        assertEq(vault.inventorySize(), 0);\n\n        // Pull A (attacker) at block 1000, pull B (victim) at block 1003. Nobody reveals A.\n        vm.roll(1000);\n        vm.prank(attacker);\n        uint256 pullA = vault.pull(1);\n        vm.roll(1003);\n        vm.prank(victim);\n        uint256 pullB = vault.pull(1);\n        assertEq(pullB, pullA + 1);\n\n        // Block 1010: blockhash(1004), pull B's entropy, is public. The attacker hands in both junk portraits.\n        // Per the documented rule they can only join draws of pulls made after block 1010.\n        vm.roll(1010);\n        assertTrue(blockhash(1004) != bytes32(0));\n        uint256[] memory ids = new uint256[](2);\n        ids[0] = junkA;\n        ids[1] = junkB;\n        vm.prank(attacker);\n        vault.deposit(ids, 0);\n        assertEq(vault.waitingCount(), 2);\n\n        // Block 1258: pull A's hash (block 1001) has expired, pull B's (block 1004) has not. A is renewed.\n        vm.roll(1258);\n        assertEq(blockhash(1001), bytes32(0));\n        assertTrue(blockhash(1004) != bytes32(0));\n        assertFalse(vault.reveal(pullA));\n        (, uint64 renewedBlock,) = vault.pulls(pullA);\n        assertEq(renewedBlock, 1258);\n\n        // Block 1260: both reveal. Admission runs with A's renewed block (1258), so the block-1010 deposits are\n        // admitted into the inventory, and pull B (block 1003, original hash still valid) draws over them.\n        vm.roll(1260);\n        assertTrue(blockhash(1004) != bytes32(0));\n        assertTrue(vault.reveal(pullB));\n        (, uint64 bBlock, bool bDone) = vault.pulls(pullB);\n        assertTrue(bDone);\n        assertEq(bBlock, 1003, \"pull B drew with its original block hash\");\n        // Expected: the two deposits are still waiting (they were handed in after pull B's block).\n        // Actual: they were admitted and were candidates for pull B.\n        assertEq(vault.waitingCount(), 2, \"deposits made after pull B's block joined pull B's draw\");\n    }\n}","reproduction":"State: vault released; attacker holds swarm portraits 10000 and 10001 and has approved the vault. Block 1000: attacker pull(1) -> pull A. Block 1003: victim pull(1) -> pull B. Nobody reveals. Block 1010: blockhash(1004), B's entropy, is public; attacker calls deposit([10000,10001], 0) -> waitingCount()==2. Block 1258: reveal(A) returns false and emits PullRenewed(A, 1258) because blockhash(1001)==0; blockhash(1004) is still nonzero. Block 1260: reveal(B): _admit(1258) moves both block-1010 deposits into _inventory, A draws with blockhash(1259), then B draws with its ORIGINAL blockhash(1004) over an inventory that contains the two portraits handed in 7 blocks after B was made. Expected per the contract's own rule: waitingCount()==2 after B is revealed (deposits only eligible for pulls made after block 1010). Actual: waitingCount()==0 and pulls(B).blockNumber==1003. The scratch test test/scratch/RenewalWidensDraw.t.sol fails on the current code with 'deposits made after pull B's block joined pull B's draw: 0 != 2'.","severity":"medium","snippet":"        if (!_admit(p.blockNumber)) {","title":"Renewing an earlier pull admits later deposits into a later pull's draw after that pull's hash is public"},{"citation":"resolved","description":"setInk() (lines 145-149) accepts any address once and can never be changed (InkAlreadySet). The only funding check, ink.balanceOf(address(this)) >= SEATS * PORTRAIT, lives in setup() (line 160) and is evaluated against whatever setInk() stored. If the owner passes a wrong address (the IMD token, a typo, the pool, the hook), setup() reverts NotFunded forever, setInk() cannot be redone, and no other function can move the real 800,000,000 INK (80% of supply) that the launch sent to the vault: claim() needs statsRoot, release() needs statsRoot, pull()/rerollTraits() need released, deposit() has nothing to deposit. The contracts have no sweep and no owner path to recover, so the mistake is a permanent loss of the whole allocation and of the claim/swarm distribution. This is an owner-error precondition, not an attacker path, but the irreversible setter is the one without the check while the recoverable step (setup can be retried) has it. Fix: validate in setInk() that ink_.balanceOf(address(this)) >= SEATS * PORTRAIT (the launch funds the vault before the owner can call it), or let setInk() be re-called until setup() has succeeded.","line":147,"path":"src/InkVault.sol","reproduction":"Deploy per the manifest; launch sends 800,000,000 real INK to the vault. Owner calls setInk(0xWrongToken) where 0xWrongToken is any ERC-20 that is not INK: succeeds, emits InkSet. Owner calls setInk(realInk): reverts InkAlreadySet. Owner calls setup(root, now, liquidity): reverts NotFunded (wrong token balance is 0). release(): reverts InkNotSet; pull(1): reverts NotReleased; claimOpen()==false forever. realInk.balanceOf(vault) stays 800,000,000e18 with no function able to transfer it. Expected: either setInk rejects a token the vault is not funded in, or it can be corrected before setup. test/scratch/SetInkBrick.t.sol walks this sequence and passes on the current code (it asserts the bricked state).","severity":"low","snippet":"        ink = ink_;","title":"setInk() is irreversible but unvalidated: a wrong token address permanently strands the vault's entire INK allocation"}],"hash":"7528ff8d1e0a95fb9447211bce1641db78497472b9e9b1b49907d90aa1c4ab36","nodeId":"3f4575ef-1820-4ed3-b875-3890697aae73","outcome":"completed","summary":"Findings file is written and verified: both snippets match the tree exactly, the JSON parses, and the coverage record answers all 52 listed entry points plus two invariant rows.\n\n**Outcome:** `.imd-findings.json` holds 2 substantiated findings and 54 coverage rows. The existing suite passed before review (71 passed, 0 failed, 14 skipped, the fork tests). The Sepolia Uniswap v4 addresses in the adaptation were checked live over three RPCs: the PoolManager has code and the PositionManager's `poolManager()` returns it.\n\n**Findings**\n\n1. **Medium, `src/InkVault.sol:288`.** Renewing the head pull admits deposits by the renewed block, so a pull queued behind it draws, with its original and already public hash, over portraits handed in after its own block. This breaks the contract's documented rule that nobody can change what a pull picks once its hash is public, and a depositor who knows the victim's random value can choose how many portraits to hand in to steer the pick. Precondition is an earlier pull left unrevealed past 256 blocks or hitting zero availability. The attached Foundry proof under `test/scratch/RenewalWidensDraw.t.sol` fails on the current code.\n\n2. **Low, `src/InkVault.sol:147`.** The one-shot `setInk` takes any address with no funding check, while the funding check sits in the retryable `setup`. A wrong value can never be corrected and strands the vault's full 800 million INK allocation with no path out. Owner error is required, so this is a footgun rather than an attack. A scratch test walks the bricked state.\n\n**Coverage.** Every vault, portraits, liquidity, renderer and TestSeats entry point was traced for checks, state changes and failure paths. Hook and fee router entry points are outside this launch; I traced their swap-delta, lock and payout paths anyway and found nothing to report. Static-analysis leads were checked and none rose to a finding: the weak PRNG is documented design, the strict equality and uninitialized locals are benign, and the ceil-of-ceil rounding in position minting cannot exceed the supplied maximum.\n\n**Not covered.** Trait parity between the on-chain labels and the art script, and production-router authentication of the hook's holder waiver, belong to other specialists and were not examined.","treeHash":null,"usage":{"cachedInputTokens":1280972,"inputTokens":322,"model":"claude-fable-5-1","outputTokens":54844,"runtime":"claude","turns":31,"wallClockMs":864519}},{"artifacts":[],"attempt":1,"bundleHash":"ba6d6babb73e21742a895f1e179881cd9c720afc11749bfcd207d09b1577e76c","device":"8a38929b31c716ff","findings":[],"hash":"812a00fbb79e3b541a216679a351aa6fcd58e19b5f3be94bafb180168e4aa399","nodeId":"3d9643ed-2233-4715-b24c-69223b2b4342","outcome":"completed","summary":"Created [launch.json](/home/imd3/.identitymd/work/0afb11c1-8f3d-44b3-837a-96cbd6064413/3d9643ed-2233-4715-b24c-69223b2b4342/launch.json) with the required contract order, constructor arguments, `$owner` roles, treasury, and [official Sepolia Uniswap addresses](https://developers.uniswap.org/docs/protocols/v4/deployments).\n\nVerified manifest constraints and compiled constructor ABIs. `forge build` passed. Tests using the supplied fuzz seed: **71 passed, 0 failed, 28 skipped** for unavailable fork/snapshot prerequisites.\n\nOnly `launch.json` is included in the deliverable.","treeHash":"8817ccd55a4e26d7dc0fe19a17899ea750230306","usage":{"cachedInputTokens":447872,"inputTokens":40932,"model":"gpt-6-astra","outputTokens":3666,"runtime":"codex","turns":3,"wallClockMs":124306}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"4be5e039e1c77ce3","findings":[{"citation":"resolved","description":"Every reveal first admits, in one loop, every portrait handed in with deposit() since the last revealed pull's block (_admit). Each admitted entry costs about 33,400 gas (an _inventory.push of a fresh slot plus the delete of the two-slot Incoming). Measured with a scratch test on this tree: 500 pending entries make the next reveal() cost 16.79M gas, 1,000 cost 33.44M and 2,000 cost 66.73M, linear at ~33.4k per entry. A 36M block (Sepolia, Ethereum today) is exhausted at about 1,080 pending entries; a 60M block at about 1,800. Pulls are revealed strictly in order and the admission cannot be split across calls, so once one pull's reveal cannot fit in a block, that pull and every pull queued after it can never be revealed: their 400,000 INK each stays in the vault with no refund path, the deposited portraits stay in _incoming, and the pull/reveal mechanic is dead for good. The precondition is heavy (holding more than half of the 2,000 portraits at once, or ~430M INK to pull them) and the attacker only loses the 1% reroll fee on each deposit (4,000 INK each), so this is a griefing vector for a whale rather than a profit attack; severity set to medium for that reason. Fix: bound the work per reveal (admit at most N entries per call and let a renewed/early-returning reveal continue later), or admit at deposit time with a one-block delay keyed on blockNumber instead of a queue walked inside the reveal.","line":306,"path":"src/InkVault.sol","reproduction":"State: after release(). Attacker A holds n swarm portraits (pull(n) at block B, reveal at B+2). A calls deposit() with all n token ids (in batches of 100 per tx) at blocks later than the last revealed pull's block; A receives n*396,000 INK back. Victim V calls pull(1) at a later block P (passes: available() + waiting covers it). At block P+2 anyone calls reveal(V's pull id): _admit(P) walks all n entries. With n = 1,000 the call needs 33,436,245 gas (measured); with n >= ~1,080 it exceeds a 36M block gas limit and reverts out of gas. Expected: V's pull is revealed and a portrait is delivered. Actual: reveal() and revealNext() can never complete for that pull or any later one; V's 400,000 INK and every later puller's INK are locked permanently. Scratch test used: deploy TestSeats, InkPortraits(owner), InkVault(portraits, seats, owner), a plain ERC20 as INK minted 800M to the vault, a mock liquidity whose addLiquidity() is a no-op; owner: setVault, setInk, setup(root=0x01, start=now, liquidity); warp 24h; release(); then the sequence above, measuring gasleft() around reveal().","severity":"medium","snippet":"        while (head < end && _incoming[head].blockNumber < beforeBlock) {","title":"Unbounded admission loop in _revealNext: a large pile of deposits before one pull makes every later reveal run out of gas, locking all later pullers' INK forever"},{"citation":"resolved","description":"The vault takes portraits only through deposit(), which uses transferFrom and pushes the token into _incoming. It never needs onERC721Received, yet it returns the magic value unconditionally, so a holder who calls portraits.safeTransferFrom(holder, vault, id) (the natural 'hand it to the vault' action for a wallet or marketplace UI) succeeds, gets no INK, and the token is not in _incoming or _inventory, so no pull can ever return it. The 400,000 INK that backed it stays in the vault with no path to pay it out: the holder's value is lost and the vault's INK balance is permanently above its obligations. Fix: revert in onERC721Received (the vault has no legitimate inbound safe-transfer), or route it into _incoming and pay the depositor like deposit() does.","line":406,"path":"src/InkVault.sol","reproduction":"State: after release(). Alice pulls one portrait (pull(1), reveal) and owns token 10000. Alice calls portraits.safeTransferFrom(alice, address(vault), 10000). Expected: revert (or treated as a deposit paying 396,000 INK). Actual: call succeeds, portraits.ownerOf(10000) == vault, alice's INK balance unchanged, vault.heldCount() == 0 and vault.inventorySize() == 0, ink.balanceOf(vault) still 400,000e18 for a portrait nobody can pull. Confirmed with a scratch test on this tree (same minimal setup as the medium finding).","severity":"low","snippet":"    function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {","title":"Vault accepts safeTransferFrom of any portrait but never uses it: a portrait sent that way is stranded with no INK paid and never enters the draw"},{"citation":"resolved","description":"floorTick is the price floor every later listing is clamped to (releaseStartTick, _rangeFrom) and setPool can be called exactly once. StateLibrary.getSlot0 on a pool id that was never initialized returns sqrtPriceX96 = 0 and tick = 0 instead of reverting, so if the owner calls setPool before the swarm launch initializes the pool (the launch README orders setPool after the hook launch, but nothing enforces it), floorTick becomes 0 and cannot be corrected. With INK as currency0 and a real opening tick around 129,000 (the tree's own tests use that), every addLiquidity() would then clamp to tick 0, i.e. list released INK at about 1.0001^-129000 (~2.5e-6) of the opening price; with INK as currency1 the clamp is the other way and INK is listed at ~400,000x the opening price, so the released supply is never bought. The same function also does not check key.hooks or that the other currency is IMD/ETH, so any key containing INK is accepted. Fix: require sqrtPriceX96 != 0 from getSlot0 (and ideally key.hooks == the expected hook) in setPool.","line":100,"path":"src/InkLiquidity.sol","reproduction":"Deploy a fresh PoolManager and InkLiquidity(pm, posm, owner, treasury). Build key = (currency0 = 0x0 (ETH), currency1 = INK, fee 12500, tickSpacing 200, hooks 0x0) and do NOT initialize the pool. owner calls setPool(key, INK). Expected: revert (pool not initialized). Actual: succeeds; floorTick() == 0; poolSet() == true; a second setPool reverts PoolAlreadySet. Confirmed with a scratch test on this tree.","severity":"low","snippet":"        (, floorTick,,) = poolManager.getSlot0(key.toId());","title":"setPool records floorTick from getSlot0 without checking the pool is initialized or the key is the INK/IMD hook pool: a call before initialization fixes floorTick = 0 forever"},{"citation":"resolved","description":"rerollTraits() only blocks deposit() while a reroll is pending; InkPortraits puts no restriction on transfers. From block b+1 the entropy blockhash(b+1) and therefore the exact new seed keccak256(abi.encode(entropy, tokenId, \"traits\")) and the new traits are computable off-chain by the holder. A holder who sees a worse result can list and sell the portrait before anyone calls revealTraits(); marketplaces display the current (better) traits, the buyer pays for them, and the permissionless reveal then repaints the token in the buyer's wallet. The seller never gains a better outcome (they cannot cancel), so this is information asymmetry rather than theft, but it breaks the 'what you see is what you buy' property for every swarm portrait with traitRerollBlock != 0. Fix: have InkPortraits._update revert while the vault reports a pending reroll for the token (or clear/apply the reroll on transfer), and surface traitRerollBlock in tokenJSON.","line":356,"path":"src/InkVault.sol","reproduction":"State: after release(). Alice owns swarm token 10000 and 4,000 INK for the fee. Block b: alice calls rerollTraits(10000, 4000e18). Block b+2: blockhash(b+1) is known; alice computes the new seed, dislikes it, and calls portraits.transferFrom(alice, buyer, 10000). Expected: transfer refused while a reroll is pending (or the reroll voided). Actual: transfer succeeds; afterwards anyone calls vault.revealTraits(10000) and portraits.swarmSeed(10000) changes while portraits.ownerOf(10000) == buyer. Confirmed with a scratch test on this tree.","severity":"low","snippet":"        traitRerollBlock[tokenId] = uint64(block.number);","title":"A pending trait reroll does not block transfers: the new seed is public one block after the request, so a seller can pass a known-bad repaint to a buyer"},{"citation":"resolved","description":"The evm_contracts recipe wants applications fully configured in nonpayable constructors and lists post-deployment initialization among the things the adapter removes. As the tree arrived: InkPortraits.setVault (once), setRenderer/lockRenderer, setContractURI; InkVault.setInk (once), setup (once: statsRoot, claimStart, liquidity), setFees; InkLiquidity.setPool (once), setWidth, setPremium; InkRenderer.addChunk, setPreviewBase, lock. All are onlyOwner, none is a proxy, initializer, delegatecall or upgrade path, and none of the five runtime bytecodes contains DELEGATECALL, CALLCODE or SELFDESTRUCT (scanned on this tree). Most of these cannot be moved into constructors without changing the design: INK and the pool do not exist until the later hook launch (setInk, setPool, setup), Portraits and Vault reference each other (setVault), and the renderer script is uploaded in chunks after deployment. The task text ('no changes to their logic') and the launch README both anticipate these owner calls. Reported at low so the adapter and the final review see the full list and decide deliberately rather than treating it as a defect to strip; what matters for the launch is that every such role is $owner (it is: every constructor takes owner_ and passes it to Ownable; none uses msg.sender or tx.origin) and that nothing is callable by anyone else (it is not). Until the owner completes them: pull/rerollTraits revert NotReleased, release reverts, claim reverts ClaimClosed, tokenURI reverts (no renderer), addLiquidity reverts PoolNotSet.","line":140,"path":"src/InkVault.sol","reproduction":"Deploy the five contracts with the task's constructor arguments and call nothing else. portraits.tokenURI(x) for any minted x reverts (renderer == address(0), confirmed in a scratch test); vault.pull(1) reverts NotReleased; vault.release() reverts InkNotSet (statsRoot == 0); liquidity.addLiquidity() reverts PoolNotSet; renderer.tokenJSON(..) returns an animation_url whose <script> block is empty (chunks.length == 0). Expected under the recipe's letter: a constructor-configured contract; actual: the contract is inert until the owner's setters run.","severity":"low","snippet":"    function setInk(IERC20 ink_) external onlyOwner {","title":"Launch-policy note: four of the five contracts are configured after deployment by owner-only setters rather than in their constructors"},{"citation":"resolved","description":"Once the price has moved above the release start, addLiquidity() puts the whole INK balance (>= 400,000) into one width-tick (20,000) position starting one spacing above the current tick. Anyone may call it, so a trader can sell INK to push the tick down, call addLiquidity(), then buy back: the part of the new position between the pushed tick and the market tick is bought below market. The clamp to releaseStartTick caps the listing at the opening price (plus premium), and the ladder spreads anything listed at or below the start across eight doubling bands, so the surface is bounded. Modelled on this tree's numbers (1.25% pool fee + 1% hook fee round trip, ladder bands of ~2.35M, 4.7M, 9.4M, 18.8M ... INK for 600M released) the fee on moving the price one band exceeds the discount captured on a 400,000-INK batch, so I could not construct a profitable input; it is recorded so the final review does not have to redo the model. A cheap hardening is to restrict addLiquidity() to the vault/hook/fee router callers or to list relative to a TWAP tick rather than the spot tick.","line":129,"path":"src/InkLiquidity.sol","reproduction":"State: pool trading, price 11,600 ticks above releaseStartTick, InkLiquidity holds 400,000 INK of fees. One transaction: swap INK->IMD until the tick is just above releaseStartTick (sells the INK the market bought in bands 0-1, ~7M INK, paying ~2.25% in fees = ~158,000 INK), call addLiquidity() (lists 400,000 INK over [start+spacing, start+20,000]), swap IMD->INK back to the old tick, buying ~40% of the new position (the share of a 20,000-tick token0 range that lies in its lowest 11,600 ticks) at an average ~30% discount, worth ~50,000 INK. Expected: a listing price independent of the caller; actual: the caller chooses it within [floor, market]. Net negative in this configuration; positive only if liquidity between market and floor is thinner than the fee batch, which the doubling ladder is designed to prevent.","severity":"info","snippet":"        (int24 lower, int24 upper) = _rangeFrom(tick, width);","title":"addLiquidity() is permissionless and lists fee INK just above whatever the current tick is: a caller can move the price first in the same transaction, down to the release floor"},{"citation":"resolved","description":"release() transfers the unclaimed INK and then calls addLiquidity() in the same transaction. If the owner has not called InkLiquidity.setPool, or the pool is not initialized, or the position mint reverts for any reason, release() reverts, released stays false, and the hook keeps trading closed, pull()/rerollTraits() revert NotReleased, and the 24-hour window's INK sits in the vault. There is no owner escape hatch and no time fallback, which the hook's comment presents as intended. It is an operational dependency, not a defect, but the adapter and the launch runbook should treat 'setPool before claimEnd' as a hard step. Minor: the guard on line 199 reverts with InkNotSet when it is actually setup() that has not run (statsRoot == 0), which will confuse a keeper reading the revert.","line":207,"path":"src/InkVault.sol","reproduction":"Deploy all five as the task orders, owner calls setVault, setInk, setup(root, now, liquidity) but not InkLiquidity.setPool; warp past claimEnd with fewer than 2,000 claims; call vault.release(). Expected (from the README): unclaimed INK goes to the pool and trading opens. Actual: reverts PoolNotSet from addLiquidity(); released() stays false; vault.pull(1) reverts NotReleased. Calling release() before setup() reverts InkNotSet although ink is set.","severity":"info","snippet":"            IInkLiquidity(liquidity).addLiquidity();","title":"release() depends on InkLiquidity being fully set up and on addLiquidity() succeeding; until then nobody can release, pull, reroll or trade"},{"citation":"resolved","description":"Read in full, line by line, against the Pashov checklist (every state-changing external function: caller, what it moves, what it trusts): src/testnet/TestSeats.sol, src/InkPortraits.sol, src/InkVault.sol, src/InkLiquidity.sol, src/InkRenderer.sol, plus src/InkHook.sol for the trust the vault places in it (the hook and src/InkFeeRouter.sol are outside this deployment and were not audited for their own defects). Checks on this tree: forge test --offline (55 passed, 0 failed, 28 skipped fork tests that vm.skip without FORK_RPC_URL); forge build --sizes (all five runtimes under 24,576 bytes: TestSeats 3,814; InkPortraits 7,838; InkVault 10,380; InkLiquidity 9,692; InkRenderer 12,291); Slither 0.11.6 medium+high on src/ (no High; mediums are weak-prng on the documented blockhash draw, divide-before-multiply in tick rounding, a benign reentrancy-no-eth in deposit and unused returns); a scan of the five deployed runtimes for 0xf4/0xf2/0xff (none). Verified by reading: every owner role is the owner_ constructor argument (no msg.sender/tx.origin owners, InkPortraits royalty receiver is owner_ at 0 bps, InkLiquidity treasury only receives the IMD/ETH side of LP fees and holds no role); no constructor calls another contract; constructor argument counts match the task's order (0, 1, 3, 4, 2); contract names are short identifiers; no minting of INK, no pause/freeze/blacklist/seize on any balance, no proxy or upgrade path; the vault's backing invariant (INK held == 400,000 x portraits outside the vault, after release) held under the tree's own invariant suite and my hand-traced claim/release/pull/reveal/deposit/reroll sequences, including same-block deposits, renewals and agent-portrait recycling. Not reached: the Uniswap v4 position minting path of InkLiquidity against the real Sepolia PoolManager/PositionManager (no fork), and the renderer's byte-exact script and JS trait parity (art/original-art.js was not re-derived; only the Solidity side was read).","line":37,"path":"src/InkVault.sol","reproduction":"Not a defect. forge test --offline on this tree: 55 passed, 0 failed, 28 skipped; scratch tests for the four reproduced findings above were run with forge test --match-path test/scratch/… and then removed.","severity":"info","snippet":"contract InkVault is Ownable, ReentrancyGuard, IERC721Receiver {","title":"Coverage: contracts read in full, checks run, and what was not reached"}],"hash":"823d5693abeec2b7f23babe6945555a72ee9e220a822019268beea7f0d0558ef","nodeId":"03ddc5d2-0f08-422f-88f2-4eeda09c9f8d","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds 8 entries, every snippet verified against its line in the tree. No source file was changed; the scratch tests were deleted after running.\n\n**Outcome.** No critical or high defect. One medium, four low, three info. The vault's accounting, access control and randomness held up under adversarial tracing and the tree's own invariant suite.\n\n**Findings written to the report**\n\n- **Medium, `src/InkVault.sol:306`.** The admission loop inside every reveal is unbounded. Measured on this tree at 33.4k gas per pending deposit, so about 1,080 portraits deposited after the last revealed pull and before the next pull make that reveal, and every later one, exceed a 36M block forever. Every later puller's 400,000 INK is locked with no refund path. Heavy precondition (a whale holding over half the collection), which is why it is medium rather than high.\n- **Low, `src/InkVault.sol:406`.** The vault accepts `safeTransferFrom` of a portrait but never uses that path, so the token is stranded, no INK is paid and it never joins the draw. Reproduced.\n- **Low, `src/InkLiquidity.sol:100`.** `setPool` on a never-initialized pool records a zero floor tick permanently, and does not check the key's hook or counter-currency. Reproduced.\n- **Low, `src/InkVault.sol:356`.** A pending trait reroll does not block transfers, and the new seed is public one block after the request, so a seller can pass a known-bad repaint to a buyer. Reproduced.\n- **Low, launch policy.** Lists every post-deployment owner setter across the four configured contracts, with the reason most cannot move into constructors (INK and the pool do not exist yet, Portraits and Vault reference each other). All roles go to the `owner_` argument, none to `msg.sender` or `tx.origin`; the treasury holds no role.\n- **Info.** Permissionless `addLiquidity` lists at the spot tick (modelled as unprofitable to sandwich at the current fees), `release()` has no escape hatch if `setPool` was skipped, and a coverage entry.\n\n**Coverage.** Read in full: TestSeats, InkPortraits, InkVault, InkLiquidity, InkRenderer, plus InkHook for the trust the vault places in it. Checks run: the existing suite (55 passed, 0 failed, 28 fork tests skipped cleanly without an RPC), `forge build --sizes` (all five under EIP-170), Slither medium and high on `src/` (no High), and an opcode scan of the five runtimes (no DELEGATECALL, CALLCODE or SELFDESTRUCT). Not reached: the real Sepolia PoolManager/PositionManager minting path (no fork) and the renderer's JavaScript trait parity.\n\n**For the adapter.** Constructor argument counts and order match the task for all five contracts, and no constructor calls another contract, so the tree deploys as ordered. The one thing to settle before the manifest step is the post-deployment setter list in the low finding, since the recipe's letter forbids it while the task's own deploy order relies on it.","treeHash":null,"usage":{"cachedInputTokens":1355618,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":48398,"runtime":"claude","turns":24,"wallClockMs":761613}},{"artifacts":[],"attempt":1,"bundleHash":"a59bfc6c0dc9ad4e6c52390ea1b25ff4949dbcfe76d09bcc36422756b38c99dc","device":"6d3b3ab49d68191e","findings":[],"hash":"c469c37b0faef2d14f844e70c7596fbc1f51bf8f573a9d0996219ff8248ca2a5","nodeId":"1a6a9599-3116-4b77-8260-f67a3cab7ae4","outcome":"completed","summary":"Added failure-path, fuzz, and stateful vault/liquidity tests under `test/`, using local Uniswap v4 contracts.\n\nValidation passed:\n- `forge build`\n- Required-seed `forge test`: **106 passed, 0 failed, 28 skipped**\n\nLive Sepolia deployment and fork verification remain unperformed.","treeHash":"577f721e326bff86511a9f6479f6b56755a73c77","usage":{"cachedInputTokens":2836608,"inputTokens":126683,"model":"gpt-6-astra","outputTokens":26905,"runtime":"codex","turns":7,"wallClockMs":666301}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"5d667e4b0751bcb5","findings":[{"citation":"resolved","description":"Access-control / trust-gap finding (owner-triggered, so demoted to low per the validation gates). The system opens only through InkVault.release(), which does ink.safeTransfer(liquidity, inkToPool) and then IInkLiquidity(liquidity).addLiquidity(). addLiquidity() measures its own `ink` (set once in InkLiquidity.setPool) and reverts NothingToAdd when that balance is 0. The vault's INK is set once in setInk(), the liquidity address is set once in setup(), and the liquidity contract's INK/pool is set once in setPool(). None of the three validates the others: setup() does not require IInkLiquidity(liquidity_).poolSet(), does not compare IInkLiquidity(liquidity_).ink() with `ink`, and does not require liquidity_ to have code. Because statsRoot, ink, poolSet and liquidity are all guarded by AlreadySetUp / InkAlreadySet / PoolAlreadySet, a single wrong argument cannot be corrected afterwards: release() reverts on every call, `released` stays false, the hook's pool never opens (beforeSwap requires vault.released()), pull() stays closed (NotReleased), and the 400,000 INK of every unclaimed seat (640,000,000 INK for 1,600 unclaimed seats) is locked in the vault with no owner withdrawal path. This is a configuration hazard, not an unprivileged exploit; it is reported because the protocol's own release step is the only way out and a one-line consistency check in setup() removes the failure mode. The related recoverable case (owner calls setup() but has not yet called setPool() when claimEnd passes) is already covered by test_releaseFailureRollsBackUntilLiquidityIsConfigured.","line":156,"path":"src/InkVault.sol","reproduction":"State: InkVault funded with 2000*400_000e18 INK, owner calls portraits.setVault(vault); vault.setInk(INK); liq.setPool(key for a pool whose currencies are (ETH, WRONG) initialized at tick 0, IERC20(WRONG)) -> accepted; vault.setup(root, block.timestamp, address(liq)) -> accepted (no check that liq.ink() == INK). Warp to vault.claimEnd(). Call vault.release() from any address. Expected: unclaimed INK moves into the pool and released becomes true. Actual: InkLiquidity.addLiquidity() reverts NothingToAdd (its `ink` is WRONG, balance 0), release() reverts, released() == false, ink.balanceOf(vault) == 800_000_000e18. Afterwards vault.setInk(...) reverts InkAlreadySet, vault.setup(...) reverts AlreadySetUp, liq.setPool(...) reverts PoolAlreadySet. Variant: vault.setup(root, now, <EOA>) is accepted (only address(0) is rejected) and release() reverts forever because the high-level call to an address without code reverts. Both variants are in test/scratch/SetupConsistency.t.sol (2 passing tests that assert the permanent revert). Minimal fix preserving the design: in setup(), require IInkLiquidity(liquidity_).poolSet() and IInkLiquidity(liquidity_).ink() == ink (and optionally that the liquidity contract's poolKey contains ink), so the mistake reverts at configuration time while it is still correctable.","severity":"low","snippet":"        if (\n            root == bytes32(0) || liquidity_ == address(0) || start < block.timestamp\n                || start > block.timestamp + MAX_START_DELAY\n        ) revert BadSetup();","title":"setup()/setInk()/setPool() are three independent one-shot owner calls with no cross-check; one inconsistent value makes release() revert forever and strands the unclaimed INK"},{"citation":"resolved","description":"Access-control note on a callback that trusts whoever reaches it. beforeInitialize is correctly restricted to the PoolManager, but PoolManager.initialize(key, sqrtPrice) is permissionless for any key, and the hook accepts any pool whose hooks field is itself and whose other currency is `imd`: it records the counter-currency as `ink` and sets poolCreated. The hook has no constructor input naming the expected INK token (INK is created by the same launch), so it cannot reject a foreign counter-currency. If the IMD launch factory deploys the hook and initializes the real INK/IMD pool in the same transaction (as the launch recipe states), there is no window and this does not apply. If the hook is ever deployed in a separate transaction, or the hook's CREATE2 address is predictable before the factory transaction lands and the factory does not initialize the pool atomically, the hook can be bricked before the real pool exists. The hook is not part of this contracts-only Sepolia launch; this is recorded so the later hook launch's atomicity is verified rather than assumed.","line":121,"path":"src/InkHook.sol","reproduction":"Precondition: InkHook deployed (constructor imd=IMD) but the INK/IMD pool not yet initialized in the same transaction. Attacker deploys any ERC20 X and calls PoolManager.initialize(PoolKey(sorted(IMD, X), fee, tickSpacing, hooks=InkHook), sqrtPrice). beforeInitialize passes (key.hooks == this, one currency == imd), sets ink = X and poolCreated = true, emits PoolOpened(X). The launch's subsequent initialize of PoolKey(sorted(IMD, INK), ..., hooks=InkHook) reverts PoolAlreadyCreated. Expected: only the launch's INK/IMD pool can bind the hook. Actual: first caller wins; the hook must be redeployed. Mitigation if the deployment is not atomic: have the launch pass the expected INK address or restrict beforeInitialize's sender/pool (e.g. a one-shot `expectedInk` or factory-only initialization).","severity":"low","snippet":"        if (key.currency0 == imd) ink = key.currency1;\n        else if (key.currency1 == imd) ink = key.currency0;\n        else revert WrongPool();","title":"InkHook.beforeInitialize binds the hook to whichever pool is initialized first; if hook deployment and pool initialization are not in one transaction, anyone can brick the hook"},{"citation":"resolved","description":"Not a defect; the roles the brief requests are present and correctly assigned. Recorded as the trust surface the judge should weigh. (a) InkPortraits.setVault accepts any non-zero address once; whoever it names holds the onlyVault powers mintAgent/mintSwarm/setSwarmSeed for the life of the collection, with no check that it is the InkVault deployed in step 3. (b) InkPortraits.setRenderer may point at any contract until lockRenderer(); the renderer is only ever staticcalled (tokenURI is view), so it can change presentation but not state. (c) All four Ownable contracts use OpenZeppelin single-step transferOwnership and renounceOwnership; renouncing InkVault before setup() permanently strands any INK already sent to it, and transferring to a mistyped address does the same. (d) ERC-2981 receiver is the constructor owner at 0 bps and there is no setter; transferOwnership does not move it (harmless at 0 bps). (e) InkLiquidity.setWidth can be changed at any time and setPremium up to 100,000 ticks until the first position; both only shape where permissionless addLiquidity lists INK, never below the opening price. (f) The treasury (0xbc10...b3d4) is an EOA on Sepolia and mainnet (cast code returns 0x) and receives only the non-INK side of fees; it holds no role. Constructor ownership was verified: InkPortraits/InkVault/InkLiquidity/InkRenderer owner() == owner_ argument, not the factory; TestSeats has no owner.","line":83,"path":"src/InkPortraits.sol","reproduction":"Concrete state showing (a): owner calls portraits.setVault(0xAttackerEOA) instead of the vault address; that EOA can then call portraits.mintAgent(seat, anyone, arbitrary stats) for all 2,000 seats and portraits.mintSwarm(anyone, seed) with no INK backing; the real InkVault can never mint (OnlyVault) and setVault reverts VaultAlreadySet forever. Only the owner can cause this; a defensive check would be to require vault_.code.length != 0 and InkVault(vault_).portraits() == address(this). Concrete state showing (c): owner calls vault.renounceOwnership() before vault.setup(); setup() reverts OwnableUnauthorizedAccount for every caller, release() reverts InkNotSet forever, and the INK sent to the vault is unrecoverable.","severity":"info","snippet":"    function setVault(address vault_) external onlyOwner {\n        if (vault != address(0)) revert VaultAlreadySet();\n        vault = vault_;\n        emit VaultSet(vault_);\n    }","title":"Owner trust assumptions (documented, by design): setVault grants the minter role to any address, renderer is swappable until locked, ownership is single-step and renounceable on all four Ownable contr"},{"citation":"resolved","description":"InkVault.claim authenticates the caller only through seats.ownerOf(seat) == msg.sender plus a Merkle proof of the seat's stats. With TestSeats as `seats`, anyone can mint any unminted seat id and then claim that seat's agent portrait during the window, and the agent-holder perks (half reroll fee, hook fee waiver) follow. This matches the file's own notice and the Sepolia brief, so it is not a defect of this launch; it is recorded because the mainnet draft (launch/mainnet-1-contracts.json) must pass the real identity.md collection instead, and because InkVault has no way to change `seats` after deployment (immutable).","line":10,"path":"src/testnet/TestSeats.sol","reproduction":"On the Sepolia deployment: attacker calls TestSeats.mint(7) (seat 7 unminted) -> owns seat 7; during the claim window calls vault.claim(7, statsOf7, proofOf7) with the public snapshot proof -> portraits.ownerOf(7) == attacker, agentBalanceOf[attacker] == 1. Expected on mainnet: only the real identity.md seat holder can do this. Actual on Sepolia: anyone, by design.","severity":"info","snippet":"    function mint(uint256 seat) external {\n        _mint(msg.sender, seat);\n    }","title":"TestSeats.mint is permissionless, so on Sepolia any address can claim any seat's agent portrait (intended testnet stand-in; must never be the seats contract on mainnet)"}],"hash":"d22c7490fdc898f5c6f33b3cae1112c97ab4541e1fb75b08659862a978330bb0","nodeId":"bf2b468c-e99c-4144-87f6-2cb3df10fe25","outcome":"completed","summary":"The review is complete. `.imd-findings.json` holds 4 findings and 54 coverage rows, with every snippet verified against the tree. The only files I created are the findings file and the scratch test under `test/scratch/`, which the assignment allots to me.\n\n**Outcome:** no unprivileged access-control break, trust-gap exploit or asymmetric fee path was found in the five Sepolia contracts. The existing suite passes, and the owner roles match the brief.\n\n**What I verified**\n\n- Every Ownable contract takes the owner from its constructor argument, the ERC-2981 receiver is that owner at 0 bps, the treasury is a role-less immutable EOA, and TestSeats has no owner.\n- The Sepolia PoolManager address has code and the PositionManager reports it as its pool manager, checked over a live RPC. The adaptation notes had left this unverified.\n- Full Foundry suite excluding fork-only files: 71 passed, 0 failed, 14 skipped.\n- Traced every `onlyOwner`, `onlyVault` and `onlyPoolManager` guard, the vault's deposit, pull, reveal and reroll paths for ownership and fee-class asymmetries, and the liquidity contract's permissionless `addLiquidity` and `collectFees` for sandwichable or misdirected value. All hold.\n\n**Findings reported**\n\n1. **Low.** `setInk`, `setPool` and `setup` are three independent one-shot owner calls with no cross-check. A mismatched INK address or a non-contract liquidity address makes `release()` revert forever, locking the unclaimed seats' INK with no recovery. Owner-triggered, so demoted per the validation gates. Reproduced by two passing scratch tests in `test/scratch/SetupConsistency.t.sol`.\n2. **Low.** `InkHook.beforeInitialize` binds the hook to the first pool that pairs IMD with anything. This is only exploitable if the hook is deployed in a different transaction from the pool initialization, which the later hook launch must confirm. Not part of this launch.\n3. **Info.** Owner trust assumptions documented: `setVault` grants the minter role to any address, the renderer is swappable until locked, ownership is single-step and renounceable, and the royalty receiver is fixed to the constructor owner.\n4. **Info.** `TestSeats.mint` is permissionless, so any Sepolia address can claim any seat's portrait. Intended for the testnet stand-in and must not carry to mainnet.\n\n**Not reached:** no fork tests ran, since they need an RPC the suite skips without. Nothing else in my area was left unchecked.","treeHash":null,"usage":{"cachedInputTokens":1295282,"inputTokens":322,"model":"claude-fable-5-1","outputTokens":44160,"runtime":"claude","turns":29,"wallClockMs":786022}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"3a720671678111bc","findings":[{"citation":"resolved","description":"InkVault documents (lines 27-29) that a portrait handed in only joins the draw for pulls made in later blocks, so once a block hash is public nobody can change what a pull picks. _revealNext() enforces this by calling _admit(p.blockNumber) for the head pull only, and when that pull is renewed (line 295: p.blockNumber = uint64(block.number), because blockhash(blockNumber+1) expired or avail == 0) the next _admit uses the renewed block. Every deposit made before the renewed block is pushed into _inventory, and the pulls queued behind the head keep their original blockNumber and entropy, so they draw (pick = r % avail, inventory indexes [0, held) first) over portraits handed in after their own block by someone who already knew their r. A depositor can choose how many portraits k to hand in so that r % (unminted + k) lands on one of their own junk portraits or misses a valuable agent portrait sitting in inventory. Preconditions: the head pull must be renewed, which happens when nobody reveals it for 256 blocks (reveal is permissionless; a queued puller who reveals promptly is safe) or when avail == 0 at its reveal. No INK is lost; the documented fairness guarantee of the draw is broken under those conditions, hence medium. Merged: reported by audit_flow only. Minimal fix: keep the admission cutoff separate from the entropy block, e.g. store the original pull block (or never raise it on renewal) and pass that to _admit, so a renewal changes only which block hash is used, never which deposits are eligible for later pulls.","line":288,"path":"src/InkVault.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {InkPortraits} from \"src/InkPortraits.sol\";\nimport {InkVault} from \"src/InkVault.sol\";\nimport {TestSeats} from \"src/testnet/TestSeats.sol\";\n\ncontract MockInk is ERC20(\"INK\", \"INK\") {\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// Stands in for InkLiquidity: release() only needs addLiquidity() to succeed.\ncontract MockLiquidity {\n    function addLiquidity() external pure returns (uint256) {\n        return 1;\n    }\n}\n\n/// A pull's draw is documented as fixed once its block hash is public: \"a portrait handed in only joins the\n/// draw for pulls made in later blocks, so once a block hash is public nobody can change what it picks\".\n/// When an earlier pull is renewed (hash expired), _admit runs with the renewed block, so a deposit made\n/// AFTER a later pull's block (and after that pull's hash is public) joins the later pull's candidate set.\ncontract RenewalWidensDrawTest is Test {\n    uint256 constant PORTRAIT = 400_000e18;\n    address owner = makeAddr(\"owner\");\n    address attacker = makeAddr(\"attacker\");\n    address victim = makeAddr(\"victim\");\n\n    MockInk ink;\n    TestSeats seats;\n    InkPortraits portraits;\n    InkVault vault;\n    MockLiquidity liq;\n\n    function setUp() public {\n        vm.roll(100);\n        ink = new MockInk();\n        seats = new TestSeats();\n        portraits = new InkPortraits(owner);\n        vault = new InkVault(portraits, seats, owner);\n        liq = new MockLiquidity();\n        vm.prank(owner);\n        portraits.setVault(address(vault));\n        ink.mint(address(vault), 2000 * PORTRAIT);\n        vm.startPrank(owner);\n        vault.setInk(IERC20(address(ink)));\n        vault.setup(keccak256(\"root\"), uint64(block.timestamp), address(liq));\n        vm.stopPrank();\n        vm.warp(block.timestamp + 24 hours);\n        vault.release();\n        ink.mint(attacker, 10 * PORTRAIT);\n        ink.mint(victim, 10 * PORTRAIT);\n        vm.prank(attacker);\n        ink.approve(address(vault), type(uint256).max);\n        vm.prank(victim);\n        ink.approve(address(vault), type(uint256).max);\n        vm.prank(attacker);\n        portraits.setApprovalForAll(address(vault), true);\n    }\n\n    function test_depositAfterLaterPullsHashIsPublicJoinsItsDraw() public {\n        // Attacker owns two swarm portraits (pulled and revealed earlier).\n        vm.prank(attacker);\n        vault.pull(2);\n        vm.roll(block.number + 2);\n        vault.reveal(1);\n        uint256 junkA = 10_000;\n        uint256 junkB = 10_001;\n        assertEq(portraits.ownerOf(junkA), attacker);\n        assertEq(vault.inventorySize(), 0);\n\n        // Pull A (attacker) at block 1000, pull B (victim) at block 1003. Nobody reveals A.\n        vm.roll(1000);\n        vm.prank(attacker);\n        uint256 pullA = vault.pull(1);\n        vm.roll(1003);\n        vm.prank(victim);\n        uint256 pullB = vault.pull(1);\n        assertEq(pullB, pullA + 1);\n\n        // Block 1010: blockhash(1004), pull B's entropy, is public. The attacker hands in both junk portraits.\n        // Per the documented rule they can only join draws of pulls made after block 1010.\n        vm.roll(1010);\n        assertTrue(blockhash(1004) != bytes32(0));\n        uint256[] memory ids = new uint256[](2);\n        ids[0] = junkA;\n        ids[1] = junkB;\n        vm.prank(attacker);\n        vault.deposit(ids, 0);\n        assertEq(vault.waitingCount(), 2);\n\n        // Block 1258: pull A's hash (block 1001) has expired, pull B's (block 1004) has not. A is renewed.\n        vm.roll(1258);\n        assertEq(blockhash(1001), bytes32(0));\n        assertTrue(blockhash(1004) != bytes32(0));\n        assertFalse(vault.reveal(pullA));\n        (, uint64 renewedBlock,) = vault.pulls(pullA);\n        assertEq(renewedBlock, 1258);\n\n        // Block 1260: both reveal. Admission runs with A's renewed block (1258), so the block-1010 deposits are\n        // admitted into the inventory, and pull B (block 1003, original hash still valid) draws over them.\n        vm.roll(1260);\n        assertTrue(blockhash(1004) != bytes32(0));\n        assertTrue(vault.reveal(pullB));\n        (, uint64 bBlock, bool bDone) = vault.pulls(pullB);\n        assertTrue(bDone);\n        assertEq(bBlock, 1003, \"pull B drew with its original block hash\");\n        // Expected: the two deposits are still waiting (they were handed in after pull B's block).\n        // Actual: they were admitted and were candidates for pull B.\n        assertEq(vault.waitingCount(), 2, \"deposits made after pull B's block joined pull B's draw\");\n    }\n}","reproduction":"State: vault released; attacker holds swarm portraits 10000 and 10001 and approved the vault. Block 1000: attacker pull(1) -> pull A. Block 1003: victim pull(1) -> pull B. Block 1010 (blockhash(1004), B's entropy, is public): attacker deposit([10000,10001], 0) -> waitingCount()==2. Block 1258: reveal(A) returns false, emits PullRenewed(A, 1258) since blockhash(1001)==0; pulls(A).blockNumber==1258. Block 1260: reveal(B): _admit(1258) moves both block-1010 deposits into _inventory, A draws with blockhash(1259), then B draws with its ORIGINAL blockhash(1004) over an inventory holding the two portraits deposited 7 blocks after B was made. Expected (contract's own rule): waitingCount()==2 after B reveals. Actual: waitingCount()==0 and pulls(B).blockNumber==1003. Ran the attached test: FAIL 'deposits made after pull B's block joined pull B's draw: 0 != 2'.","severity":"medium","snippet":"        if (!_admit(p.blockNumber)) {","title":"Renewing the head pull admits later deposits into a queued pull whose block hash is already public"},{"citation":"resolved","description":"Merged from four specialists (audit_flow setInk, audit_math setup-with-no-code, audit_economics INK mismatch, audit_permissions setup/setInk/setPool, plus the setVault item of its trust note): one root cause. The system opens only through release() (line 213-214: ink.safeTransfer(liquidity, inkToPool); IInkLiquidity(liquidity).addLiquidity()), and every input it depends on is set exactly once with no corrective path: InkVault.setInk (InkAlreadySet, line 146), InkVault.setup (AlreadySetUp, line 155; liquidity_ only checked against address(0)), InkLiquidity.setPool (PoolAlreadySet, line 98) and InkPortraits.setVault (VaultAlreadySet, line 84). None checks the others: setup() does not require liquidity_ to have code, nor IInkLiquidity(liquidity_).poolSet(), nor IInkLiquidity(liquidity_).ink() == ink; setInk() does not check the vault actually holds SEATS*PORTRAIT of that token (that check is in the retryable setup(), not in the irreversible setInk()); setVault() does not check the target is an InkVault bound to this collection. In each wrong case release() reverts on every call, released stays false, the hook never opens trading, pull() stays NotReleased, and the vault's INK has no exit except agent-portrait deposits. The same irreversibility applies to renounceOwnership()/transferOwnership() before setup(). This is owner error, not an unprivileged path, so low; the impact when it happens is permanent. Minimal fix preserving the design: in setup() require liquidity_.code.length > 0, IInkLiquidity(liquidity_).poolSet() and IInkLiquidity(liquidity_).ink() == ink; in setInk() require ink_.balanceOf(address(this)) >= SEATS*PORTRAIT or allow setInk() to be re-called until setup() succeeds; in setVault() require InkVault(vault_).portraits() == address(this).","line":157,"path":"src/InkVault.sol","reproduction":"test/scratch/JudgeRepro.t.sol, 3 passing tests that assert the stuck state on the current code. (1) owner setInk(INK); setup(root, now, 0x1234 EOA) accepted, liquidity()==0x1234; warp +24h; release() reverts (call to address without code), released()==false, vault still holds 800,000,000e18 INK; setup() again reverts AlreadySetUp, setInk() reverts InkAlreadySet. (2) owner setInk(WRONG) accepted; setInk(INK) reverts InkAlreadySet; setup(...) reverts NotFunded forever; release() reverts InkNotSet; 800,000,000e18 real INK stays in the vault. (3) liq.setPool(key(WRONG, X), WRONG) on an initialized pool; vault.setInk(INK); vault.setup(root, now, liq) accepted; warp +24h; release() -> INK.safeTransfer(liq, 800M) then liq.addLiquidity() reads WRONG balance 0 -> revert NothingToAdd, whole release() reverts; setPool(key, INK) reverts PoolAlreadySet. Expected in all three: the wrong input is rejected while still correctable.","severity":"low","snippet":"            root == bytes32(0) || liquidity_ == address(0) || start < block.timestamp","title":"Write-once setters (setInk, setup, setPool, setVault) take no validation or cross-check; one wrong owner input permanently strands the 800,000,000 INK allocation"},{"citation":"resolved","description":"PoolManager.initialize is permissionless for any key. beforeInitialize only requires key.hooks == this and that one currency is imd; it records the other currency as ink and sets poolCreated, after which any further pool reverts PoolAlreadyCreated (line 119). The hook has no constructor input naming the expected INK token, so it cannot tell the launch's INK from an attacker's token. If the hook is deployed and the real INK pool is initialized in the same factory transaction, there is no window; the hook is not in this contracts-only manifest (launch.json) and belongs to the later univ4_hook launch, so this is recorded at low for that launch's atomicity to be checked rather than assumed. Reported by audit_permissions; kept with a local reproduction. If the deployment is not atomic, a one-shot expected-INK argument or a factory-only initializer closes it.","line":121,"path":"src/InkHook.sol","reproduction":"test/scratch/JudgeRepro.t.sol test_hookBindsToFirstInitializer (passes, asserting the defect): deploy InkHook(manager, portraits, vault, imd=IMD, treasury, 25) at an address with the BEFORE_INITIALIZE|BEFORE_SWAP|AFTER_SWAP|AFTER_SWAP_RETURNS_DELTA flags; attacker deploys token X and calls manager.initialize(PoolKey(sorted(X, IMD), 12500, 200, hook), sqrtPrice(0)). Expected: only the launch's INK/IMD pool can bind the hook. Actual: poolCreated()==true, ink()==X, and manager.initialize(PoolKey(sorted(INK, IMD), 12500, 200, hook), ...) reverts (PoolAlreadyCreated wrapped by the PoolManager).","severity":"low","snippet":"        if (key.currency0 == imd) ink = key.currency1;\n        else if (key.currency1 == imd) ink = key.currency0;\n        else revert WrongPool();","title":"InkHook.beforeInitialize binds the hook to the first IMD-paired pool anyone initializes with it"},{"citation":"resolved","description":"Merged from audit_economics and audit_permissions (same root cause). InkVault.claim authenticates only seats.ownerOf(seat) == msg.sender plus a Merkle proof against statsRoot, which by the vault's own comment contains every seat. With TestSeats as seats, anyone can mint any unminted seat, claim its agent portrait during the window, hand it to deposit() for exactly 400,000 INK with no fee, and, by claiming all 2,000 seats before claimEnd, make release() send 0 INK to the pool and leave available()==0 so pull() reverts NoneAvailable. This is what the file's notice and the brief ask for on Sepolia; it is not a defect of this launch and nothing in the contracts should change. Recorded so the rehearsal publishes a statsRoot covering only team-controlled seats (or mints them first), and so the mainnet manifest (launch/mainnet-1-contracts.json) passes the real identity.md collection, since InkVault.seats is immutable.","line":10,"path":"src/testnet/TestSeats.sol","reproduction":"Sepolia deployment after setup(root, start, liq) with a root covering all seats: attacker calls TestSeats.mint(7) (unminted) -> owns seat 7; during the window calls vault.claim(7, statsOf(7), proofOf(7)) from the published stats -> portraits.ownerOf(7)==attacker; attacker.setApprovalForAll(vault) then vault.deposit([7], 400_000e18) -> attacker receives 400,000 INK, fee 0 (isAgentPortrait). Repeating for every seat before claimEnd: release() emits Released(0, 0), swarmSlots()==0, pull(1) reverts NoneAvailable. Expected on mainnet: only the real seat holder; on Sepolia: anyone, by design.","severity":"info","snippet":"    function mint(uint256 seat) external {\n        _mint(msg.sender, seat);\n    }","title":"TestSeats.mint is permissionless, so on Sepolia any address can claim any seat's agent portrait and redeem it for 400,000 INK (intended rehearsal stand-in)"},{"citation":"resolved","description":"foundry.toml sets [fmt] line_length = 110 and the formatter rewraps the signatures at lines 118 (beforeInitialize), 148 (afterSwap) and 242 (afterDonate) of src/InkHook.sol; the other six source files pass. Formatting only, no behavioural effect, and the hook is outside this contracts-only manifest; recorded because a verifier that runs the project's formatter check on src/ would reject the tree. Running forge fmt src/InkHook.sol changes no logic.","line":118,"path":"src/InkHook.sol","reproduction":"Run `forge fmt --check src` at the repository root. Expected: exit 0, no diff. Actual: exit 1, 'Diff in src/InkHook.sol:' with the three signatures rewrapped; `forge fmt --check src/InkVault.sol src/InkPortraits.sol src/InkLiquidity.sol src/InkRenderer.sol src/InkFeeRouter.sol src/testnet/TestSeats.sol` exits 0.","severity":"info","snippet":"    function beforeInitialize(address, PoolKey calldata key, uint160) external onlyPoolManager returns (bytes4) {","title":"forge fmt --check fails on src/InkHook.sol (three function signatures exceed the configured 110-column width)"}],"hash":"ea55121e8b01c85fca63e1bceb35360290c32363ee0cb3f72d58dc4e9bc9eaf4","nodeId":"af109a30-50a4-4f5f-a937-24682a929e24","outcome":"completed","summary":"Review complete. The findings file at `.imd-findings.json` holds 5 findings and 55 coverage rows, one for each of the 52 listed entry points plus three invariants.\n\n**What I kept, after reproducing each against the code**\n\n- **Medium, InkVault.reveal / revealNext.** Renewing the head pull passes the renewed block to `_admit`, so deposits made after a queued pull's block, and after its block hash is public, join that pull's draw. The specialist's proof fails on the current code for exactly that reason, and it is attached. No INK is lost. The documented fairness rule is broken when the head pull idles past 256 blocks or hits zero availability.\n- **Low, merged from four specialists.** The write-once setters `setInk`, `setup`, `setPool` and `setVault` validate nothing about each other. A wrong token, an EOA as liquidity, or an INK mismatch makes `release` revert forever and strands the 800 million INK allocation. Reproduced with three scratch tests in `test/scratch/JudgeRepro.t.sol`. Owner error only, so low.\n- **Low, InkHook.beforeInitialize.** The first IMD-paired pool initialized with the hook binds it, whoever initializes it. Reproduced locally with a real PoolManager. The hook belongs to the later hook launch, so this is a note for that launch's atomicity.\n- **Info, TestSeats.** Permissionless mint lets anyone claim any seat and redeem it for 400,000 INK on Sepolia. By design per the brief, merged from two specialists.\n- **Info, formatting.** The formatter check fails on three over-long signatures in `src/InkHook.sol`. The other six source files pass.\n\n**What I dropped or folded**\n\n- The separate owner trust-surface note from audit_permissions is folded into the merged low finding, since its concrete items share that root cause.\n- No slither or aderyn line reproduced as a defect beyond the weak-PRNG lead, which is the documented blockhash design and only matters through the admission bug above.\n\n**Checks run**\n\n- Existing suite: 106 passed, 0 failed, 28 skipped (fork and env-gated tests).\n- Manifest: keys, names and constructor argument order match each constructor. The Sepolia PoolManager and PositionManager addresses have code and the PositionManager points at that PoolManager.\n- Deployability: no constructor calls another contract. Largest runtime is InkRenderer at about 12.7 KB, well under the limit.\n\nNothing in the tree outside `test/scratch/` and `.imd-findings.json` was changed.","treeHash":null,"usage":{"cachedInputTokens":1361390,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":33619,"runtime":"claude","turns":24,"wallClockMs":538801}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"468e82a89b9bfe18","findings":[{"citation":"resolved","description":"Economic Security guide, 'break dependencies': construct a dependency failure that permanently blocks claims. release() is the only way the pool unlocks (InkHook.beforeSwap requires vault.released()) and the only way pulls open. It transfers the vault's INK (vault.ink, fixed once by setInk) to `liquidity` (fixed once by setup) and then requires InkLiquidity.addLiquidity() to succeed, which reads its own `ink` (fixed once by setPool, together with the pool key and floor). Every one of those three settings is write-once and there is no corrective setter, no timeout fallback in the hook, and no sweep. If the two INK addresses differ, or setPool was called on a pool whose INK is not the vault's token, addLiquidity() sees a zero balance of its own `ink` and reverts NothingToAdd; release() rolls back every time, `released` stays false, no swap is ever possible, no pull is ever possible, and the 2,000 x 400,000 INK stays in the vault with no path out except agent-portrait deposits (which only return 400,000 per claimed seat). The only 'repair' is for someone to donate >= MIN_ADD (400,000) of InkLiquidity's token so addLiquidity() succeeds, after which the released INK that was transferred to InkLiquidity is stranded there (it is not the token InkLiquidity lists). The admission brief fixes the deployment order and forbids extra admin roles, so this is a trust/operations finding: the owner must verify vault.ink() == InkLiquidity.ink() and that InkLiquidity.poolKey contains that token before the 24-hour window closes, and the deployer handoff should record that check. Reported low because it requires an owner misconfiguration (Pashov gate 3 demotes admin-only triggers), but with permanent, unrecoverable impact.","line":213,"path":"src/InkVault.sol","reproduction":"State: deploy per the brief; two ERC-20s T1 and T2. Owner calls vault.setInk(T1); 800M T1 sits in the vault; owner calls liq.setPool(key(T2, IMD), T2) on an initialized pool; owner calls vault.setup(root, now, address(liq)). Warp past claimEnd with at least one unclaimed seat. Call vault.release(). Expected (per the contract's own docs): unclaimed INK is listed and the pool opens. Actual: release() -> T1.safeTransfer(liq, unclaimed*400k) -> liq.addLiquidity() -> `amount = ink.balanceOf(address(this))` reads T2 balance 0 -> revert NothingToAdd() -> the whole release() reverts. Repeat at any later time: identical revert. vault.released() == false forever; InkHook.beforeSwap reverts TradingNotOpen on every swap; vault.pull reverts NotReleased. No owner function can change vault.ink, vault.liquidity, or liq.ink/poolKey (setInk: InkAlreadySet; setup: AlreadySetUp; setPool: PoolAlreadySet). Sending 400,000 T2 to liq lets release() complete but then the released T1 sits in InkLiquidity, which has no function that moves T1.","severity":"low","snippet":"            ink.safeTransfer(liquidity, inkToPool);\n            IInkLiquidity(liquidity).addLiquidity();","title":"One-shot setInk/setup/setPool chain has no recovery: an INK mismatch between vault and InkLiquidity makes release() revert forever and strands the 800M INK and the pool lock"},{"citation":"resolved","description":"Documented and intended for the Sepolia rehearsal ('Anyone can mint any unminted seat number'), so this is an informational note on the economic consequence rather than a defect in the production design. Because InkVault.claim() only checks seats.ownerOf(seat) == msg.sender and a Merkle proof against statsRoot (which, per the vault's own comment, contains every seat including idle ones with zeros), with TestSeats as the seat collection any address can take every unclaimed seat during the 24-hour window. Two economic effects on the Sepolia deployment follow: (1) each claimed agent portrait can be handed to InkVault.deposit() for exactly 400,000 INK with no fee, so the whole 800M INK vault allocation is extractable by anyone who claims the seats; (2) if all 2,000 seats are claimed before claimEnd, release() sends 0 INK to InkLiquidity (unclaimed == 0, the addLiquidity branch is skipped), swarmSlots() == 0, available() == 0 and every pull() reverts NoneAvailable until someone deposits a portrait, so the pull/reveal/reroll flows that the rehearsal is meant to exercise cannot be exercised by the intended testers if a third party claims the seats first. Mitigation for the rehearsal: publish a statsRoot that only contains the seats the team controls, or mint the seats before publishing the vault address. Nothing to change in the contracts.","line":10,"path":"src/testnet/TestSeats.sol","reproduction":"Sepolia, after the owner's setup(root, start, liq) with a root covering all 2,000 seats. Attacker A, for seat s in 0..1999 not yet minted: TestSeats.mint(s); InkVault.claim(s, statsOf(s), proofOf(s)) where statsOf/proofOf are built from the published stats list. Each call mints agent portrait s to A and leaves 400,000 INK in the vault as its backing. Then A.approve(vault) and vault.deposit([s], 400_000e18) -> A receives 400,000 INK, fee 0 (isAgentPortrait). Expected for a rehearsal: only the team's seats are claimable. Actual: every seat is claimable by anyone, 800M INK is withdrawable by anyone over 2,000 deposits, and if A claims all seats before claimEnd then release() emits Released(0, 0) and vault.pull(1) reverts NoneAvailable for every caller.","severity":"info","snippet":"    function mint(uint256 seat) external {\n        _mint(msg.sender, seat);","title":"Sepolia stand-in: anyone can mint every TestSeats seat, claim all 2,000 agent portraits, redeem each for 400,000 INK, and leave zero swarm slots so pull() can never succeed in the rehearsal"}],"hash":"fc3d2f5a946858d7bb2ee7f3c78bbacb727483151fc16904529dfb833effbb2c","nodeId":"478cd71b-84ef-465f-9b66-af099aa2e806","outcome":"completed","summary":"Review complete. The findings file is written and validated, the scratch test is removed, and no repository file was changed.\n\n**Outcome:** no critical, high or medium economic defects. The vault's backing invariant holds exactly: vault INK equals 400,000 per portrait outside the vault plus per pending pull, through claim, release, pull, reveal, deposit and trait reroll, with no rounding slack needed. The pull capacity check and the strict deposit-block cutoff mean an unprivileged actor cannot steer or re-draw a known pick. The hook's fee sign, currency and claim accounting match Uniswap v4's afterSwap delta handling. InkLiquidity positions cannot be removed by anyone.\n\n**Findings written to `.imd-findings.json`:**\n\n- **Low:** the setInk, setup and setPool settings are each write-once with no correction path. If the vault's INK and InkLiquidity's INK differ, release reverts with NothingToAdd forever, the pool never unlocks, pulls never open, and the 800M INK is stranded. Owner misconfiguration is required, so it is a trust and operations finding, with a concrete state sequence.\n- **Info:** on Sepolia, TestSeats lets anyone mint every seat, claim all 2,000 agent portraits, redeem each for 400,000 INK fee-free, and leave zero swarm slots so pull can never succeed in the rehearsal. This is the documented stand-in behaviour. The mitigation is a stats root limited to team seats.\n\n**Leads checked and dropped:**\n\n- Spot-following permissionless listing, previously disposed as a tradeoff. I measured it in the project's own harness: selling to the floor, relisting 400k fee INK, and buying back cost the attacker 23.8 IMD versus 3.58 IMD at market. Not profitable.\n- Both Sepolia Uniswap addresses in the adaptation have code, and the PositionManager reports the listed PoolManager.\n- Slither leads (weak PRNG, strict equality, reentrancy in deposit) traced to acknowledged next-block randomness, a correct ladder boundary, and a nonReentrant path with view-only callbacks.\n\n**Coverage:** all 52 listed entry points have rows, plus three invariant rows. The existing suite passed 71 tests with 28 fork and fixture tests skipped.","treeHash":null,"usage":{"cachedInputTokens":1758018,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":52527,"runtime":"claude","turns":30,"wallClockMs":860851}}],"verification":[{"checks":[{"durationMs":13409,"exitCode":0,"name":"build","output":"Compiling 186 files with Solc 0.8.26\nSolc 0.8.26 finished in 13.25s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n  --> script/LocalStack.s.sol:95:26:\n   |\n95 |     function _deployHook(InkPortraits portraits, InkVault vault, address treasury) internal returns (InkHook) {\n   |                          ^^^^^^^^^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:38:5:\n   |\n38 |     InkPortraits portraits;\n   |     ^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n  --> script/LocalStack.s.sol:95:50:\n   |\n95 |     function _deployHook(InkPortraits portraits, InkVault vault, address treasury) internal returns (InkHook) {\n   |                                                  ^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:39:5:\n   |\n39 |     InkVault vault;\n   |     ^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> script/LocalStack.s.sol:119:20:\n    |\n119 |     function _seed(PoolKey memory key, bool inkIs0, address to, ForkInk ink) internal {\n    |                    ^^^^^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:44:5:\n   |\n44 |     PoolKey key;\n   |     ^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> script/LocalStack.s.sol:119:65:\n    |\n119 |     function _seed(PoolKey memory key, bool inkIs0, address to, ForkInk ink) internal {\n    |                                                                 ^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:43:5:\n   |\n43 |     ForkInk ink;\n   |     ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint128) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/InkLiquidity.sol:158:31:\n    |\n158 |             uint128 unitLiq = uint128(1e24) << k;\n    |                               ^^^^^^^^^^^^^^^^^^\n\n","passed":true},{"durationMs":30617,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/LaunchFeeHandoff.t.sol:LaunchFeeHandoffTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 273.09µs (0.00ns CPU time)\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium1xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 629.23µs (21.43µs CPU time)\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium5xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 603.26µs (36.04µs CPU time)\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium2xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 706.70µs (16.03µs CPU time)\n\nRan 1 test for test/LaunchDay.t.sol:LaunchDayTest\n[SKIP] test_fork_launchDay() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 791.96µs (20.34µs CPU time)\n\nRan 7 tests for test/MainnetFork.t.sol:MainnetForkInkIs0Test\n[SKIP] test_fork_agentHolderPaysNoHookFee() (gas: 0)\n[SKIP] test_fork_bigBuyAfterRelease() (gas: 0)\n[SKIP] test_fork_fullLifecycle() (gas: 0)\n[SKIP] test_fork_hookFeeExactInBuyIsInkToLiquidity() (gas: 0)\n[SKIP] test_fork_lockedUntilReleaseHoweverLong() (gas: 0)\n[SKIP] test_fork_sellsPayFeesBothWays() (gas: 0)\n[SKIP] test_fork_sybilFirstHour() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 7 skipped; finished in 809.48µs (155.68µs CPU time)\n\nRan 7 tests for test/MainnetFork.t.sol:MainnetForkInkIs1Test\n[SKIP] test_fork_agentHolderPaysNoHookFee() (gas: 0)\n[SKIP] test_fork_bigBuyAfterRelease() (gas: 0)\n[SKIP] test_fork_fullLifecycle() (gas: 0)\n[SKIP] test_fork_hookFeeExactInBuyIsInkToLiquidity() (gas: 0)\n[SKIP] test_fork_lockedUntilReleaseHoweverLong() (gas: 0)\n[SKIP] test_fork_sellsPayFeesBothWays() (gas: 0)\n[SKIP] test_fork_sybilFirstHour() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 7 skipped; finished in 961.35µs (125.67µs CPU time)\n\nRan 9 tests for test/SnapshotAllSeats.t.sol:SnapshotAllSeatsTest\n[SKIP] test_claims_0000() (gas: 0)\n[SKIP] test_claims_0250() (gas: 0)\n[SKIP] test_claims_0500() (gas: 0)\n[SKIP] test_claims_0750() (gas: 0)\n[SKIP] test_claims_1000() (gas: 0)\n[SKIP] test_claims_1250() (gas: 0)\n[SKIP] test_claims_1500() (gas: 0)\n[SKIP] test_claims_1750() (gas: 0)\n[SKIP] test_treeHasEverySeatOnce() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 9 skipped; finished in 1.00ms (1.33ms CPU time)\n\nRan 2 tests for test/SnapshotProofs.t.sol:SnapshotProofsTest\n[PASS] test_realSnapshotProofsClaim() (gas: 3207992)\n[PASS] test_tamperedStatsAreRejected() (gas: 605207)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 4.48ms (4.14ms CPU time)\n\nRan 4 tests for test/SepoliaLaunch.t.sol:SepoliaLaunchTest\n[PASS] test_allFiveRuntimesMeetProtectedFloor() (gas: 16068981)\n[PASS] test_existingOwnerSetupRemainsRequiredAndAvailable() (gas: 430716)\n[PASS] test_factoryDeploymentPreservesOwnersRoyaltiesAndReferences() (gas: 183934)\n[PASS] test_factoryTreasuryAndOutsiderHaveNoAdminRole() (gas: 1701118)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 24.63ms (24.86ms CPU time)\n\nRan 1 test for test/TokenURIPreview.t.sol:TokenURIPreviewTest\n[PASS] test_writeTokenURIs() (gas: 25225288)\nLogs:\n  read-only profile: not written out-preview/agent-280.txt (use FOUNDRY_PROFILE=preview)\n  read-only profile: not written out-preview/pool.txt (use FOUNDRY_PROFILE=preview)\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 36.90ms (36.60ms CPU time)\n\nRan 6 tests for test/RendererStorageProperties.t.sol:RendererStoragePropertiesTest\n[PASS] testFuzz_chunkRoundTripPreservesBinaryDataAndOrder(bytes,bytes) (runs: 1000, μ: 276707, ~: 272617)\n[PASS] testFuzz_nonexistentPortraitCannotExposeCollectionTokenUri(uint256) (runs: 1000, μ: 13212, ~: 13212)\n[PASS] test_artAndPointerLocksSurviveOwnershipTransfer() (gas: 470247)\n[PASS] test_failedEmptyLockRemainsConfigurableByOwnerOnly() (gas: 264200)\n[PASS] test_maximumChunkFitsEip170AndNextByteRevertsAtomically() (gas: 9409333)\n[PASS] test_testSeatsCannotRemintASeatAfterItChangesHands() (gas: 213508)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 87.15ms (108.20ms CPU time)\n\nRan 6 tests for test/InkRenderer.t.sol:InkRendererTest\n[PASS] test_agentTraitsMatchTheScript() (gas: 61685138)\n[PASS] test_noClaimsAfterTheFirstSwarmPortrait() (gas: 399350)\n[PASS] test_poolTraitsMatchTheScript() (gas: 60520738)\n[PASS] test_scriptStoredInChunksAndLocked() (gas: 12171334)\n[PASS] test_swarmNumbersFillUnclaimedSeats() (gas: 155416619)\n[PASS] test_tokenURIForAnAgentPortrait() (gas: 3511800)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 284.65ms (328.58ms CPU time)\n\nRan 12 tests for test/VaultFailurePaths.t.sol:VaultFailurePathsTest\n[PASS] testFuzz_batchDepositConservesBackingForAllAllowedFees(uint16,uint16,uint8,bool) (runs: 1000, μ: 1288760, ~: 1250795)\n[PASS] testFuzz_claimRejectsEveryAlteredStatsField(uint8) (runs: 1000, μ: 309881, ~: 310050)\n[PASS] test_allowanceFailureLeavesPullQueueUntouched() (gas: 238173)\n[PASS] test_claimWindowUsesInclusiveStartAndExclusiveEnd() (gas: 526398)\n[PASS] test_claimedAgentCanExitBeforeReleaseWithoutSpendingUnclaimedBacking() (gas: 619226)\n[PASS] test_duplicateAndForeignNftsRevertTheWholeBatch() (gas: 1124192)\n[PASS] test_failedDepositPaymentRollsBackNftCustodyAndIncomingQueue() (gas: 722984)\n[PASS] test_failedFeeTransferRollsBackEvenTheCompletedUserPayment() (gas: 746412)\n[PASS] test_failedPullDoesNotChargeOrReserveAndCanRetry() (gas: 721491)\n[PASS] test_failedTraitFeeDoesNotLockPortraitOrConsumeReroll() (gas: 654891)\n[PASS] test_seatApprovalDoesNotGrantClaimAndSeatTransferCannotDoubleClaim() (gas: 583414)\n[PASS] test_transferFromCallbackCannotReenterPull() (gas: 441604)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 306.24ms (510.91ms CPU time)\n\nRan 46 tests for test/InkSystem.t.sol:InkSystemTest\n[PASS] test_agentClaimsOwnPortraitFree() (gas: 307623)\n[PASS] test_agentDepositReturnsExactlyItsBacking() (gas: 3444670)\n[PASS] test_agentHoldersPayNoHookFee() (gas: 2729098)\n[PASS] test_agentHoldersRerollForHalf() (gas: 4090911)\n[PASS] test_borrowedAgentPortraitGetsNoWaiver() (gas: 2542113)\n[PASS] test_cantLockAnEmptyRenderer() (gas: 3077030)\n[PASS] test_claimRules() (gas: 458186)\n[PASS] test_collectFeesPaysImdToTreasuryAndKeepsInk() (gas: 3365728)\n[PASS] test_contractURIOwnerOnly() (gas: 94674)\n[PASS] test_contractWalletSeatCanClaim() (gas: 383617)\n[PASS] test_depositMinOut() (gas: 2977033)\n[PASS] test_depositedAgentPortraitIsPullable() (gas: 3286517)\n[PASS] test_feeRouterClaimAndRoute() (gas: 4374318)\n[PASS] test_feeRouterNeedsPoolAndAddresses() (gas: 3237985)\n[PASS] test_feeRouterSplitsAndCompounds() (gas: 4120625)\n[PASS] test_gasAtScale() (gas: 54586630)\nLogs:\n  pull(1) gas: 135304\n  revealNext(1) gas: 175105\n    per reveal: 175105\n  pull(10) gas: 322102\n  revealNext(10) gas: 696371\n    per reveal: 69637\n  pull(100) gas: 2532239\n  revealNext(100) gas: 6424530\n    per reveal: 64245\n  pull(400) gas: 9901430\n  revealNext(400) gas: 25551314\n    per reveal: 63878\n  deposit(100 portraits) gas: 6546515\n\n[PASS] test_guards() (gas: 11481192)\n[PASS] test_hookFeeImdToTreasury() (gas: 2209380)\n[PASS] test_hookFeeInkGoesToLiquidity() (gas: 2700451)\n[PASS] test_idleSeatClaimsToo() (gas: 223531)\n[PASS] test_inkFeesGoBackIntoThePool() (gas: 6021484)\n[PASS] test_launchSplit() (gas: 67288)\n[PASS] test_liquidityFloorHoldsIfPriceIsDragged() (gas: 4676817)\n[PASS] test_noPerWalletLimitAfterRelease() (gas: 2123236)\n[PASS] test_noPullsUntilRelease() (gas: 2175273)\n[PASS] test_onlyOnePool() (gas: 46322)\n[PASS] test_ownerIsOwnerWalletNotDeployer() (gas: 6854235)\n[PASS] test_permissionlessListingTracksSpotAboveReleaseFloor() (gas: 3359646)\n[PASS] test_poolLockedUntilRelease() (gas: 2617014)\n[PASS] test_poolNeverOpensWithoutRelease() (gas: 2475796)\n[PASS] test_premiumBeforePoolCannotBeFrontRun() (gas: 4839563)\n[PASS] test_pullRerollPullAgain() (gas: 3801768)\n[PASS] test_releaseLadderShape() (gas: 2318388)\n[PASS] test_releasePremiumListsAboveTheOpeningPrice() (gas: 1923886)\n[PASS] test_releaseTurnsUnclaimedSeatsIntoSlotsAndPoolInk() (gas: 2069112)\n[PASS] test_revealIsNotEarlyAndNotTwice() (gas: 2569927)\n[PASS] test_seatSoldDuringWindowNewOwnerClaims() (gas: 297827)\n[PASS] test_setupRules() (gas: 2730867)\n[PASS] test_setupStartMustBeSoon() (gas: 2836133)\n[PASS] test_traitRerollBeforeReleaseReverts() (gas: 39919)\n[PASS] test_traitRerollExpiredHashRenews() (gas: 2936861)\n[PASS] test_traitRerollGuards() (gas: 3398991)\n[PASS] test_traitRerollHalfPriceForAgentHolders() (gas: 3067235)\n[PASS] test_traitRerollRepaintsSameToken() (gas: 2974695)\n[PASS] test_vaultStaysFullyBacked() (gas: 3846452)\n[PASS] test_zeroTradingFee() (gas: 29344)\nSuite result: ok. 46 passed; 0 failed; 0 skipped; finished in 318.72ms (94.01ms CPU time)\n\nRan 6 tests for test/LiquidityFailurePaths.t.sol:LiquidityFailurePathsInk0Test\n[PASS] testFuzz_ladderConservesEveryWei(uint256,uint24) (runs: 1000, μ: 2674536, ~: 2678121)\n[PASS] test_feeCollectionLeavesAllPositionPrincipalAndCustodyIntact() (gas: 4373261)\n[PASS] test_invalidFeeRangesRevertWithoutMovingFunds() (gas: 1956141)\n[PASS] test_ownerTreasuryAndCallerCannotTransferApproveOrBurnLockedPosition() (gas: 2442041)\n[PASS] test_positionManagerFailureRollsBackAndCanBeRetried() (gas: 2079521)\n[PASS] test_thresholdRejectsZeroAndDustWithoutMovingFunds() (gas: 2711365)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 318.86ms (331.78ms CPU time)\n\nRan 6 tests for test/LiquidityFailurePaths.t.sol:LiquidityFailurePathsInk1Test\n[PASS] testFuzz_ladderConservesEveryWei(uint256,uint24) (runs: 1000, μ: 2672040, ~: 2676330)\n[PASS] test_feeCollectionLeavesAllPositionPrincipalAndCustodyIntact() (gas: 4370461)\n[PASS] test_invalidFeeRangesRevertWithoutMovingFunds() (gas: 1957083)\n[PASS] test_ownerTreasuryAndCallerCannotTransferApproveOrBurnLockedPosition() (gas: 2442983)\n[PASS] test_positionManagerFailureRollsBackAndCanBeRetried() (gas: 2073880)\n[PASS] test_thresholdRejectsZeroAndDustWithoutMovingFunds() (gas: 2712307)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 318.94ms (329.29ms CPU time)\n\nRan 15 tests for test/AuditRegression.t.sol:AuditRegressionTest\n[PASS] testFuzz_admissionCompletesWithBothRevealEntryPoints(uint16,bool) (runs: 256, μ: 19145116, ~: 15385965)\n[PASS] test_directSafeTransfersRejectedForBothOverloadsAndOtherNFTs() (gas: 888526)\n[PASS] test_hashExpiryDuringAdmissionRenewsAndEventuallyReveals() (gas: 37570918)\n[PASS] test_initializedPoolAtZeroTickIsValid() (gas: 4545830)\n[PASS] test_largeAdmissionQueueMakesProgressWithinGasBudget() (gas: 366692738)\nLogs:\n  largest admission/reveal step gas (including call overhead): 3455847\n\n[PASS] test_partialAdmissionPreservesDrawAndExcludesSameBlockAndLaterDeposits() (gas: 19778596)\n[PASS] test_pendingRerollBlocksApprovedOperatorsAndReportsMetadataThroughRenewal() (gas: 1881046)\n[PASS] test_pendingRerollCannotBeSoldWithOldTraits() (gas: 740312)\n[PASS] test_releaseFailureRollsBackUntilLiquidityIsConfigured() (gas: 4779138)\n[PASS] test_renewalsKeepEachPullsOwnCutoff() (gas: 1691070)\n[PASS] test_renewedHeadPullNeverWidensALaterPullsDraw() (gas: 2047318)\n[PASS] test_safeTransferToVaultIsRejected() (gas: 523768)\n[PASS] test_sameBlockHandInsDoNotCountForPullsMadeNow() (gas: 181958490)\n[PASS] test_setupRejectsLiquidityWithoutCode() (gas: 281626)\n[PASS] test_uninitializedPoolCannotSetPermanentFloor() (gas: 4639751)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 370.55ms (563.59ms CPU time)\n\nRan 2 tests for test/LiquidityStateful.t.sol:LiquidityStatefulInk1Test\n[PASS]\nLiquidityStatefulInk1Test invariants:\n[PASS] invariant_allTokensConservedAcrossTradingListingAndFees\n[PASS] invariant_onlyImdFeesReachTreasuryAndInkCompounds\n[PASS] invariant_positionsCannotLosePrincipalOrCustody\n LiquidityStatefulInk1Test invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------------------+-------------------------+-------+---------+----------╮\n| Contract                 | Selector                | Calls | Reverts | Discards |\n+=================================================================================+\n| LiquiditySequenceHandler | add                     | 2311  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | changeWidth             | 2362  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | collect                 | 2354  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | donate                  | 2329  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | rejectRepricing         | 2360  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | rejectUnauthorizedAdmin | 2347  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | trade                   | 2321  | 0       | 0        |\n╰--------------------------+-------------------------+-------+---------+----------╯\n\n[PASS] test_sequenceActuallyTradesCollectsAndReinvests() (gas: 2910899)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 13.20s (13.20s CPU time)\n\nRan 2 tests for test/LiquidityStateful.t.sol:LiquidityStatefulInk0Test\n[PASS]\nLiquidityStatefulInk0Test invariants:\n[PASS] invariant_allTokensConservedAcrossTradingListingAndFees\n[PASS] invariant_onlyImdFeesReachTreasuryAndInkCompounds\n[PASS] invariant_positionsCannotLosePrincipalOrCustody\n LiquidityStatefulInk0Test invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------------------+-------------------------+-------+---------+----------╮\n| Contract                 | Selector                | Calls | Reverts | Discards |\n+=================================================================================+\n| LiquiditySequenceHandler | add                     | 2311  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | changeWidth             | 2362  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | collect                 | 2354  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | donate                  | 2329  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | rejectRepricing         | 2360  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | rejectUnauthorizedAdmin | 2347  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | trade                   | 2321  | 0       | 0        |\n╰--------------------------+-------------------------+-------+---------+----------╯\n\n[PASS] test_sequenceActuallyTradesCollectsAndReinvests() (gas: 2827543)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 13.39s (13.39s CPU time)\n\nRan 2 tests for test/VaultInvariants.t.sol:VaultInvariantsTest\n[PASS]\nVaultInvariantsTest invariants:\n[PASS] invariant_agentBalancesMatch\n[PASS] invariant_capAndSlots\n[PASS] invariant_custody\n[PASS] invariant_feesToLiquidity\n[PASS] invariant_fullyBacked\n[PASS] invariant_inkConserved\n[PASS] invariant_pullCutoffFixed\n[PASS] invariant_queueCovered\n VaultInvariantsTest invariants (runs: 256, calls: 30720, reverts: 0)\n\n╭--------------+------------------+-------+---------+----------╮\n| Contract     | Selector         | Calls | Reverts | Discards |\n+==============================================================+\n| VaultHandler | claim            | 2148  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | deposit          | 2073  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | donate           | 2108  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | moveSeat         | 2017  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pull             | 2064  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pullAll          | 2035  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pullTooMany      | 1979  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | release          | 2063  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | rerollTraits     | 1993  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | reveal           | 2007  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | revealNext       | 1989  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | revealTraits     | 2022  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | roll             | 2062  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | setFees          | 2080  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | transferPortrait | 2080  | 0       | 0        |\n╰--------------+------------------+-------+---------+----------╯\n\n[PASS] test_donationRemainsSurplusThroughEarlyRedemptionReleaseAndPull() (gas: 1783252)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 30.47s (30.46s CPU time)\n\nRan 21 test suites in 30.47s (59.14s CPU time): 110 tests passed, 0 failed, 28 skipped (138 total tests)\n","passed":true},{"durationMs":119,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"InkFeeRouter.claimAndRoute(address,uint64)\",\"InkFeeRouter.route()\",\"InkHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"InkHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"InkHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"InkHook.collectFees()\",\"InkHook.unlockCallback(bytes)\",\"InkLiquidity.addLiquidity()\",\"InkLiquidity.collectFees(uint256,uint256)\",\"InkLiquidity.renounceOwnership()\",\"InkLiquidity.setPool((address,address,uint24,int24,address),address)\",\"InkLiquidity.setPremium(int24)\",\"InkLiquidity.setWidth(int24)\",\"InkLiquidity.transferOwnership(address)\",\"InkPortraits.approve(address,uint256)\",\"InkPortraits.lockRenderer()\",\"InkPortraits.mintAgent(uint256,address,(uint32,uint16,uint32,uint32,bool))\",\"InkPortraits.mintSwarm(address,bytes32)\",\"InkPortraits.renounceOwnership()\",\"InkPortraits.safeTransferFrom(address,address,uint256)\",\"InkPortraits.safeTransferFrom(address,address,uint256,bytes)\",\"InkPortraits.setApprovalForAll(address,bool)\",\"InkPortraits.setContractURI(string)\",\"InkPortraits.setRenderer(address)\",\"InkPortraits.setSwarmSeed(uint256,bytes32)\",\"InkPortraits.setVault(address)\",\"InkPortraits.transferFrom(address,address,uint256)\",\"InkPortraits.transferOwnership(address)\",\"InkRenderer.addChunk(bytes)\",\"InkRenderer.lock()\",\"InkRenderer.renounceOwnership()\",\"InkRenderer.setPreviewBase(string)\",\"InkRenderer.transferOwnership(address)\",\"InkVault.claim(uint256,(uint32,uint16,uint32,uint32,bool),bytes32[])\",\"InkVault.deposit(uint256[],uint256)\",\"InkVault.pull(uint256)\",\"InkVault.release()\",\"InkVault.renounceOwnership()\",\"InkVault.rerollTraits(uint256,uint256)\",\"InkVault.reveal(uint256)\",\"InkVault.revealNext(uint256)\",\"InkVault.revealTraits(uint256)\",\"InkVault.setFees(uint16,uint16)\",\"InkVault.setInk(address)\",\"InkVault.setup(bytes32,uint64,address)\",\"InkVault.transferOwnership(address)\",\"TestSeats.approve(address,uint256)\",\"TestSeats.mint(uint256)\",\"TestSeats.safeTransferFrom(address,address,uint256)\",\"TestSeats.safeTransferFrom(address,address,uint256,bytes)\",\"TestSeats.setApprovalForAll(address,bool)\",\"TestSeats.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"ADAPTATION.md\":230,\"AUDIT_BRIEF.md\":71,\"BYTECODE_HASHES.json\":52,\"DEPENDENCIES.md\":10,\"PACKAGING.md\":16,\"README.md\":70,\"SETUP.md\":80,\"SHA256SUMS\":1638,\"SOURCE_PROVENANCE.json\":11363,\"VALIDATION.txt\":496,\"art/original-art.js\":418,\"foundry.toml\":33,\"launch/README.md\":28,\"launch/mainnet-1-contracts.json\":31,\"launch/mainnet-2-hook.json\":31,\"launch/sepolia-1-contracts.json\":31,\"launch/sepolia-2-hook.json\":31,\"remappings.txt\":8,\"script/Configure.s.sol\":63,\"script/InkSteps.sol\":122,\"script/LocalStack.s.sol\":135,\"script/Rehearse.s.sol\":236,\"script/UploadRenderer.s.sol\":33,\"src/InkFeeRouter.sol\":77,\"src/InkHook.sol\":255,\"src/InkLiquidity.sol\":295,\"src/InkPortraits.sol\":217,\"src/InkRenderer.sol\":304,\"src/InkVault.sol\":466,\"src/testnet/TestSeats.sol\":13,\"test/AuditRegression.t.sol\":466,\"test/InkRenderer.t.sol\":205,\"test/InkSystem.t.sol\":1241,\"test/LaunchDay.t.sol\":270,\"test/LaunchFeeHandoff.t.sol\":82,\"test/LiquidityFailurePaths.t.sol\":151,\"test/LiquidityStateful.t.sol\":287,\"test/MainnetFork.t.sol\":519,\"test/PROPERTY_COVERAGE.md\":40,\"test/RendererStorageProperties.t.sol\":129,\"test/SepoliaLaunch.t.sol\":186,\"test/SnapshotAllSeats.t.sol\":126,\"test/SnapshotProofs.t.sol\":86,\"test/SybilOptions.t.sol\":104,\"test/TokenURIPreview.t.sol\":78,\"test/VaultFailurePaths.t.sol\":373,\"test/VaultInvariants.t.sol\":468,\"test/fixtures/snapshot-sample.json\":98,\"test/fixtures/traits.json\":1,\"test/helpers/LiquidityFixture.sol\":110},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"13828115a745caa68cf99a2b4cd629827dcb1edb5b05c8586ac8f9b3600169bf","verifiedTreeHash":"2d1ec4da07af64ff0ae8134f1327149b0158445e","verifierVersion":"0.1.0+6a374fcd"},{"checks":[{"durationMs":10287,"exitCode":0,"name":"build","output":"Compiling 179 files with Solc 0.8.26\nSolc 0.8.26 finished in 10.14s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n  --> script/LocalStack.s.sol:95:26:\n   |\n95 |     function _deployHook(InkPortraits portraits, InkVault vault, address treasury) internal returns (InkHook) {\n   |                          ^^^^^^^^^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:38:5:\n   |\n38 |     InkPortraits portraits;\n   |     ^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n  --> script/LocalStack.s.sol:95:50:\n   |\n95 |     function _deployHook(InkPortraits portraits, InkVault vault, address treasury) internal returns (InkHook) {\n   |                                                  ^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:39:5:\n   |\n39 |     InkVault vault;\n   |     ^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> script/LocalStack.s.sol:119:20:\n    |\n119 |     function _seed(PoolKey memory key, bool inkIs0, address to, ForkInk ink) internal {\n    |                    ^^^^^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:44:5:\n   |\n44 |     PoolKey key;\n   |     ^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> script/LocalStack.s.sol:119:65:\n    |\n119 |     function _seed(PoolKey memory key, bool inkIs0, address to, ForkInk ink) internal {\n    |                                                                 ^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:43:5:\n   |\n43 |     ForkInk ink;\n   |     ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint128) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/InkLiquidity.sol:158:31:\n    |\n158 |             uint128 unitLiq = uint128(1e24) << k;\n    |                               ^^^^^^^^^^^^^^^^^^\n\n","passed":true},{"durationMs":3360,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/LaunchFeeHandoff.t.sol:LaunchFeeHandoffTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 254.12µs (0.00ns CPU time)\n\nRan 9 tests for test/SnapshotAllSeats.t.sol:SnapshotAllSeatsTest\n[SKIP] test_claims_0000() (gas: 0)\n[SKIP] test_claims_0250() (gas: 0)\n[SKIP] test_claims_0500() (gas: 0)\n[SKIP] test_claims_0750() (gas: 0)\n[SKIP] test_claims_1000() (gas: 0)\n[SKIP] test_claims_1250() (gas: 0)\n[SKIP] test_claims_1500() (gas: 0)\n[SKIP] test_claims_1750() (gas: 0)\n[SKIP] test_treeHasEverySeatOnce() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 9 skipped; finished in 630.27µs (1.16ms CPU time)\n\nRan 1 test for test/LaunchDay.t.sol:LaunchDayTest\n[SKIP] test_fork_launchDay() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 758.23µs (36.99µs CPU time)\n\nRan 7 tests for test/MainnetFork.t.sol:MainnetForkInkIs0Test\n[SKIP] test_fork_agentHolderPaysNoHookFee() (gas: 0)\n[SKIP] test_fork_bigBuyAfterRelease() (gas: 0)\n[SKIP] test_fork_fullLifecycle() (gas: 0)\n[SKIP] test_fork_hookFeeExactInBuyIsInkToLiquidity() (gas: 0)\n[SKIP] test_fork_lockedUntilReleaseHoweverLong() (gas: 0)\n[SKIP] test_fork_sellsPayFeesBothWays() (gas: 0)\n[SKIP] test_fork_sybilFirstHour() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 7 skipped; finished in 765.84µs (234.63µs CPU time)\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium1xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 698.92µs (40.93µs CPU time)\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium2xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.00ms (119.79µs CPU time)\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium5xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 967.16µs (40.58µs CPU time)\n\nRan 7 tests for test/MainnetFork.t.sol:MainnetForkInkIs1Test\n[SKIP] test_fork_agentHolderPaysNoHookFee() (gas: 0)\n[SKIP] test_fork_bigBuyAfterRelease() (gas: 0)\n[SKIP] test_fork_fullLifecycle() (gas: 0)\n[SKIP] test_fork_hookFeeExactInBuyIsInkToLiquidity() (gas: 0)\n[SKIP] test_fork_lockedUntilReleaseHoweverLong() (gas: 0)\n[SKIP] test_fork_sellsPayFeesBothWays() (gas: 0)\n[SKIP] test_fork_sybilFirstHour() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 7 skipped; finished in 1.97ms (172.99µs CPU time)\n\nRan 2 tests for test/SnapshotProofs.t.sol:SnapshotProofsTest\n[PASS] test_realSnapshotProofsClaim() (gas: 3207992)\n[PASS] test_tamperedStatsAreRejected() (gas: 605207)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 2.88ms (2.27ms CPU time)\n\nRan 4 tests for test/SepoliaLaunch.t.sol:SepoliaLaunchTest\n[PASS] test_allFiveRuntimesMeetProtectedFloor() (gas: 15835385)\n[PASS] test_existingOwnerSetupRemainsRequiredAndAvailable() (gas: 430748)\n[PASS] test_factoryDeploymentPreservesOwnersRoyaltiesAndReferences() (gas: 183912)\n[PASS] test_factoryTreasuryAndOutsiderHaveNoAdminRole() (gas: 1700815)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 23.76ms (24.16ms CPU time)\n\nRan 1 test for test/TokenURIPreview.t.sol:TokenURIPreviewTest\n[PASS] test_writeTokenURIs() (gas: 25222461)\nLogs:\n  read-only profile: not written out-preview/agent-280.txt (use FOUNDRY_PROFILE=preview)\n  read-only profile: not written out-preview/pool.txt (use FOUNDRY_PROFILE=preview)\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 45.63ms (45.39ms CPU time)\n\nRan 6 tests for test/InkRenderer.t.sol:InkRendererTest\n[PASS] test_agentTraitsMatchTheScript() (gas: 61685138)\n[PASS] test_noClaimsAfterTheFirstSwarmPortrait() (gas: 399350)\n[PASS] test_poolTraitsMatchTheScript() (gas: 60520738)\n[PASS] test_scriptStoredInChunksAndLocked() (gas: 12171334)\n[PASS] test_swarmNumbersFillUnclaimedSeats() (gas: 155416619)\n[PASS] test_tokenURIForAnAgentPortrait() (gas: 3508973)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 124.61ms (295.59ms CPU time)\n\nRan 46 tests for test/InkSystem.t.sol:InkSystemTest\n[PASS] test_agentClaimsOwnPortraitFree() (gas: 307623)\n[PASS] test_agentDepositReturnsExactlyItsBacking() (gas: 3412945)\n[PASS] test_agentHoldersPayNoHookFee() (gas: 2729098)\n[PASS] test_agentHoldersRerollForHalf() (gas: 4054745)\n[PASS] test_borrowedAgentPortraitGetsNoWaiver() (gas: 2542113)\n[PASS] test_cantLockAnEmptyRenderer() (gas: 3077030)\n[PASS] test_claimRules() (gas: 458186)\n[PASS] test_collectFeesPaysImdToTreasuryAndKeepsInk() (gas: 3365728)\n[PASS] test_contractURIOwnerOnly() (gas: 94674)\n[PASS] test_contractWalletSeatCanClaim() (gas: 383617)\n[PASS] test_depositMinOut() (gas: 2950400)\n[PASS] test_depositedAgentPortraitIsPullable() (gas: 3260327)\n[PASS] test_feeRouterClaimAndRoute() (gas: 4374318)\n[PASS] test_feeRouterNeedsPoolAndAddresses() (gas: 3237985)\n[PASS] test_feeRouterSplitsAndCompounds() (gas: 4120625)\n[PASS] test_gasAtScale() (gas: 53423162)\nLogs:\n  pull(1) gas: 133079\n  revealNext(1) gas: 172889\n    per reveal: 172889\n  pull(10) gas: 319877\n  revealNext(10) gas: 674211\n    per reveal: 67421\n  pull(100) gas: 2530014\n  revealNext(100) gas: 6202930\n    per reveal: 62029\n  pull(400) gas: 9899205\n  revealNext(400) gas: 24664914\n    per reveal: 61662\n  deposit(100 portraits) gas: 6524323\n\n[PASS] test_guards() (gas: 11372409)\n[PASS] test_hookFeeImdToTreasury() (gas: 2209380)\n[PASS] test_hookFeeInkGoesToLiquidity() (gas: 2700451)\n[PASS] test_idleSeatClaimsToo() (gas: 223531)\n[PASS] test_inkFeesGoBackIntoThePool() (gas: 5994851)\n[PASS] test_launchSplit() (gas: 67288)\n[PASS] test_liquidityFloorHoldsIfPriceIsDragged() (gas: 4676817)\n[PASS] test_noPerWalletLimitAfterRelease() (gas: 2123236)\n[PASS] test_noPullsUntilRelease() (gas: 2170854)\n[PASS] test_onlyOnePool() (gas: 46322)\n[PASS] test_ownerIsOwnerWalletNotDeployer() (gas: 6745359)\n[PASS] test_permissionlessListingTracksSpotAboveReleaseFloor() (gas: 3359646)\n[PASS] test_poolLockedUntilRelease() (gas: 2617014)\n[PASS] test_poolNeverOpensWithoutRelease() (gas: 2475796)\n[PASS] test_premiumBeforePoolCannotBeFrontRun() (gas: 4839563)\n[PASS] test_pullRerollPullAgain() (gas: 3770694)\n[PASS] test_releaseLadderShape() (gas: 2318388)\n[PASS] test_releasePremiumListsAboveTheOpeningPrice() (gas: 1923886)\n[PASS] test_releaseTurnsUnclaimedSeatsIntoSlotsAndPoolInk() (gas: 2069112)\n[PASS] test_revealIsNotEarlyAndNotTwice() (gas: 2565486)\n[PASS] test_seatSoldDuringWindowNewOwnerClaims() (gas: 297827)\n[PASS] test_setupRules() (gas: 2619391)\n[PASS] test_setupStartMustBeSoon() (gas: 2719457)\n[PASS] test_traitRerollBeforeReleaseReverts() (gas: 39919)\n[PASS] test_traitRerollExpiredHashRenews() (gas: 2932420)\n[PASS] test_traitRerollGuards() (gas: 3372358)\n[PASS] test_traitRerollHalfPriceForAgentHolders() (gas: 3062794)\n[PASS] test_traitRerollRepaintsSameToken() (gas: 2970254)\n[PASS] test_vaultStaysFullyBacked() (gas: 3810286)\n[PASS] test_zeroTradingFee() (gas: 29344)\nSuite result: ok. 46 passed; 0 failed; 0 skipped; finished in 124.64ms (128.02ms CPU time)\n\nRan 11 tests for test/AuditRegression.t.sol:AuditRegressionTest\n[PASS] testFuzz_admissionCompletesWithBothRevealEntryPoints(uint16,bool) (runs: 256, μ: 20113193, ~: 16682518)\n[PASS] test_directSafeTransfersRejectedForBothOverloadsAndOtherNFTs() (gas: 861937)\n[PASS] test_hashExpiryDuringAdmissionRenewsAndEventuallyReveals() (gas: 37066158)\n[PASS] test_initializedPoolAtZeroTickIsValid() (gas: 4545874)\n[PASS] test_largeAdmissionQueueMakesProgressWithinGasBudget() (gas: 362048933)\nLogs:\n  largest admission/reveal step gas (including call overhead): 3453631\n\n[PASS] test_partialAdmissionPreservesDrawAndExcludesSameBlockAndLaterDeposits() (gas: 19507307)\n[PASS] test_pendingRerollBlocksApprovedOperatorsAndReportsMetadataThroughRenewal() (gas: 1876582)\n[PASS] test_pendingRerollCannotBeSoldWithOldTraits() (gas: 735826)\n[PASS] test_releaseFailureRollsBackUntilLiquidityIsConfigured() (gas: 4776497)\n[PASS] test_safeTransferToVaultIsRejected() (gas: 519349)\n[PASS] test_uninitializedPoolCannotSetPermanentFloor() (gas: 4639773)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 306.18ms (446.57ms CPU time)\n\nRan 1 test for test/VaultInvariants.t.sol:VaultInvariantsTest\n[PASS]\nVaultInvariantsTest invariants:\n[PASS] invariant_agentBalancesMatch\n[PASS] invariant_capAndSlots\n[PASS] invariant_custody\n[PASS] invariant_feesToLiquidity\n[PASS] invariant_fullyBacked\n[PASS] invariant_inkConserved\n[PASS] invariant_queueCovered\n VaultInvariantsTest invariants (runs: 64, calls: 7680, reverts: 0)\n\n╭--------------+------------------+-------+---------+----------╮\n| Contract     | Selector         | Calls | Reverts | Discards |\n+==============================================================+\n| VaultHandler | claim            | 611   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | deposit          | 531   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | moveSeat         | 554   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pull             | 555   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pullAll          | 548   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pullTooMany      | 557   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | release          | 524   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | rerollTraits     | 531   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | reveal           | 574   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | revealNext       | 516   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | revealTraits     | 559   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | roll             | 548   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | setFees          | 553   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | transferPortrait | 519   | 0       | 0        |\n╰--------------+------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.23s (3.23s CPU time)\n\nRan 15 test suites in 3.23s (3.87s CPU time): 71 tests passed, 0 failed, 28 skipped (99 total tests)\n","passed":true},{"durationMs":91,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"InkFeeRouter.claimAndRoute(address,uint64)\",\"InkFeeRouter.route()\",\"InkHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"InkHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"InkHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"InkHook.collectFees()\",\"InkHook.unlockCallback(bytes)\",\"InkLiquidity.addLiquidity()\",\"InkLiquidity.collectFees(uint256,uint256)\",\"InkLiquidity.renounceOwnership()\",\"InkLiquidity.setPool((address,address,uint24,int24,address),address)\",\"InkLiquidity.setPremium(int24)\",\"InkLiquidity.setWidth(int24)\",\"InkLiquidity.transferOwnership(address)\",\"InkPortraits.approve(address,uint256)\",\"InkPortraits.lockRenderer()\",\"InkPortraits.mintAgent(uint256,address,(uint32,uint16,uint32,uint32,bool))\",\"InkPortraits.mintSwarm(address,bytes32)\",\"InkPortraits.renounceOwnership()\",\"InkPortraits.safeTransferFrom(address,address,uint256)\",\"InkPortraits.safeTransferFrom(address,address,uint256,bytes)\",\"InkPortraits.setApprovalForAll(address,bool)\",\"InkPortraits.setContractURI(string)\",\"InkPortraits.setRenderer(address)\",\"InkPortraits.setSwarmSeed(uint256,bytes32)\",\"InkPortraits.setVault(address)\",\"InkPortraits.transferFrom(address,address,uint256)\",\"InkPortraits.transferOwnership(address)\",\"InkRenderer.addChunk(bytes)\",\"InkRenderer.lock()\",\"InkRenderer.renounceOwnership()\",\"InkRenderer.setPreviewBase(string)\",\"InkRenderer.transferOwnership(address)\",\"InkVault.claim(uint256,(uint32,uint16,uint32,uint32,bool),bytes32[])\",\"InkVault.deposit(uint256[],uint256)\",\"InkVault.pull(uint256)\",\"InkVault.release()\",\"InkVault.renounceOwnership()\",\"InkVault.rerollTraits(uint256,uint256)\",\"InkVault.reveal(uint256)\",\"InkVault.revealNext(uint256)\",\"InkVault.revealTraits(uint256)\",\"InkVault.setFees(uint16,uint16)\",\"InkVault.setInk(address)\",\"InkVault.setup(bytes32,uint64,address)\",\"InkVault.transferOwnership(address)\",\"TestSeats.approve(address,uint256)\",\"TestSeats.mint(uint256)\",\"TestSeats.safeTransferFrom(address,address,uint256)\",\"TestSeats.safeTransferFrom(address,address,uint256,bytes)\",\"TestSeats.setApprovalForAll(address,bool)\",\"TestSeats.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"ADAPTATION.md\":182,\"AUDIT_BRIEF.md\":71,\"BYTECODE_HASHES.json\":52,\"DEPENDENCIES.md\":10,\"PACKAGING.md\":16,\"README.md\":65,\"SETUP.md\":80,\"SHA256SUMS\":1638,\"SOURCE_PROVENANCE.json\":11363,\"VALIDATION.txt\":496,\"art/original-art.js\":418,\"foundry.toml\":33,\"launch/README.md\":28,\"launch/mainnet-1-contracts.json\":31,\"launch/mainnet-2-hook.json\":31,\"launch/sepolia-1-contracts.json\":31,\"launch/sepolia-2-hook.json\":31,\"remappings.txt\":8,\"script/Configure.s.sol\":63,\"script/InkSteps.sol\":122,\"script/LocalStack.s.sol\":135,\"script/Rehearse.s.sol\":236,\"script/UploadRenderer.s.sol\":33,\"src/InkFeeRouter.sol\":77,\"src/InkHook.sol\":245,\"src/InkLiquidity.sol\":295,\"src/InkPortraits.sol\":217,\"src/InkRenderer.sol\":304,\"src/InkVault.sol\":431,\"src/testnet/TestSeats.sol\":13,\"test/AuditRegression.t.sol\":334,\"test/InkRenderer.t.sol\":180,\"test/InkSystem.t.sol\":1241,\"test/LaunchDay.t.sol\":270,\"test/LaunchFeeHandoff.t.sol\":82,\"test/MainnetFork.t.sol\":519,\"test/SepoliaLaunch.t.sol\":186,\"test/SnapshotAllSeats.t.sol\":126,\"test/SnapshotProofs.t.sol\":82,\"test/SybilOptions.t.sol\":104,\"test/TokenURIPreview.t.sol\":70,\"test/VaultInvariants.t.sol\":395,\"test/fixtures/snapshot-sample.json\":98,\"test/fixtures/traits.json\":1},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":4717,"exitCode":0,"name":"slither","output":"[high/medium] weak-prng at src/InkVault.sol:282: InkVault._revealNext() (src/InkVault.sol#282-316) uses a weak PRNG: \"pick = r % avail (src/InkVault.sol#303)\"\n[medium/medium] divide-before-multiply at src/InkLiquidity.sol:290: InkLiquidity._floor(int24,int24) (src/InkLiquidity.sol#290-294) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/InkLiquidity.sol:223: InkLiquidity._rangeFrom(int24,int24) (src/InkLiquidity.sol#223-242) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/InkLiquidity.sol:127: InkLiquidity.addLiquidity() (src/InkLiquidity.sol#127-137) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/InkVault.sol:338: Reentrancy in InkVault.deposit(uint256[],uint256) (src/InkVault.sol#338-362):\n[medium/medium] uninitialized-local at src/InkLiquidity.sol:150: InkLiquidity._addLadder(int24,uint256).total (src/InkLiquidity.sol#150) is a local variable never initialized\n[medium/medium] uninitialized-local at src/InkRenderer.sol:110: InkRenderer.tokenJSON(uint256).extra (src/InkRenderer.sol#110) is a local variable never initialized\n[medium/medium] uninitialized-local at src/InkVault.sol:345: InkVault.deposit(uint256[],uint256).fees (src/InkVault.sol#345) is a local variable never initialized\n[medium/medium] unused-return at src/InkLiquidity.sol:97: InkLiquidity.setPool(PoolKey,IERC20) (src/InkLiquidity.sol#97-108) ignores return value by (sqrtPriceX96,tick,None,None) = poolManager.getSlot0(key.toId()) (src/InkLiquidity.sol#101)\n[medium/medium] unused-return at src/InkFeeRouter.sol:54: InkFeeRouter.claimAndRoute(ILaunchFeeFactory,uint64) (src/InkFeeRouter.sol#54-61) ignores return value by factory.claimFees(launchNumber) (src/InkFeeRouter.sol#58)\n[medium/medium] unused-return at src/InkVault.sol:205: InkVault.release() (src/InkVault.sol#205-217) ignores return value by IInkLiquidity(liquidity).addLiquidity() (src/InkVault.sol#214)\n[medium/medium] unused-return at src/InkLiquidity.sol:127: InkLiquidity.addLiquidity() (src/InkLiquidity.sol#127-137) ignores return value by (None,tick,None,None) = poolManager.getSlot0(poolKey.toId()) (src/InkLiquidity.sol#132)\n[medium/high] write-after-write at src/InkRenderer.sol:236: InkRenderer.swarmLabels(string).u (src/InkRenderer.sol#236) is written in both\n[medium/high] write-after-write at src/InkRenderer.sol:236: InkRenderer.swarmLabels(string).u (src/InkRenderer.sol#236) is written in both\n[low/medium] missing-zero-check at src/InkPortraits.sol:83: InkPortraits.setVault(address).vault_ (src/InkPortraits.sol#83) lacks a zero-check on :\n[low/medium] missing-zero-check at src/InkPortraits.sol:89: InkPortraits.setRenderer(address).renderer_ (src/InkPortraits.sol#89) lacks a zero-check on :\n[low/medium] calls-loop at src/InkVault.sol:282: InkVault._revealNext() (src/InkVault.sol#282-316) has external calls inside a loop: tokenId_scope_0 = portraits.mintSwarm(p.to,keccak256(bytes)(abi.encode(entropy,pullId,seed))) (src/InkVault.sol#312)\n[low/medium] calls-loop at src/InkVault.sol:338: InkVault.deposit(uint256[],uint256) (src/InkVault.sol#338-362) has external calls inside a loop: portraits.transferFrom(msg.sender,address(this),tokenId) (src/InkVault.sol#349)\n[low/medium] calls-loop at src/InkLiquidity.sol:244: InkLiquidity._mintSingleSided(int24,int24,uint256) (src/InkLiquidity.sol#244-280) has external calls inside a loop: positionId = positionManager.nextTokenId() (src/InkLiquidity.sol#257)\n[low/medium] calls-loop at src/InkLiquidity.sol:244: InkLiquidity._mintSingleSided(int24,int24,uint256) (src/InkLiquidity.sol#244-280) has external calls inside a loop: positionManager.modifyLiquidities(abi.encode(abi.encodePacked(uint8(Actions.MINT_POSITION),uint8(Actions.SETTLE),uint8(Actions.SWEEP)),params),block.timestamp) (src/InkLiquidity.sol#273-279)\n[low/medium] calls-loop at src/InkVault.sol:282: InkVault._revealNext() (src/InkVault.sol#282-316) has external calls inside a loop: portraits.transferFrom(address(this),p.to,tokenId) (src/InkVault.sol#309)\n[low/medium] calls-loop at src/InkVault.sol:282: InkVault._revealNext() (src/InkVault.sol#282-316) has external calls inside a loop: tokenId_scope_0 = portraits.mintSwarm(p.to,keccak256(bytes)(abi.encode(entropy,pullId,seed))) (src/InkVault.sol#312)\n[low/medium] calls-loop at src/InkVault.sol:222: InkVault.swarmSlots() (src/InkVault.sol#222-225) has external calls inside a loop: SEATS - portraits.agentMinted() (src/InkVault.sol#224)\n[low/medium] calls-loop at src/InkVault.sol:228: InkVault.available() (src/InkVault.sol#228-230) has external calls inside a loop: _inventory.length + swarmSlots() - portraits.swarmMinted() (src/InkVault.sol#229)\n[low/medium] calls-loop at src/InkVault.sol:222: InkVault.swarmSlots() (src/InkVault.sol#222-225) has external calls inside a loop: SEATS - portraits.agentMinted() (src/InkVault.sol#224)\n[low/medium] calls-loop at src/InkVault.sol:338: InkVault.deposit(uint256[],uint256) (src/InkVault.sol#338-362) has external calls inside a loop: portraits.isAgentPortrait(tokenId) (src/InkVault.sol#352-354)\n[low/medium] calls-loop at src/InkVault.sol:282: InkVault._revealNext() (src/InkVault.sol#282-316) has external calls inside a loop: portraits.transferFrom(address(this),p.to,tokenId) (src/InkVault.sol#309)\n[low/medium] calls-loop at src/InkVault.sol:228: InkVault.available() (src/InkVault.sol#228-230) has external calls inside a loop: _inventory.length + swarmSlots() - portraits.swarmMinted() (src/InkVault.sol#229)\n[low/medium] reentrancy-benign at src/InkLiquidity.sol:178: Reentrancy in InkLiquidity._add(int24,int24,uint256) (src/InkLiquidity.sol#178-182):\n[low/medium] reentrancy-events at src/InkLiquidity.sol:187: Reentrancy in InkLiquidity.collectFees(uint256,uint256) (src/InkLiquidity.sol#187-214):\n[low/medium] reentrancy-events at src/InkHook.sol:182: Reentrancy in InkHook._payOut(Currency,address) (src/InkHook.sol#182-188):\n[low/medium] reentrancy-events at src/InkFeeRouter.sol:65: Reentrancy in InkFeeRouter.route() (src/InkFeeRouter.sol#65-76):\n[low/medium] reentrancy-events at src/InkFeeRouter.sol:54: Reentrancy in InkFeeRouter.claimAndRoute(ILaunchFeeFactory,uint64) (src/InkFeeRouter.sol#54-61):\n[low/medium] reentrancy-events at src/InkFeeRouter.sol:54: Reentrancy in InkFeeRouter.claimAndRoute(ILaunchFeeFactory,uint64) (src/InkFeeRouter.sol#54-61):\n[low/medium] reentrancy-events at src/InkHook.sol:148: Reentrancy in InkHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/InkHook.sol#148-168):\n[low/medium] reentrancy-events at src/InkLiquidity.sol:178: Reentrancy in InkLiquidity._add(int24,int24,uint256) (src/InkLiquidity.sol#178-182):\n[low/medium] timestamp at src/InkVault.sol:205: InkVault.release() (src/InkVault.sol#205-217) uses timestamp for comparisons\n[low/medium] timestamp at src/InkHook.sol:131: InkHook.tradingOpen() (src/InkHook.sol#131-133) uses timestamp for comparisons\n[low/medium] timestamp at src/InkHook.sol:135: InkHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/InkHook.sol#135-146) uses timestamp for comparisons\n[low/medium] timestamp at src/InkVault.sol:153: InkVault.setup(bytes32,uint64,address) (src/InkVault.sol#153-166) uses timestamp for comparisons\n[low/medium] timestamp at src/InkVault.sol:177: InkVault.claimOpen() (src/InkVault.sol#177-179) uses timestamp for comparisons\n[low/medium] timestamp at src/InkVault.sol:222: InkVault.swarmSlots() (src/InkVault.sol#222-225) uses timestamp for comparisons","passed":true},{"durationMs":2788,"exitCode":0,"name":"aderyn","output":"[high] abi-encode-packed-hash-collision at src/InkRenderer.sol:56: `abi.encodePacked()` Hash Collision (4 places)\n[high] reentrancy-state-change at src/InkHook.sol:141: Reentrancy: State change after external call (8 places)\n[high] unsafe-casting at src/InkLiquidity.sol:265: Unsafe Casting of integers (2 places)\n[low] centralization-risk at src/InkLiquidity.sol:32: Centralization Risk (17 places)\n[low] costly-loop at src/InkLiquidity.sol:169: Costly operations inside loop (6 places)\n[low] large-numeric-literal at src/InkHook.sol:161: Large Numeric Literal (6 places)\n[low] literal-instead-of-constant at src/InkPortraits.sol:118: Literal Instead of Constant (35 places)\n[low] require-revert-in-loop at src/InkLiquidity.sol:152: Loop Contains `require`/`revert` (4 places)\n[low] state-no-address-check at src/InkLiquidity.sol:104: Address State Variable Set Without Checks (4 places)\n[low] state-variable-could-be-constant at src/InkRenderer.sol:20: State Variable Could Be Constant\n[low] storage-array-length-not-cached at src/InkRenderer.sol:87: Storage Array Length not Cached\n[low] unchecked-return at src/InkFeeRouter.sol:58: Unchecked Return (5 places)\n[low] uninitialized-local-variable at src/InkLiquidity.sol:169: Uninitialized Local Variable (7 places)\n[low] unsafe-oz-erc721-mint at src/InkPortraits.sol:123: Unsafe `ERC721::_mint()` (3 places)\n[low] unused-public-function at src/InkHook.sol:131: Public Function Not Used Internally","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"18db3d4316a7383ff698a1335333d4aab9510ac46ff2561c6ae9050419f5ef9e","verifiedTreeHash":"5103c176237603d9484b166dd8100fbab40acd7f","verifierVersion":"0.1.0+6a374fcd"},{"checks":[{"durationMs":20776,"exitCode":0,"name":"build","output":"Compiling 179 files with Solc 0.8.26\nSolc 0.8.26 finished in 20.52s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n  --> script/LocalStack.s.sol:95:26:\n   |\n95 |     function _deployHook(InkPortraits portraits, InkVault vault, address treasury) internal returns (InkHook) {\n   |                          ^^^^^^^^^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:38:5:\n   |\n38 |     InkPortraits portraits;\n   |     ^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n  --> script/LocalStack.s.sol:95:50:\n   |\n95 |     function _deployHook(InkPortraits portraits, InkVault vault, address treasury) internal returns (InkHook) {\n   |                                                  ^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:39:5:\n   |\n39 |     InkVault vault;\n   |     ^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> script/LocalStack.s.sol:119:20:\n    |\n119 |     function _seed(PoolKey memory key, bool inkIs0, address to, ForkInk ink) internal {\n    |                    ^^^^^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:44:5:\n   |\n44 |     PoolKey key;\n   |     ^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> script/LocalStack.s.sol:119:65:\n    |\n119 |     function _seed(PoolKey memory key, bool inkIs0, address to, ForkInk ink) internal {\n    |                                                                 ^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:43:5:\n   |\n43 |     ForkInk ink;\n   |     ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint128) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/InkLiquidity.sol:158:31:\n    |\n158 |             uint128 unitLiq = uint128(1e24) << k;\n    |                               ^^^^^^^^^^^^^^^^^^\n\n","passed":true},{"durationMs":5388,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium2xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.08ms (39.30µs CPU time)\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium5xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.37ms (36.93µs CPU time)\n\nRan 1 test for test/LaunchDay.t.sol:LaunchDayTest\n[SKIP] test_fork_launchDay() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.45ms (47.69µs CPU time)\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium1xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 960.70µs (32.44µs CPU time)\n\nRan 9 tests for test/SnapshotAllSeats.t.sol:SnapshotAllSeatsTest\n[SKIP] test_claims_0000() (gas: 0)\n[SKIP] test_claims_0250() (gas: 0)\n[SKIP] test_claims_0500() (gas: 0)\n[SKIP] test_claims_0750() (gas: 0)\n[SKIP] test_claims_1000() (gas: 0)\n[SKIP] test_claims_1250() (gas: 0)\n[SKIP] test_claims_1500() (gas: 0)\n[SKIP] test_claims_1750() (gas: 0)\n[SKIP] test_treeHasEverySeatOnce() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 9 skipped; finished in 8.62ms (47.56ms CPU time)\n\nRan 1 test for test/LaunchFeeHandoff.t.sol:LaunchFeeHandoffTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 6.80ms (0.00ns CPU time)\n\nRan 2 tests for test/SnapshotProofs.t.sol:SnapshotProofsTest\n[PASS] test_realSnapshotProofsClaim() (gas: 3207992)\n[PASS] test_tamperedStatsAreRejected() (gas: 605207)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 13.99ms (8.98ms CPU time)\n\nRan 4 tests for test/SepoliaLaunch.t.sol:SepoliaLaunchTest\n[PASS] test_allFiveRuntimesMeetProtectedFloor() (gas: 16068981)\n[PASS] test_existingOwnerSetupRemainsRequiredAndAvailable() (gas: 430716)\n[PASS] test_factoryDeploymentPreservesOwnersRoyaltiesAndReferences() (gas: 183934)\n[PASS] test_factoryTreasuryAndOutsiderHaveNoAdminRole() (gas: 1701118)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 48.65ms (56.40ms CPU time)\n\nRan 1 test for test/TokenURIPreview.t.sol:TokenURIPreviewTest\n[PASS] test_writeTokenURIs() (gas: 25225288)\nLogs:\n  read-only profile: not written out-preview/agent-280.txt (use FOUNDRY_PROFILE=preview)\n  read-only profile: not written out-preview/pool.txt (use FOUNDRY_PROFILE=preview)\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 59.89ms (57.27ms CPU time)\n\nRan 6 tests for test/InkRenderer.t.sol:InkRendererTest\n[PASS] test_agentTraitsMatchTheScript() (gas: 61685138)\n[PASS] test_noClaimsAfterTheFirstSwarmPortrait() (gas: 399350)\n[PASS] test_poolTraitsMatchTheScript() (gas: 60520738)\n[PASS] test_scriptStoredInChunksAndLocked() (gas: 12171334)\n[PASS] test_swarmNumbersFillUnclaimedSeats() (gas: 155416619)\n[PASS] test_tokenURIForAnAgentPortrait() (gas: 3511800)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 237.56ms (607.49ms CPU time)\n\nRan 46 tests for test/InkSystem.t.sol:InkSystemTest\n[PASS] test_agentClaimsOwnPortraitFree() (gas: 307623)\n[PASS] test_agentDepositReturnsExactlyItsBacking() (gas: 3444670)\n[PASS] test_agentHoldersPayNoHookFee() (gas: 2729098)\n[PASS] test_agentHoldersRerollForHalf() (gas: 4090911)\n[PASS] test_borrowedAgentPortraitGetsNoWaiver() (gas: 2542113)\n[PASS] test_cantLockAnEmptyRenderer() (gas: 3077030)\n[PASS] test_claimRules() (gas: 458186)\n[PASS] test_collectFeesPaysImdToTreasuryAndKeepsInk() (gas: 3365728)\n[PASS] test_contractURIOwnerOnly() (gas: 94674)\n[PASS] test_contractWalletSeatCanClaim() (gas: 383617)\n[PASS] test_depositMinOut() (gas: 2977033)\n[PASS] test_depositedAgentPortraitIsPullable() (gas: 3286517)\n[PASS] test_feeRouterClaimAndRoute() (gas: 4374318)\n[PASS] test_feeRouterNeedsPoolAndAddresses() (gas: 3237985)\n[PASS] test_feeRouterSplitsAndCompounds() (gas: 4120625)\n[PASS] test_gasAtScale() (gas: 54586630)\nLogs:\n  pull(1) gas: 135304\n  revealNext(1) gas: 175105\n    per reveal: 175105\n  pull(10) gas: 322102\n  revealNext(10) gas: 696371\n    per reveal: 69637\n  pull(100) gas: 2532239\n  revealNext(100) gas: 6424530\n    per reveal: 64245\n  pull(400) gas: 9901430\n  revealNext(400) gas: 25551314\n    per reveal: 63878\n  deposit(100 portraits) gas: 6546515\n\n[PASS] test_guards() (gas: 11481192)\n[PASS] test_hookFeeImdToTreasury() (gas: 2209380)\n[PASS] test_hookFeeInkGoesToLiquidity() (gas: 2700451)\n[PASS] test_idleSeatClaimsToo() (gas: 223531)\n[PASS] test_inkFeesGoBackIntoThePool() (gas: 6021484)\n[PASS] test_launchSplit() (gas: 67288)\n[PASS] test_liquidityFloorHoldsIfPriceIsDragged() (gas: 4676817)\n[PASS] test_noPerWalletLimitAfterRelease() (gas: 2123236)\n[PASS] test_noPullsUntilRelease() (gas: 2175273)\n[PASS] test_onlyOnePool() (gas: 46322)\n[PASS] test_ownerIsOwnerWalletNotDeployer() (gas: 6854235)\n[PASS] test_permissionlessListingTracksSpotAboveReleaseFloor() (gas: 3359646)\n[PASS] test_poolLockedUntilRelease() (gas: 2617014)\n[PASS] test_poolNeverOpensWithoutRelease() (gas: 2475796)\n[PASS] test_premiumBeforePoolCannotBeFrontRun() (gas: 4839563)\n[PASS] test_pullRerollPullAgain() (gas: 3801768)\n[PASS] test_releaseLadderShape() (gas: 2318388)\n[PASS] test_releasePremiumListsAboveTheOpeningPrice() (gas: 1923886)\n[PASS] test_releaseTurnsUnclaimedSeatsIntoSlotsAndPoolInk() (gas: 2069112)\n[PASS] test_revealIsNotEarlyAndNotTwice() (gas: 2569927)\n[PASS] test_seatSoldDuringWindowNewOwnerClaims() (gas: 297827)\n[PASS] test_setupRules() (gas: 2730867)\n[PASS] test_setupStartMustBeSoon() (gas: 2836133)\n[PASS] test_traitRerollBeforeReleaseReverts() (gas: 39919)\n[PASS] test_traitRerollExpiredHashRenews() (gas: 2936861)\n[PASS] test_traitRerollGuards() (gas: 3398991)\n[PASS] test_traitRerollHalfPriceForAgentHolders() (gas: 3067235)\n[PASS] test_traitRerollRepaintsSameToken() (gas: 2974695)\n[PASS] test_vaultStaysFullyBacked() (gas: 3846452)\n[PASS] test_zeroTradingFee() (gas: 29344)\nSuite result: ok. 46 passed; 0 failed; 0 skipped; finished in 231.98ms (307.61ms CPU time)\n\nRan 7 tests for test/MainnetFork.t.sol:MainnetForkInkIs1Test\n[SKIP] test_fork_agentHolderPaysNoHookFee() (gas: 0)\n[SKIP] test_fork_bigBuyAfterRelease() (gas: 0)\n[SKIP] test_fork_fullLifecycle() (gas: 0)\n[SKIP] test_fork_hookFeeExactInBuyIsInkToLiquidity() (gas: 0)\n[SKIP] test_fork_lockedUntilReleaseHoweverLong() (gas: 0)\n[SKIP] test_fork_sellsPayFeesBothWays() (gas: 0)\n[SKIP] test_fork_sybilFirstHour() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 7 skipped; finished in 541.25ms (494.70µs CPU time)\n\nRan 7 tests for test/MainnetFork.t.sol:MainnetForkInkIs0Test\n[SKIP] test_fork_agentHolderPaysNoHookFee() (gas: 0)\n[SKIP] test_fork_bigBuyAfterRelease() (gas: 0)\n[SKIP] test_fork_fullLifecycle() (gas: 0)\n[SKIP] test_fork_hookFeeExactInBuyIsInkToLiquidity() (gas: 0)\n[SKIP] test_fork_lockedUntilReleaseHoweverLong() (gas: 0)\n[SKIP] test_fork_sellsPayFeesBothWays() (gas: 0)\n[SKIP] test_fork_sybilFirstHour() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 7 skipped; finished in 541.14ms (722.46µs CPU time)\n\nRan 15 tests for test/AuditRegression.t.sol:AuditRegressionTest\n[PASS] testFuzz_admissionCompletesWithBothRevealEntryPoints(uint16,bool) (runs: 256, μ: 19201908, ~: 13683362)\n[PASS] test_directSafeTransfersRejectedForBothOverloadsAndOtherNFTs() (gas: 888526)\n[PASS] test_hashExpiryDuringAdmissionRenewsAndEventuallyReveals() (gas: 37570918)\n[PASS] test_initializedPoolAtZeroTickIsValid() (gas: 4545830)\n[PASS] test_largeAdmissionQueueMakesProgressWithinGasBudget() (gas: 366692738)\nLogs:\n  largest admission/reveal step gas (including call overhead): 3455847\n\n[PASS] test_partialAdmissionPreservesDrawAndExcludesSameBlockAndLaterDeposits() (gas: 19778596)\n[PASS] test_pendingRerollBlocksApprovedOperatorsAndReportsMetadataThroughRenewal() (gas: 1881046)\n[PASS] test_pendingRerollCannotBeSoldWithOldTraits() (gas: 740312)\n[PASS] test_releaseFailureRollsBackUntilLiquidityIsConfigured() (gas: 4779138)\n[PASS] test_renewalsKeepEachPullsOwnCutoff() (gas: 1691070)\n[PASS] test_renewedHeadPullNeverWidensALaterPullsDraw() (gas: 2047318)\n[PASS] test_safeTransferToVaultIsRejected() (gas: 523768)\n[PASS] test_sameBlockHandInsDoNotCountForPullsMadeNow() (gas: 181958490)\n[PASS] test_setupRejectsLiquidityWithoutCode() (gas: 281626)\n[PASS] test_uninitializedPoolCannotSetPermanentFloor() (gas: 4639751)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 562.73ms (993.58ms CPU time)\n\nRan 1 test for test/VaultInvariants.t.sol:VaultInvariantsTest\n[PASS]\nVaultInvariantsTest invariants:\n[PASS] invariant_agentBalancesMatch\n[PASS] invariant_capAndSlots\n[PASS] invariant_custody\n[PASS] invariant_feesToLiquidity\n[PASS] invariant_fullyBacked\n[PASS] invariant_inkConserved\n[PASS] invariant_queueCovered\n VaultInvariantsTest invariants (runs: 64, calls: 7680, reverts: 0)\n\n╭--------------+------------------+-------+---------+----------╮\n| Contract     | Selector         | Calls | Reverts | Discards |\n+==============================================================+\n| VaultHandler | claim            | 611   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | deposit          | 531   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | moveSeat         | 554   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pull             | 555   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pullAll          | 548   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pullTooMany      | 557   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | release          | 524   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | rerollTraits     | 531   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | reveal           | 574   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | revealNext       | 516   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | revealTraits     | 559   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | roll             | 548   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | setFees          | 553   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | transferPortrait | 519   | 0       | 0        |\n╰--------------+------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.12s (5.11s CPU time)\n\nRan 15 test suites in 5.12s (7.37s CPU time): 75 tests passed, 0 failed, 28 skipped (103 total tests)\n","passed":true},{"durationMs":234,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"InkFeeRouter.claimAndRoute(address,uint64)\",\"InkFeeRouter.route()\",\"InkHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"InkHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"InkHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"InkHook.collectFees()\",\"InkHook.unlockCallback(bytes)\",\"InkLiquidity.addLiquidity()\",\"InkLiquidity.collectFees(uint256,uint256)\",\"InkLiquidity.renounceOwnership()\",\"InkLiquidity.setPool((address,address,uint24,int24,address),address)\",\"InkLiquidity.setPremium(int24)\",\"InkLiquidity.setWidth(int24)\",\"InkLiquidity.transferOwnership(address)\",\"InkPortraits.approve(address,uint256)\",\"InkPortraits.lockRenderer()\",\"InkPortraits.mintAgent(uint256,address,(uint32,uint16,uint32,uint32,bool))\",\"InkPortraits.mintSwarm(address,bytes32)\",\"InkPortraits.renounceOwnership()\",\"InkPortraits.safeTransferFrom(address,address,uint256)\",\"InkPortraits.safeTransferFrom(address,address,uint256,bytes)\",\"InkPortraits.setApprovalForAll(address,bool)\",\"InkPortraits.setContractURI(string)\",\"InkPortraits.setRenderer(address)\",\"InkPortraits.setSwarmSeed(uint256,bytes32)\",\"InkPortraits.setVault(address)\",\"InkPortraits.transferFrom(address,address,uint256)\",\"InkPortraits.transferOwnership(address)\",\"InkRenderer.addChunk(bytes)\",\"InkRenderer.lock()\",\"InkRenderer.renounceOwnership()\",\"InkRenderer.setPreviewBase(string)\",\"InkRenderer.transferOwnership(address)\",\"InkVault.claim(uint256,(uint32,uint16,uint32,uint32,bool),bytes32[])\",\"InkVault.deposit(uint256[],uint256)\",\"InkVault.pull(uint256)\",\"InkVault.release()\",\"InkVault.renounceOwnership()\",\"InkVault.rerollTraits(uint256,uint256)\",\"InkVault.reveal(uint256)\",\"InkVault.revealNext(uint256)\",\"InkVault.revealTraits(uint256)\",\"InkVault.setFees(uint16,uint16)\",\"InkVault.setInk(address)\",\"InkVault.setup(bytes32,uint64,address)\",\"InkVault.transferOwnership(address)\",\"TestSeats.approve(address,uint256)\",\"TestSeats.mint(uint256)\",\"TestSeats.safeTransferFrom(address,address,uint256)\",\"TestSeats.safeTransferFrom(address,address,uint256,bytes)\",\"TestSeats.setApprovalForAll(address,bool)\",\"TestSeats.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"ADAPTATION.md\":230,\"AUDIT_BRIEF.md\":71,\"BYTECODE_HASHES.json\":52,\"DEPENDENCIES.md\":10,\"PACKAGING.md\":16,\"README.md\":70,\"SETUP.md\":80,\"SHA256SUMS\":1638,\"SOURCE_PROVENANCE.json\":11363,\"VALIDATION.txt\":496,\"art/original-art.js\":418,\"foundry.toml\":33,\"launch/README.md\":28,\"launch/mainnet-1-contracts.json\":31,\"launch/mainnet-2-hook.json\":31,\"launch/sepolia-1-contracts.json\":31,\"launch/sepolia-2-hook.json\":31,\"remappings.txt\":8,\"script/Configure.s.sol\":63,\"script/InkSteps.sol\":122,\"script/LocalStack.s.sol\":135,\"script/Rehearse.s.sol\":236,\"script/UploadRenderer.s.sol\":33,\"src/InkFeeRouter.sol\":77,\"src/InkHook.sol\":255,\"src/InkLiquidity.sol\":295,\"src/InkPortraits.sol\":217,\"src/InkRenderer.sol\":304,\"src/InkVault.sol\":466,\"src/testnet/TestSeats.sol\":13,\"test/AuditRegression.t.sol\":466,\"test/InkRenderer.t.sol\":205,\"test/InkSystem.t.sol\":1241,\"test/LaunchDay.t.sol\":270,\"test/LaunchFeeHandoff.t.sol\":82,\"test/MainnetFork.t.sol\":519,\"test/SepoliaLaunch.t.sol\":186,\"test/SnapshotAllSeats.t.sol\":126,\"test/SnapshotProofs.t.sol\":86,\"test/SybilOptions.t.sol\":104,\"test/TokenURIPreview.t.sol\":78,\"test/VaultInvariants.t.sol\":397,\"test/fixtures/snapshot-sample.json\":98,\"test/fixtures/traits.json\":1},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":8274,"exitCode":0,"name":"slither","output":"[high/medium] weak-prng at src/InkVault.sol:290: InkVault._revealNext() (src/InkVault.sol#290-330) uses a weak PRNG: \"pick = r % avail (src/InkVault.sol#317)\"\n[medium/medium] divide-before-multiply at src/InkLiquidity.sol:290: InkLiquidity._floor(int24,int24) (src/InkLiquidity.sol#290-294) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/InkLiquidity.sol:223: InkLiquidity._rangeFrom(int24,int24) (src/InkLiquidity.sol#223-242) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/InkVault.sol:441: InkVault.waitingBefore() (src/InkVault.sol#441-444) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/InkVault.sol:352: InkVault.deposit(uint256[],uint256) (src/InkVault.sol#352-382) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/InkLiquidity.sol:127: InkLiquidity.addLiquidity() (src/InkLiquidity.sol#127-137) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/InkVault.sol:352: Reentrancy in InkVault.deposit(uint256[],uint256) (src/InkVault.sol#352-382):\n[medium/medium] uninitialized-local at src/InkLiquidity.sol:150: InkLiquidity._addLadder(int24,uint256).total (src/InkLiquidity.sol#150) is a local variable never initialized\n[medium/medium] uninitialized-local at src/InkRenderer.sol:110: InkRenderer.tokenJSON(uint256).extra (src/InkRenderer.sol#110) is a local variable never initialized\n[medium/medium] uninitialized-local at src/InkVault.sol:365: InkVault.deposit(uint256[],uint256).fees (src/InkVault.sol#365) is a local variable never initialized\n[medium/medium] unused-return at src/InkLiquidity.sol:97: InkLiquidity.setPool(PoolKey,IERC20) (src/InkLiquidity.sol#97-108) ignores return value by (sqrtPriceX96,tick,None,None) = poolManager.getSlot0(key.toId()) (src/InkLiquidity.sol#101)\n[medium/medium] unused-return at src/InkVault.sol:213: InkVault.release() (src/InkVault.sol#213-225) ignores return value by IInkLiquidity(liquidity).addLiquidity() (src/InkVault.sol#222)\n[medium/medium] unused-return at src/InkFeeRouter.sol:54: InkFeeRouter.claimAndRoute(ILaunchFeeFactory,uint64) (src/InkFeeRouter.sol#54-61) ignores return value by factory.claimFees(launchNumber) (src/InkFeeRouter.sol#58)\n[medium/medium] unused-return at src/InkLiquidity.sol:127: InkLiquidity.addLiquidity() (src/InkLiquidity.sol#127-137) ignores return value by (None,tick,None,None) = poolManager.getSlot0(poolKey.toId()) (src/InkLiquidity.sol#132)\n[medium/high] write-after-write at src/InkRenderer.sol:236: InkRenderer.swarmLabels(string).u (src/InkRenderer.sol#236) is written in both\n[medium/high] write-after-write at src/InkRenderer.sol:236: InkRenderer.swarmLabels(string).u (src/InkRenderer.sol#236) is written in both\n[low/medium] missing-zero-check at src/InkPortraits.sol:83: InkPortraits.setVault(address).vault_ (src/InkPortraits.sol#83) lacks a zero-check on :\n[low/medium] missing-zero-check at src/InkPortraits.sol:89: InkPortraits.setRenderer(address).renderer_ (src/InkPortraits.sol#89) lacks a zero-check on :\n[low/medium] calls-loop at src/InkVault.sol:236: InkVault.available() (src/InkVault.sol#236-238) has external calls inside a loop: _inventory.length + swarmSlots() - portraits.swarmMinted() (src/InkVault.sol#237)\n[low/medium] calls-loop at src/InkVault.sol:290: InkVault._revealNext() (src/InkVault.sol#290-330) has external calls inside a loop: tokenId_scope_0 = portraits.mintSwarm(p.to,keccak256(bytes)(abi.encode(entropy,pullId,seed))) (src/InkVault.sol#326)\n[low/medium] calls-loop at src/InkLiquidity.sol:244: InkLiquidity._mintSingleSided(int24,int24,uint256) (src/InkLiquidity.sol#244-280) has external calls inside a loop: positionId = positionManager.nextTokenId() (src/InkLiquidity.sol#257)\n[low/medium] calls-loop at src/InkLiquidity.sol:244: InkLiquidity._mintSingleSided(int24,int24,uint256) (src/InkLiquidity.sol#244-280) has external calls inside a loop: positionManager.modifyLiquidities(abi.encode(abi.encodePacked(uint8(Actions.MINT_POSITION),uint8(Actions.SETTLE),uint8(Actions.SWEEP)),params),block.timestamp) (src/InkLiquidity.sol#273-279)\n[low/medium] calls-loop at src/InkVault.sol:290: InkVault._revealNext() (src/InkVault.sol#290-330) has external calls inside a loop: portraits.transferFrom(address(this),p.to,tokenId) (src/InkVault.sol#323)\n[low/medium] calls-loop at src/InkVault.sol:352: InkVault.deposit(uint256[],uint256) (src/InkVault.sol#352-382) has external calls inside a loop: portraits.transferFrom(msg.sender,address(this),tokenId) (src/InkVault.sol#369)\n[low/medium] calls-loop at src/InkVault.sol:230: InkVault.swarmSlots() (src/InkVault.sol#230-233) has external calls inside a loop: SEATS - portraits.agentMinted() (src/InkVault.sol#232)\n[low/medium] calls-loop at src/InkVault.sol:230: InkVault.swarmSlots() (src/InkVault.sol#230-233) has external calls inside a loop: SEATS - portraits.agentMinted() (src/InkVault.sol#232)\n[low/medium] calls-loop at src/InkVault.sol:352: InkVault.deposit(uint256[],uint256) (src/InkVault.sol#352-382) has external calls inside a loop: portraits.isAgentPortrait(tokenId) (src/InkVault.sol#372-374)\n[low/medium] calls-loop at src/InkVault.sol:290: InkVault._revealNext() (src/InkVault.sol#290-330) has external calls inside a loop: portraits.transferFrom(address(this),p.to,tokenId) (src/InkVault.sol#323)\n[low/medium] calls-loop at src/InkVault.sol:290: InkVault._revealNext() (src/InkVault.sol#290-330) has external calls inside a loop: tokenId_scope_0 = portraits.mintSwarm(p.to,keccak256(bytes)(abi.encode(entropy,pullId,seed))) (src/InkVault.sol#326)\n[low/medium] calls-loop at src/InkVault.sol:236: InkVault.available() (src/InkVault.sol#236-238) has external calls inside a loop: _inventory.length + swarmSlots() - portraits.swarmMinted() (src/InkVault.sol#237)\n[low/medium] reentrancy-benign at src/InkLiquidity.sol:178: Reentrancy in InkLiquidity._add(int24,int24,uint256) (src/InkLiquidity.sol#178-182):\n[low/medium] reentrancy-events at src/InkLiquidity.sol:187: Reentrancy in InkLiquidity.collectFees(uint256,uint256) (src/InkLiquidity.sol#187-214):\n[low/medium] reentrancy-events at src/InkHook.sol:152: Reentrancy in InkHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/InkHook.sol#152-174):\n[low/medium] reentrancy-events at src/InkFeeRouter.sol:65: Reentrancy in InkFeeRouter.route() (src/InkFeeRouter.sol#65-76):\n[low/medium] reentrancy-events at src/InkFeeRouter.sol:54: Reentrancy in InkFeeRouter.claimAndRoute(ILaunchFeeFactory,uint64) (src/InkFeeRouter.sol#54-61):\n[low/medium] reentrancy-events at src/InkFeeRouter.sol:54: Reentrancy in InkFeeRouter.claimAndRoute(ILaunchFeeFactory,uint64) (src/InkFeeRouter.sol#54-61):\n[low/medium] reentrancy-events at src/InkHook.sol:188: Reentrancy in InkHook._payOut(Currency,address) (src/InkHook.sol#188-194):\n[low/medium] reentrancy-events at src/InkLiquidity.sol:178: Reentrancy in InkLiquidity._add(int24,int24,uint256) (src/InkLiquidity.sol#178-182):\n[low/medium] timestamp at src/InkHook.sol:135: InkHook.tradingOpen() (src/InkHook.sol#135-137) uses timestamp for comparisons\n[low/medium] timestamp at src/InkVault.sol:213: InkVault.release() (src/InkVault.sol#213-225) uses timestamp for comparisons\n[low/medium] timestamp at src/InkHook.sol:139: InkHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/InkHook.sol#139-150) uses timestamp for comparisons\n[low/medium] timestamp at src/InkVault.sol:185: InkVault.claimOpen() (src/InkVault.sol#185-187) uses timestamp for comparisons\n[low/medium] timestamp at src/InkVault.sol:161: InkVault.setup(bytes32,uint64,address) (src/InkVault.sol#161-174) uses timestamp for comparisons\n[low/medium] timestamp at src/InkVault.sol:230: InkVault.swarmSlots() (src/InkVault.sol#230-233) uses timestamp for comparisons","passed":true},{"durationMs":1716,"exitCode":0,"name":"aderyn","output":"[high] abi-encode-packed-hash-collision at src/InkRenderer.sol:56: `abi.encodePacked()` Hash Collision (4 places)\n[high] reentrancy-state-change at src/InkHook.sol:145: Reentrancy: State change after external call (8 places)\n[high] unsafe-casting at src/InkLiquidity.sol:265: Unsafe Casting of integers (2 places)\n[low] centralization-risk at src/InkLiquidity.sol:32: Centralization Risk (17 places)\n[low] costly-loop at src/InkLiquidity.sol:169: Costly operations inside loop (6 places)\n[low] large-numeric-literal at src/InkHook.sol:167: Large Numeric Literal (6 places)\n[low] literal-instead-of-constant at src/InkPortraits.sol:118: Literal Instead of Constant (35 places)\n[low] require-revert-in-loop at src/InkLiquidity.sol:152: Loop Contains `require`/`revert` (4 places)\n[low] state-no-address-check at src/InkLiquidity.sol:104: Address State Variable Set Without Checks (4 places)\n[low] state-variable-could-be-constant at src/InkRenderer.sol:20: State Variable Could Be Constant\n[low] storage-array-length-not-cached at src/InkRenderer.sol:87: Storage Array Length not Cached\n[low] unchecked-return at src/InkFeeRouter.sol:58: Unchecked Return (5 places)\n[low] uninitialized-local-variable at src/InkLiquidity.sol:169: Uninitialized Local Variable (7 places)\n[low] unsafe-oz-erc721-mint at src/InkPortraits.sol:123: Unsafe `ERC721::_mint()` (3 places)\n[low] unused-public-function at src/InkHook.sol:135: Public Function Not Used Internally","passed":true},{"durationMs":4157,"exitCode":0,"name":"proof 4ca493450231","output":"Compiling 102 files with Solc 0.8.26\nSolc 0.8.26 finished in 3.08s\nCompiler run successful with warnings:\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint128) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/InkLiquidity.sol:158:31:\n    |\n158 |             uint128 unitLiq = uint128(1e24) << k;\n    |                               ^^^^^^^^^^^^^^^^^^\n\n\nRan 1 test for test/imd-proof-35ace372/Proof_4ca493450231.t.sol:RenewalWidensDrawTest\n[PASS] test_depositAfterLaterPullsHashIsPublicJoinsItsDraw() (gas: 1237855)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.07ms (650.97µs CPU time)\n\nRan 1 test suite in 6.70ms (2.07ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"257a74c8f4500255dea506ced4bd5a743c3dc7c0595c08305854445e97f7f81e","verifiedTreeHash":"c8fd32b1b83f402d65939e685c884060044cef5a","verifierVersion":"0.1.0+6a374fcd"},{"checks":[{"durationMs":11636,"exitCode":0,"name":"build","output":"Compiling 179 files with Solc 0.8.26\nSolc 0.8.26 finished in 11.47s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n  --> script/LocalStack.s.sol:95:26:\n   |\n95 |     function _deployHook(InkPortraits portraits, InkVault vault, address treasury) internal returns (InkHook) {\n   |                          ^^^^^^^^^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:38:5:\n   |\n38 |     InkPortraits portraits;\n   |     ^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n  --> script/LocalStack.s.sol:95:50:\n   |\n95 |     function _deployHook(InkPortraits portraits, InkVault vault, address treasury) internal returns (InkHook) {\n   |                                                  ^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:39:5:\n   |\n39 |     InkVault vault;\n   |     ^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> script/LocalStack.s.sol:119:20:\n    |\n119 |     function _seed(PoolKey memory key, bool inkIs0, address to, ForkInk ink) internal {\n    |                    ^^^^^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:44:5:\n   |\n44 |     PoolKey key;\n   |     ^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> script/LocalStack.s.sol:119:65:\n    |\n119 |     function _seed(PoolKey memory key, bool inkIs0, address to, ForkInk ink) internal {\n    |                                                                 ^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:43:5:\n   |\n43 |     ForkInk ink;\n   |     ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint128) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/InkLiquidity.sol:158:31:\n    |\n158 |             uint128 unitLiq = uint128(1e24) << k;\n    |                               ^^^^^^^^^^^^^^^^^^\n\n","passed":true},{"durationMs":3752,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/LaunchFeeHandoff.t.sol:LaunchFeeHandoffTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 363.21µs (0.00ns CPU time)\n\nRan 9 tests for test/SnapshotAllSeats.t.sol:SnapshotAllSeatsTest\n[SKIP] test_claims_0000() (gas: 0)\n[SKIP] test_claims_0250() (gas: 0)\n[SKIP] test_claims_0500() (gas: 0)\n[SKIP] test_claims_0750() (gas: 0)\n[SKIP] test_claims_1000() (gas: 0)\n[SKIP] test_claims_1250() (gas: 0)\n[SKIP] test_claims_1500() (gas: 0)\n[SKIP] test_claims_1750() (gas: 0)\n[SKIP] test_treeHasEverySeatOnce() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 9 skipped; finished in 498.48µs (1.22ms CPU time)\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium2xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 661.70µs (30.88µs CPU time)\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium5xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 603.46µs (34.72µs CPU time)\n\nRan 7 tests for test/MainnetFork.t.sol:MainnetForkInkIs1Test\n[SKIP] test_fork_agentHolderPaysNoHookFee() (gas: 0)\n[SKIP] test_fork_bigBuyAfterRelease() (gas: 0)\n[SKIP] test_fork_fullLifecycle() (gas: 0)\n[SKIP] test_fork_hookFeeExactInBuyIsInkToLiquidity() (gas: 0)\n[SKIP] test_fork_lockedUntilReleaseHoweverLong() (gas: 0)\n[SKIP] test_fork_sellsPayFeesBothWays() (gas: 0)\n[SKIP] test_fork_sybilFirstHour() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 7 skipped; finished in 767.55µs (134.46µs CPU time)\n\nRan 7 tests for test/MainnetFork.t.sol:MainnetForkInkIs0Test\n[SKIP] test_fork_agentHolderPaysNoHookFee() (gas: 0)\n[SKIP] test_fork_bigBuyAfterRelease() (gas: 0)\n[SKIP] test_fork_fullLifecycle() (gas: 0)\n[SKIP] test_fork_hookFeeExactInBuyIsInkToLiquidity() (gas: 0)\n[SKIP] test_fork_lockedUntilReleaseHoweverLong() (gas: 0)\n[SKIP] test_fork_sellsPayFeesBothWays() (gas: 0)\n[SKIP] test_fork_sybilFirstHour() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 7 skipped; finished in 883.96µs (151.14µs CPU time)\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium1xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.05ms (22.35µs CPU time)\n\nRan 1 test for test/LaunchDay.t.sol:LaunchDayTest\n[SKIP] test_fork_launchDay() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.54ms (24.55µs CPU time)\n\nRan 2 tests for test/SnapshotProofs.t.sol:SnapshotProofsTest\n[PASS] test_realSnapshotProofsClaim() (gas: 3207992)\n[PASS] test_tamperedStatsAreRejected() (gas: 605207)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.54ms (2.82ms CPU time)\n\nRan 4 tests for test/SepoliaLaunch.t.sol:SepoliaLaunchTest\n[PASS] test_allFiveRuntimesMeetProtectedFloor() (gas: 16068981)\n[PASS] test_existingOwnerSetupRemainsRequiredAndAvailable() (gas: 430716)\n[PASS] test_factoryDeploymentPreservesOwnersRoyaltiesAndReferences() (gas: 183934)\n[PASS] test_factoryTreasuryAndOutsiderHaveNoAdminRole() (gas: 1701118)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 24.64ms (25.17ms CPU time)\n\nRan 1 test for test/TokenURIPreview.t.sol:TokenURIPreviewTest\n[PASS] test_writeTokenURIs() (gas: 25225288)\nLogs:\n  read-only profile: not written out-preview/agent-280.txt (use FOUNDRY_PROFILE=preview)\n  read-only profile: not written out-preview/pool.txt (use FOUNDRY_PROFILE=preview)\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 47.58ms (47.37ms CPU time)\n\nRan 46 tests for test/InkSystem.t.sol:InkSystemTest\n[PASS] test_agentClaimsOwnPortraitFree() (gas: 307623)\n[PASS] test_agentDepositReturnsExactlyItsBacking() (gas: 3444670)\n[PASS] test_agentHoldersPayNoHookFee() (gas: 2729098)\n[PASS] test_agentHoldersRerollForHalf() (gas: 4090911)\n[PASS] test_borrowedAgentPortraitGetsNoWaiver() (gas: 2542113)\n[PASS] test_cantLockAnEmptyRenderer() (gas: 3077030)\n[PASS] test_claimRules() (gas: 458186)\n[PASS] test_collectFeesPaysImdToTreasuryAndKeepsInk() (gas: 3365728)\n[PASS] test_contractURIOwnerOnly() (gas: 94674)\n[PASS] test_contractWalletSeatCanClaim() (gas: 383617)\n[PASS] test_depositMinOut() (gas: 2977033)\n[PASS] test_depositedAgentPortraitIsPullable() (gas: 3286517)\n[PASS] test_feeRouterClaimAndRoute() (gas: 4374318)\n[PASS] test_feeRouterNeedsPoolAndAddresses() (gas: 3237985)\n[PASS] test_feeRouterSplitsAndCompounds() (gas: 4120625)\n[PASS] test_gasAtScale() (gas: 54586630)\nLogs:\n  pull(1) gas: 135304\n  revealNext(1) gas: 175105\n    per reveal: 175105\n  pull(10) gas: 322102\n  revealNext(10) gas: 696371\n    per reveal: 69637\n  pull(100) gas: 2532239\n  revealNext(100) gas: 6424530\n    per reveal: 64245\n  pull(400) gas: 9901430\n  revealNext(400) gas: 25551314\n    per reveal: 63878\n  deposit(100 portraits) gas: 6546515\n\n[PASS] test_guards() (gas: 11481192)\n[PASS] test_hookFeeImdToTreasury() (gas: 2209380)\n[PASS] test_hookFeeInkGoesToLiquidity() (gas: 2700451)\n[PASS] test_idleSeatClaimsToo() (gas: 223531)\n[PASS] test_inkFeesGoBackIntoThePool() (gas: 6021484)\n[PASS] test_launchSplit() (gas: 67288)\n[PASS] test_liquidityFloorHoldsIfPriceIsDragged() (gas: 4676817)\n[PASS] test_noPerWalletLimitAfterRelease() (gas: 2123236)\n[PASS] test_noPullsUntilRelease() (gas: 2175273)\n[PASS] test_onlyOnePool() (gas: 46322)\n[PASS] test_ownerIsOwnerWalletNotDeployer() (gas: 6854235)\n[PASS] test_permissionlessListingTracksSpotAboveReleaseFloor() (gas: 3359646)\n[PASS] test_poolLockedUntilRelease() (gas: 2617014)\n[PASS] test_poolNeverOpensWithoutRelease() (gas: 2475796)\n[PASS] test_premiumBeforePoolCannotBeFrontRun() (gas: 4839563)\n[PASS] test_pullRerollPullAgain() (gas: 3801768)\n[PASS] test_releaseLadderShape() (gas: 2318388)\n[PASS] test_releasePremiumListsAboveTheOpeningPrice() (gas: 1923886)\n[PASS] test_releaseTurnsUnclaimedSeatsIntoSlotsAndPoolInk() (gas: 2069112)\n[PASS] test_revealIsNotEarlyAndNotTwice() (gas: 2569927)\n[PASS] test_seatSoldDuringWindowNewOwnerClaims() (gas: 297827)\n[PASS] test_setupRules() (gas: 2730867)\n[PASS] test_setupStartMustBeSoon() (gas: 2836133)\n[PASS] test_traitRerollBeforeReleaseReverts() (gas: 39919)\n[PASS] test_traitRerollExpiredHashRenews() (gas: 2936861)\n[PASS] test_traitRerollGuards() (gas: 3398991)\n[PASS] test_traitRerollHalfPriceForAgentHolders() (gas: 3067235)\n[PASS] test_traitRerollRepaintsSameToken() (gas: 2974695)\n[PASS] test_vaultStaysFullyBacked() (gas: 3846452)\n[PASS] test_zeroTradingFee() (gas: 29344)\nSuite result: ok. 46 passed; 0 failed; 0 skipped; finished in 119.04ms (118.70ms CPU time)\n\nRan 6 tests for test/InkRenderer.t.sol:InkRendererTest\n[PASS] test_agentTraitsMatchTheScript() (gas: 61685138)\n[PASS] test_noClaimsAfterTheFirstSwarmPortrait() (gas: 399350)\n[PASS] test_poolTraitsMatchTheScript() (gas: 60520738)\n[PASS] test_scriptStoredInChunksAndLocked() (gas: 12171334)\n[PASS] test_swarmNumbersFillUnclaimedSeats() (gas: 155416619)\n[PASS] test_tokenURIForAnAgentPortrait() (gas: 3511800)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 119.08ms (302.28ms CPU time)\n\nRan 15 tests for test/AuditRegression.t.sol:AuditRegressionTest\n[PASS] testFuzz_admissionCompletesWithBothRevealEntryPoints(uint16,bool) (runs: 256, μ: 19201908, ~: 13683362)\n[PASS] test_directSafeTransfersRejectedForBothOverloadsAndOtherNFTs() (gas: 888526)\n[PASS] test_hashExpiryDuringAdmissionRenewsAndEventuallyReveals() (gas: 37570918)\n[PASS] test_initializedPoolAtZeroTickIsValid() (gas: 4545830)\n[PASS] test_largeAdmissionQueueMakesProgressWithinGasBudget() (gas: 366692738)\nLogs:\n  largest admission/reveal step gas (including call overhead): 3455847\n\n[PASS] test_partialAdmissionPreservesDrawAndExcludesSameBlockAndLaterDeposits() (gas: 19778596)\n[PASS] test_pendingRerollBlocksApprovedOperatorsAndReportsMetadataThroughRenewal() (gas: 1881046)\n[PASS] test_pendingRerollCannotBeSoldWithOldTraits() (gas: 740312)\n[PASS] test_releaseFailureRollsBackUntilLiquidityIsConfigured() (gas: 4779138)\n[PASS] test_renewalsKeepEachPullsOwnCutoff() (gas: 1691070)\n[PASS] test_renewedHeadPullNeverWidensALaterPullsDraw() (gas: 2047318)\n[PASS] test_safeTransferToVaultIsRejected() (gas: 523768)\n[PASS] test_sameBlockHandInsDoNotCountForPullsMadeNow() (gas: 181958490)\n[PASS] test_setupRejectsLiquidityWithoutCode() (gas: 281626)\n[PASS] test_uninitializedPoolCannotSetPermanentFloor() (gas: 4639751)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 313.59ms (508.56ms CPU time)\n\nRan 1 test for test/VaultInvariants.t.sol:VaultInvariantsTest\n[PASS]\nVaultInvariantsTest invariants:\n[PASS] invariant_agentBalancesMatch\n[PASS] invariant_capAndSlots\n[PASS] invariant_custody\n[PASS] invariant_feesToLiquidity\n[PASS] invariant_fullyBacked\n[PASS] invariant_inkConserved\n[PASS] invariant_queueCovered\n VaultInvariantsTest invariants (runs: 64, calls: 7680, reverts: 0)\n\n╭--------------+------------------+-------+---------+----------╮\n| Contract     | Selector         | Calls | Reverts | Discards |\n+==============================================================+\n| VaultHandler | claim            | 611   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | deposit          | 531   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | moveSeat         | 554   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pull             | 555   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pullAll          | 548   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pullTooMany      | 557   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | release          | 524   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | rerollTraits     | 531   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | reveal           | 574   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | revealNext       | 516   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | revealTraits     | 559   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | roll             | 548   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | setFees          | 553   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | transferPortrait | 519   | 0       | 0        |\n╰--------------+------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.61s (3.61s CPU time)\n\nRan 15 test suites in 3.62s (4.25s CPU time): 75 tests passed, 0 failed, 28 skipped (103 total tests)\n","passed":true},{"durationMs":120,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"InkFeeRouter.claimAndRoute(address,uint64)\",\"InkFeeRouter.route()\",\"InkHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"InkHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"InkHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"InkHook.collectFees()\",\"InkHook.unlockCallback(bytes)\",\"InkLiquidity.addLiquidity()\",\"InkLiquidity.collectFees(uint256,uint256)\",\"InkLiquidity.renounceOwnership()\",\"InkLiquidity.setPool((address,address,uint24,int24,address),address)\",\"InkLiquidity.setPremium(int24)\",\"InkLiquidity.setWidth(int24)\",\"InkLiquidity.transferOwnership(address)\",\"InkPortraits.approve(address,uint256)\",\"InkPortraits.lockRenderer()\",\"InkPortraits.mintAgent(uint256,address,(uint32,uint16,uint32,uint32,bool))\",\"InkPortraits.mintSwarm(address,bytes32)\",\"InkPortraits.renounceOwnership()\",\"InkPortraits.safeTransferFrom(address,address,uint256)\",\"InkPortraits.safeTransferFrom(address,address,uint256,bytes)\",\"InkPortraits.setApprovalForAll(address,bool)\",\"InkPortraits.setContractURI(string)\",\"InkPortraits.setRenderer(address)\",\"InkPortraits.setSwarmSeed(uint256,bytes32)\",\"InkPortraits.setVault(address)\",\"InkPortraits.transferFrom(address,address,uint256)\",\"InkPortraits.transferOwnership(address)\",\"InkRenderer.addChunk(bytes)\",\"InkRenderer.lock()\",\"InkRenderer.renounceOwnership()\",\"InkRenderer.setPreviewBase(string)\",\"InkRenderer.transferOwnership(address)\",\"InkVault.claim(uint256,(uint32,uint16,uint32,uint32,bool),bytes32[])\",\"InkVault.deposit(uint256[],uint256)\",\"InkVault.pull(uint256)\",\"InkVault.release()\",\"InkVault.renounceOwnership()\",\"InkVault.rerollTraits(uint256,uint256)\",\"InkVault.reveal(uint256)\",\"InkVault.revealNext(uint256)\",\"InkVault.revealTraits(uint256)\",\"InkVault.setFees(uint16,uint16)\",\"InkVault.setInk(address)\",\"InkVault.setup(bytes32,uint64,address)\",\"InkVault.transferOwnership(address)\",\"TestSeats.approve(address,uint256)\",\"TestSeats.mint(uint256)\",\"TestSeats.safeTransferFrom(address,address,uint256)\",\"TestSeats.safeTransferFrom(address,address,uint256,bytes)\",\"TestSeats.setApprovalForAll(address,bool)\",\"TestSeats.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"ADAPTATION.md\":230,\"AUDIT_BRIEF.md\":71,\"BYTECODE_HASHES.json\":52,\"DEPENDENCIES.md\":10,\"PACKAGING.md\":16,\"README.md\":70,\"SETUP.md\":80,\"SHA256SUMS\":1638,\"SOURCE_PROVENANCE.json\":11363,\"VALIDATION.txt\":496,\"art/original-art.js\":418,\"foundry.toml\":33,\"launch.json\":31,\"launch/README.md\":28,\"launch/mainnet-1-contracts.json\":31,\"launch/mainnet-2-hook.json\":31,\"launch/sepolia-1-contracts.json\":31,\"launch/sepolia-2-hook.json\":31,\"remappings.txt\":8,\"script/Configure.s.sol\":63,\"script/InkSteps.sol\":122,\"script/LocalStack.s.sol\":135,\"script/Rehearse.s.sol\":236,\"script/UploadRenderer.s.sol\":33,\"src/InkFeeRouter.sol\":77,\"src/InkHook.sol\":255,\"src/InkLiquidity.sol\":295,\"src/InkPortraits.sol\":217,\"src/InkRenderer.sol\":304,\"src/InkVault.sol\":466,\"src/testnet/TestSeats.sol\":13,\"test/AuditRegression.t.sol\":466,\"test/InkRenderer.t.sol\":205,\"test/InkSystem.t.sol\":1241,\"test/LaunchDay.t.sol\":270,\"test/LaunchFeeHandoff.t.sol\":82,\"test/MainnetFork.t.sol\":519,\"test/SepoliaLaunch.t.sol\":186,\"test/SnapshotAllSeats.t.sol\":126,\"test/SnapshotProofs.t.sol\":86,\"test/SybilOptions.t.sol\":104,\"test/TokenURIPreview.t.sol\":78,\"test/VaultInvariants.t.sol\":397,\"test/fixtures/snapshot-sample.json\":98,\"test/fixtures/traits.json\":1},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"73c81de342a5e84b3442a03733bba2dcde1806d7f81d9718c1b97f72bc42d177","verifiedTreeHash":"c5ca58c9af97c2e1c8ce62bb8d32e718370eb747","verifierVersion":"0.1.0+6a374fcd"},{"checks":[{"durationMs":13024,"exitCode":0,"name":"build","output":"Compiling 179 files with Solc 0.8.26\nSolc 0.8.26 finished in 12.85s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n  --> script/LocalStack.s.sol:95:26:\n   |\n95 |     function _deployHook(InkPortraits portraits, InkVault vault, address treasury) internal returns (InkHook) {\n   |                          ^^^^^^^^^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:38:5:\n   |\n38 |     InkPortraits portraits;\n   |     ^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n  --> script/LocalStack.s.sol:95:50:\n   |\n95 |     function _deployHook(InkPortraits portraits, InkVault vault, address treasury) internal returns (InkHook) {\n   |                                                  ^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:39:5:\n   |\n39 |     InkVault vault;\n   |     ^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> script/LocalStack.s.sol:119:20:\n    |\n119 |     function _seed(PoolKey memory key, bool inkIs0, address to, ForkInk ink) internal {\n    |                    ^^^^^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:44:5:\n   |\n44 |     PoolKey key;\n   |     ^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> script/LocalStack.s.sol:119:65:\n    |\n119 |     function _seed(PoolKey memory key, bool inkIs0, address to, ForkInk ink) internal {\n    |                                                                 ^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:43:5:\n   |\n43 |     ForkInk ink;\n   |     ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint128) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/InkLiquidity.sol:158:31:\n    |\n158 |             uint128 unitLiq = uint128(1e24) << k;\n    |                               ^^^^^^^^^^^^^^^^^^\n\n","passed":true},{"durationMs":3721,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium5xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 610.89µs (19.33µs CPU time)\n\nRan 9 tests for test/SnapshotAllSeats.t.sol:SnapshotAllSeatsTest\n[SKIP] test_claims_0000() (gas: 0)\n[SKIP] test_claims_0250() (gas: 0)\n[SKIP] test_claims_0500() (gas: 0)\n[SKIP] test_claims_0750() (gas: 0)\n[SKIP] test_claims_1000() (gas: 0)\n[SKIP] test_claims_1250() (gas: 0)\n[SKIP] test_claims_1500() (gas: 0)\n[SKIP] test_claims_1750() (gas: 0)\n[SKIP] test_treeHasEverySeatOnce() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 9 skipped; finished in 663.29µs (1.71ms CPU time)\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium2xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 950.50µs (31.58µs CPU time)\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium1xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 898.15µs (30.85µs CPU time)\n\nRan 7 tests for test/MainnetFork.t.sol:MainnetForkInkIs0Test\n[SKIP] test_fork_agentHolderPaysNoHookFee() (gas: 0)\n[SKIP] test_fork_bigBuyAfterRelease() (gas: 0)\n[SKIP] test_fork_fullLifecycle() (gas: 0)\n[SKIP] test_fork_hookFeeExactInBuyIsInkToLiquidity() (gas: 0)\n[SKIP] test_fork_lockedUntilReleaseHoweverLong() (gas: 0)\n[SKIP] test_fork_sellsPayFeesBothWays() (gas: 0)\n[SKIP] test_fork_sybilFirstHour() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 7 skipped; finished in 1.22ms (224.59µs CPU time)\n\nRan 7 tests for test/MainnetFork.t.sol:MainnetForkInkIs1Test\n[SKIP] test_fork_agentHolderPaysNoHookFee() (gas: 0)\n[SKIP] test_fork_bigBuyAfterRelease() (gas: 0)\n[SKIP] test_fork_fullLifecycle() (gas: 0)\n[SKIP] test_fork_hookFeeExactInBuyIsInkToLiquidity() (gas: 0)\n[SKIP] test_fork_lockedUntilReleaseHoweverLong() (gas: 0)\n[SKIP] test_fork_sellsPayFeesBothWays() (gas: 0)\n[SKIP] test_fork_sybilFirstHour() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 7 skipped; finished in 1.25ms (140.84µs CPU time)\n\nRan 1 test for test/LaunchDay.t.sol:LaunchDayTest\n[SKIP] test_fork_launchDay() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.29ms (30.70µs CPU time)\n\nRan 1 test for test/LaunchFeeHandoff.t.sol:LaunchFeeHandoffTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.87ms (0.00ns CPU time)\n\nRan 2 tests for test/SnapshotProofs.t.sol:SnapshotProofsTest\n[PASS] test_realSnapshotProofsClaim() (gas: 3207992)\n[PASS] test_tamperedStatsAreRejected() (gas: 605207)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 4.93ms (2.92ms CPU time)\n\nRan 46 tests for test/InkSystem.t.sol:InkSystemTest\n[PASS] test_agentClaimsOwnPortraitFree() (gas: 307623)\n[PASS] test_agentDepositReturnsExactlyItsBacking() (gas: 3412945)\n[PASS] test_agentHoldersPayNoHookFee() (gas: 2729098)\n[PASS] test_agentHoldersRerollForHalf() (gas: 4054745)\n[PASS] test_borrowedAgentPortraitGetsNoWaiver() (gas: 2542113)\n[PASS] test_cantLockAnEmptyRenderer() (gas: 3077030)\n[PASS] test_claimRules() (gas: 458186)\n[PASS] test_collectFeesPaysImdToTreasuryAndKeepsInk() (gas: 3365728)\n[PASS] test_contractURIOwnerOnly() (gas: 94674)\n[PASS] test_contractWalletSeatCanClaim() (gas: 383617)\n[PASS] test_depositMinOut() (gas: 2950400)\n[PASS] test_depositedAgentPortraitIsPullable() (gas: 3260327)\n[PASS] test_feeRouterClaimAndRoute() (gas: 4374318)\n[PASS] test_feeRouterNeedsPoolAndAddresses() (gas: 3237985)\n[PASS] test_feeRouterSplitsAndCompounds() (gas: 4120625)\n[PASS] test_gasAtScale() (gas: 53423162)\nLogs:\n  pull(1) gas: 133079\n  revealNext(1) gas: 172889\n    per reveal: 172889\n  pull(10) gas: 319877\n  revealNext(10) gas: 674211\n    per reveal: 67421\n  pull(100) gas: 2530014\n  revealNext(100) gas: 6202930\n    per reveal: 62029\n  pull(400) gas: 9899205\n  revealNext(400) gas: 24664914\n    per reveal: 61662\n  deposit(100 portraits) gas: 6524323\n\n[PASS] test_guards() (gas: 11372409)\n[PASS] test_hookFeeImdToTreasury() (gas: 2209380)\n[PASS] test_hookFeeInkGoesToLiquidity() (gas: 2700451)\n[PASS] test_idleSeatClaimsToo() (gas: 223531)\n[PASS] test_inkFeesGoBackIntoThePool() (gas: 5994851)\n[PASS] test_launchSplit() (gas: 67288)\n[PASS] test_liquidityFloorHoldsIfPriceIsDragged() (gas: 4676817)\n[PASS] test_noPerWalletLimitAfterRelease() (gas: 2123236)\n[PASS] test_noPullsUntilRelease() (gas: 2170854)\n[PASS] test_onlyOnePool() (gas: 46322)\n[PASS] test_ownerIsOwnerWalletNotDeployer() (gas: 6745359)\n[PASS] test_permissionlessListingTracksSpotAboveReleaseFloor() (gas: 3359646)\n[PASS] test_poolLockedUntilRelease() (gas: 2617014)\n[PASS] test_poolNeverOpensWithoutRelease() (gas: 2475796)\n[PASS] test_premiumBeforePoolCannotBeFrontRun() (gas: 4839563)\n[PASS] test_pullRerollPullAgain() (gas: 3770694)\n[PASS] test_releaseLadderShape() (gas: 2318388)\n[PASS] test_releasePremiumListsAboveTheOpeningPrice() (gas: 1923886)\n[PASS] test_releaseTurnsUnclaimedSeatsIntoSlotsAndPoolInk() (gas: 2069112)\n[PASS] test_revealIsNotEarlyAndNotTwice() (gas: 2565486)\n[PASS] test_seatSoldDuringWindowNewOwnerClaims() (gas: 297827)\n[PASS] test_setupRules() (gas: 2619391)\n[PASS] test_setupStartMustBeSoon() (gas: 2719457)\n[PASS] test_traitRerollBeforeReleaseReverts() (gas: 39919)\n[PASS] test_traitRerollExpiredHashRenews() (gas: 2932420)\n[PASS] test_traitRerollGuards() (gas: 3372358)\n[PASS] test_traitRerollHalfPriceForAgentHolders() (gas: 3062794)\n[PASS] test_traitRerollRepaintsSameToken() (gas: 2970254)\n[PASS] test_vaultStaysFullyBacked() (gas: 3810286)\n[PASS] test_zeroTradingFee() (gas: 29344)\nSuite result: ok. 46 passed; 0 failed; 0 skipped; finished in 28.77ms (142.76ms CPU time)\n\nRan 1 test for test/TokenURIPreview.t.sol:TokenURIPreviewTest\n[PASS] test_writeTokenURIs() (gas: 25222461)\nLogs:\n  read-only profile: not written out-preview/agent-280.txt (use FOUNDRY_PROFILE=preview)\n  read-only profile: not written out-preview/pool.txt (use FOUNDRY_PROFILE=preview)\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 64.38ms (64.15ms CPU time)\n\nRan 6 tests for test/InkRenderer.t.sol:InkRendererTest\n[PASS] test_agentTraitsMatchTheScript() (gas: 61685138)\n[PASS] test_noClaimsAfterTheFirstSwarmPortrait() (gas: 399350)\n[PASS] test_poolTraitsMatchTheScript() (gas: 60520738)\n[PASS] test_scriptStoredInChunksAndLocked() (gas: 12171334)\n[PASS] test_swarmNumbersFillUnclaimedSeats() (gas: 155416619)\n[PASS] test_tokenURIForAnAgentPortrait() (gas: 3508973)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 126.90ms (305.25ms CPU time)\n\nRan 4 tests for test/SepoliaLaunch.t.sol:SepoliaLaunchTest\n[PASS] test_allFiveRuntimesMeetProtectedFloor() (gas: 15835385)\n[PASS] test_existingOwnerSetupRemainsRequiredAndAvailable() (gas: 430748)\n[PASS] test_factoryDeploymentPreservesOwnersRoyaltiesAndReferences() (gas: 183912)\n[PASS] test_factoryTreasuryAndOutsiderHaveNoAdminRole() (gas: 1700815)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 360.52ms (29.14ms CPU time)\n\nRan 11 tests for test/AuditRegression.t.sol:AuditRegressionTest\n[PASS] testFuzz_admissionCompletesWithBothRevealEntryPoints(uint16,bool) (runs: 256, μ: 20113193, ~: 16682518)\n[PASS] test_directSafeTransfersRejectedForBothOverloadsAndOtherNFTs() (gas: 861937)\n[PASS] test_hashExpiryDuringAdmissionRenewsAndEventuallyReveals() (gas: 37066158)\n[PASS] test_initializedPoolAtZeroTickIsValid() (gas: 4545874)\n[PASS] test_largeAdmissionQueueMakesProgressWithinGasBudget() (gas: 362048933)\nLogs:\n  largest admission/reveal step gas (including call overhead): 3453631\n\n[PASS] test_partialAdmissionPreservesDrawAndExcludesSameBlockAndLaterDeposits() (gas: 19507307)\n[PASS] test_pendingRerollBlocksApprovedOperatorsAndReportsMetadataThroughRenewal() (gas: 1876582)\n[PASS] test_pendingRerollCannotBeSoldWithOldTraits() (gas: 735826)\n[PASS] test_releaseFailureRollsBackUntilLiquidityIsConfigured() (gas: 4776497)\n[PASS] test_safeTransferToVaultIsRejected() (gas: 519349)\n[PASS] test_uninitializedPoolCannotSetPermanentFloor() (gas: 4639773)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 384.31ms (520.85ms CPU time)\n\nRan 1 test for test/VaultInvariants.t.sol:VaultInvariantsTest\n[PASS]\nVaultInvariantsTest invariants:\n[PASS] invariant_agentBalancesMatch\n[PASS] invariant_capAndSlots\n[PASS] invariant_custody\n[PASS] invariant_feesToLiquidity\n[PASS] invariant_fullyBacked\n[PASS] invariant_inkConserved\n[PASS] invariant_queueCovered\n VaultInvariantsTest invariants (runs: 64, calls: 7680, reverts: 0)\n\n╭--------------+------------------+-------+---------+----------╮\n| Contract     | Selector         | Calls | Reverts | Discards |\n+==============================================================+\n| VaultHandler | claim            | 611   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | deposit          | 531   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | moveSeat         | 554   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pull             | 555   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pullAll          | 548   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pullTooMany      | 557   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | release          | 524   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | rerollTraits     | 531   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | reveal           | 574   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | revealNext       | 516   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | revealTraits     | 559   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | roll             | 548   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | setFees          | 553   | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | transferPortrait | 519   | 0       | 0        |\n╰--------------+------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.55s (3.55s CPU time)\n\nRan 15 test suites in 3.56s (4.53s CPU time): 71 tests passed, 0 failed, 28 skipped (99 total tests)\n","passed":true},{"durationMs":174,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"InkFeeRouter.claimAndRoute(address,uint64)\",\"InkFeeRouter.route()\",\"InkHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"InkHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"InkHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"InkHook.collectFees()\",\"InkHook.unlockCallback(bytes)\",\"InkLiquidity.addLiquidity()\",\"InkLiquidity.collectFees(uint256,uint256)\",\"InkLiquidity.renounceOwnership()\",\"InkLiquidity.setPool((address,address,uint24,int24,address),address)\",\"InkLiquidity.setPremium(int24)\",\"InkLiquidity.setWidth(int24)\",\"InkLiquidity.transferOwnership(address)\",\"InkPortraits.approve(address,uint256)\",\"InkPortraits.lockRenderer()\",\"InkPortraits.mintAgent(uint256,address,(uint32,uint16,uint32,uint32,bool))\",\"InkPortraits.mintSwarm(address,bytes32)\",\"InkPortraits.renounceOwnership()\",\"InkPortraits.safeTransferFrom(address,address,uint256)\",\"InkPortraits.safeTransferFrom(address,address,uint256,bytes)\",\"InkPortraits.setApprovalForAll(address,bool)\",\"InkPortraits.setContractURI(string)\",\"InkPortraits.setRenderer(address)\",\"InkPortraits.setSwarmSeed(uint256,bytes32)\",\"InkPortraits.setVault(address)\",\"InkPortraits.transferFrom(address,address,uint256)\",\"InkPortraits.transferOwnership(address)\",\"InkRenderer.addChunk(bytes)\",\"InkRenderer.lock()\",\"InkRenderer.renounceOwnership()\",\"InkRenderer.setPreviewBase(string)\",\"InkRenderer.transferOwnership(address)\",\"InkVault.claim(uint256,(uint32,uint16,uint32,uint32,bool),bytes32[])\",\"InkVault.deposit(uint256[],uint256)\",\"InkVault.pull(uint256)\",\"InkVault.release()\",\"InkVault.renounceOwnership()\",\"InkVault.rerollTraits(uint256,uint256)\",\"InkVault.reveal(uint256)\",\"InkVault.revealNext(uint256)\",\"InkVault.revealTraits(uint256)\",\"InkVault.setFees(uint16,uint16)\",\"InkVault.setInk(address)\",\"InkVault.setup(bytes32,uint64,address)\",\"InkVault.transferOwnership(address)\",\"TestSeats.approve(address,uint256)\",\"TestSeats.mint(uint256)\",\"TestSeats.safeTransferFrom(address,address,uint256)\",\"TestSeats.safeTransferFrom(address,address,uint256,bytes)\",\"TestSeats.setApprovalForAll(address,bool)\",\"TestSeats.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"ADAPTATION.md\":182,\"AUDIT_BRIEF.md\":71,\"BYTECODE_HASHES.json\":52,\"DEPENDENCIES.md\":10,\"PACKAGING.md\":16,\"README.md\":65,\"SETUP.md\":80,\"SHA256SUMS\":1638,\"SOURCE_PROVENANCE.json\":11363,\"VALIDATION.txt\":496,\"art/original-art.js\":418,\"foundry.toml\":33,\"launch.json\":31,\"launch/README.md\":28,\"launch/mainnet-1-contracts.json\":31,\"launch/mainnet-2-hook.json\":31,\"launch/sepolia-1-contracts.json\":31,\"launch/sepolia-2-hook.json\":31,\"remappings.txt\":8,\"script/Configure.s.sol\":63,\"script/InkSteps.sol\":122,\"script/LocalStack.s.sol\":135,\"script/Rehearse.s.sol\":236,\"script/UploadRenderer.s.sol\":33,\"src/InkFeeRouter.sol\":77,\"src/InkHook.sol\":245,\"src/InkLiquidity.sol\":295,\"src/InkPortraits.sol\":217,\"src/InkRenderer.sol\":304,\"src/InkVault.sol\":431,\"src/testnet/TestSeats.sol\":13,\"test/AuditRegression.t.sol\":334,\"test/InkRenderer.t.sol\":180,\"test/InkSystem.t.sol\":1241,\"test/LaunchDay.t.sol\":270,\"test/LaunchFeeHandoff.t.sol\":82,\"test/MainnetFork.t.sol\":519,\"test/SepoliaLaunch.t.sol\":186,\"test/SnapshotAllSeats.t.sol\":126,\"test/SnapshotProofs.t.sol\":82,\"test/SybilOptions.t.sol\":104,\"test/TokenURIPreview.t.sol\":70,\"test/VaultInvariants.t.sol\":395,\"test/fixtures/snapshot-sample.json\":98,\"test/fixtures/traits.json\":1},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"812a00fbb79e3b541a216679a351aa6fcd58e19b5f3be94bafb180168e4aa399","verifiedTreeHash":"8817ccd55a4e26d7dc0fe19a17899ea750230306","verifierVersion":"0.1.0+6a374fcd"},{"checks":[{"durationMs":12074,"exitCode":0,"name":"build","output":"Compiling 186 files with Solc 0.8.26\nSolc 0.8.26 finished in 11.92s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n  --> script/LocalStack.s.sol:95:26:\n   |\n95 |     function _deployHook(InkPortraits portraits, InkVault vault, address treasury) internal returns (InkHook) {\n   |                          ^^^^^^^^^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:38:5:\n   |\n38 |     InkPortraits portraits;\n   |     ^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n  --> script/LocalStack.s.sol:95:50:\n   |\n95 |     function _deployHook(InkPortraits portraits, InkVault vault, address treasury) internal returns (InkHook) {\n   |                                                  ^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:39:5:\n   |\n39 |     InkVault vault;\n   |     ^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> script/LocalStack.s.sol:119:20:\n    |\n119 |     function _seed(PoolKey memory key, bool inkIs0, address to, ForkInk ink) internal {\n    |                    ^^^^^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:44:5:\n   |\n44 |     PoolKey key;\n   |     ^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> script/LocalStack.s.sol:119:65:\n    |\n119 |     function _seed(PoolKey memory key, bool inkIs0, address to, ForkInk ink) internal {\n    |                                                                 ^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> script/LocalStack.s.sol:43:5:\n   |\n43 |     ForkInk ink;\n   |     ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint128) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/InkLiquidity.sol:158:31:\n    |\n158 |             uint128 unitLiq = uint128(1e24) << k;\n    |                               ^^^^^^^^^^^^^^^^^^\n\n","passed":true},{"durationMs":12429,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/LaunchFeeHandoff.t.sol:LaunchFeeHandoffTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 202.57µs (0.00ns CPU time)\n\nRan 9 tests for test/SnapshotAllSeats.t.sol:SnapshotAllSeatsTest\n[SKIP] test_claims_0000() (gas: 0)\n[SKIP] test_claims_0250() (gas: 0)\n[SKIP] test_claims_0500() (gas: 0)\n[SKIP] test_claims_0750() (gas: 0)\n[SKIP] test_claims_1000() (gas: 0)\n[SKIP] test_claims_1250() (gas: 0)\n[SKIP] test_claims_1500() (gas: 0)\n[SKIP] test_claims_1750() (gas: 0)\n[SKIP] test_treeHasEverySeatOnce() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 9 skipped; finished in 451.28µs (1.10ms CPU time)\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium1xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 548.17µs (16.68µs CPU time)\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium5xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 567.12µs (20.00µs CPU time)\n\nRan 1 test for test/SybilOptions.t.sol:SybilPremium2xTest\n[SKIP] test_fork_sybilScenario() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 567.87µs (16.41µs CPU time)\n\nRan 7 tests for test/MainnetFork.t.sol:MainnetForkInkIs1Test\n[SKIP] test_fork_agentHolderPaysNoHookFee() (gas: 0)\n[SKIP] test_fork_bigBuyAfterRelease() (gas: 0)\n[SKIP] test_fork_fullLifecycle() (gas: 0)\n[SKIP] test_fork_hookFeeExactInBuyIsInkToLiquidity() (gas: 0)\n[SKIP] test_fork_lockedUntilReleaseHoweverLong() (gas: 0)\n[SKIP] test_fork_sellsPayFeesBothWays() (gas: 0)\n[SKIP] test_fork_sybilFirstHour() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 7 skipped; finished in 671.37µs (174.78µs CPU time)\n\nRan 1 test for test/LaunchDay.t.sol:LaunchDayTest\n[SKIP] test_fork_launchDay() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 762.42µs (16.99µs CPU time)\n\nRan 7 tests for test/MainnetFork.t.sol:MainnetForkInkIs0Test\n[SKIP] test_fork_agentHolderPaysNoHookFee() (gas: 0)\n[SKIP] test_fork_bigBuyAfterRelease() (gas: 0)\n[SKIP] test_fork_fullLifecycle() (gas: 0)\n[SKIP] test_fork_hookFeeExactInBuyIsInkToLiquidity() (gas: 0)\n[SKIP] test_fork_lockedUntilReleaseHoweverLong() (gas: 0)\n[SKIP] test_fork_sellsPayFeesBothWays() (gas: 0)\n[SKIP] test_fork_sybilFirstHour() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 7 skipped; finished in 1.06ms (119.45µs CPU time)\n\nRan 2 tests for test/SnapshotProofs.t.sol:SnapshotProofsTest\n[PASS] test_realSnapshotProofsClaim() (gas: 3207992)\n[PASS] test_tamperedStatsAreRejected() (gas: 605207)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 4.88ms (4.00ms CPU time)\n\nRan 1 test for test/TokenURIPreview.t.sol:TokenURIPreviewTest\n[PASS] test_writeTokenURIs() (gas: 25222461)\nLogs:\n  read-only profile: not written out-preview/agent-280.txt (use FOUNDRY_PROFILE=preview)\n  read-only profile: not written out-preview/pool.txt (use FOUNDRY_PROFILE=preview)\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 40.10ms (39.92ms CPU time)\n\nRan 46 tests for test/InkSystem.t.sol:InkSystemTest\n[PASS] test_agentClaimsOwnPortraitFree() (gas: 307623)\n[PASS] test_agentDepositReturnsExactlyItsBacking() (gas: 3412945)\n[PASS] test_agentHoldersPayNoHookFee() (gas: 2729098)\n[PASS] test_agentHoldersRerollForHalf() (gas: 4054745)\n[PASS] test_borrowedAgentPortraitGetsNoWaiver() (gas: 2542113)\n[PASS] test_cantLockAnEmptyRenderer() (gas: 3077030)\n[PASS] test_claimRules() (gas: 458186)\n[PASS] test_collectFeesPaysImdToTreasuryAndKeepsInk() (gas: 3365728)\n[PASS] test_contractURIOwnerOnly() (gas: 94674)\n[PASS] test_contractWalletSeatCanClaim() (gas: 383617)\n[PASS] test_depositMinOut() (gas: 2950400)\n[PASS] test_depositedAgentPortraitIsPullable() (gas: 3260327)\n[PASS] test_feeRouterClaimAndRoute() (gas: 4374318)\n[PASS] test_feeRouterNeedsPoolAndAddresses() (gas: 3237985)\n[PASS] test_feeRouterSplitsAndCompounds() (gas: 4120625)\n[PASS] test_gasAtScale() (gas: 53423162)\nLogs:\n  pull(1) gas: 133079\n  revealNext(1) gas: 172889\n    per reveal: 172889\n  pull(10) gas: 319877\n  revealNext(10) gas: 674211\n    per reveal: 67421\n  pull(100) gas: 2530014\n  revealNext(100) gas: 6202930\n    per reveal: 62029\n  pull(400) gas: 9899205\n  revealNext(400) gas: 24664914\n    per reveal: 61662\n  deposit(100 portraits) gas: 6524323\n\n[PASS] test_guards() (gas: 11372409)\n[PASS] test_hookFeeImdToTreasury() (gas: 2209380)\n[PASS] test_hookFeeInkGoesToLiquidity() (gas: 2700451)\n[PASS] test_idleSeatClaimsToo() (gas: 223531)\n[PASS] test_inkFeesGoBackIntoThePool() (gas: 5994851)\n[PASS] test_launchSplit() (gas: 67288)\n[PASS] test_liquidityFloorHoldsIfPriceIsDragged() (gas: 4676817)\n[PASS] test_noPerWalletLimitAfterRelease() (gas: 2123236)\n[PASS] test_noPullsUntilRelease() (gas: 2170854)\n[PASS] test_onlyOnePool() (gas: 46322)\n[PASS] test_ownerIsOwnerWalletNotDeployer() (gas: 6745359)\n[PASS] test_permissionlessListingTracksSpotAboveReleaseFloor() (gas: 3359646)\n[PASS] test_poolLockedUntilRelease() (gas: 2617014)\n[PASS] test_poolNeverOpensWithoutRelease() (gas: 2475796)\n[PASS] test_premiumBeforePoolCannotBeFrontRun() (gas: 4839563)\n[PASS] test_pullRerollPullAgain() (gas: 3770694)\n[PASS] test_releaseLadderShape() (gas: 2318388)\n[PASS] test_releasePremiumListsAboveTheOpeningPrice() (gas: 1923886)\n[PASS] test_releaseTurnsUnclaimedSeatsIntoSlotsAndPoolInk() (gas: 2069112)\n[PASS] test_revealIsNotEarlyAndNotTwice() (gas: 2565486)\n[PASS] test_seatSoldDuringWindowNewOwnerClaims() (gas: 297827)\n[PASS] test_setupRules() (gas: 2619391)\n[PASS] test_setupStartMustBeSoon() (gas: 2719457)\n[PASS] test_traitRerollBeforeReleaseReverts() (gas: 39919)\n[PASS] test_traitRerollExpiredHashRenews() (gas: 2932420)\n[PASS] test_traitRerollGuards() (gas: 3372358)\n[PASS] test_traitRerollHalfPriceForAgentHolders() (gas: 3062794)\n[PASS] test_traitRerollRepaintsSameToken() (gas: 2970254)\n[PASS] test_vaultStaysFullyBacked() (gas: 3810286)\n[PASS] test_zeroTradingFee() (gas: 29344)\nSuite result: ok. 46 passed; 0 failed; 0 skipped; finished in 199.25ms (89.33ms CPU time)\n\nRan 6 tests for test/InkRenderer.t.sol:InkRendererTest\n[PASS] test_agentTraitsMatchTheScript() (gas: 61685138)\n[PASS] test_noClaimsAfterTheFirstSwarmPortrait() (gas: 399350)\n[PASS] test_poolTraitsMatchTheScript() (gas: 60520738)\n[PASS] test_scriptStoredInChunksAndLocked() (gas: 12171334)\n[PASS] test_swarmNumbersFillUnclaimedSeats() (gas: 155416619)\n[PASS] test_tokenURIForAnAgentPortrait() (gas: 3508973)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 236.01ms (289.55ms CPU time)\n\nRan 6 tests for test/RendererStorageProperties.t.sol:RendererStoragePropertiesTest\n[PASS] testFuzz_chunkRoundTripPreservesBinaryDataAndOrder(bytes,bytes) (runs: 1000, μ: 276741, ~: 272777)\n[PASS] testFuzz_nonexistentPortraitCannotExposeCollectionTokenUri(uint256) (runs: 1000, μ: 13212, ~: 13212)\n[PASS] test_artAndPointerLocksSurviveOwnershipTransfer() (gas: 470247)\n[PASS] test_failedEmptyLockRemainsConfigurableByOwnerOnly() (gas: 264200)\n[PASS] test_maximumChunkFitsEip170AndNextByteRevertsAtomically() (gas: 9409333)\n[PASS] test_testSeatsCannotRemintASeatAfterItChangesHands() (gas: 213508)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 297.73ms (355.87ms CPU time)\n\nRan 12 tests for test/VaultFailurePaths.t.sol:VaultFailurePathsTest\n[PASS] testFuzz_batchDepositConservesBackingForAllAllowedFees(uint16,uint16,uint8,bool) (runs: 1000, μ: 1244525, ~: 1209261)\n[PASS] testFuzz_claimRejectsEveryAlteredStatsField(uint8) (runs: 1000, μ: 309882, ~: 310050)\n[PASS] test_allowanceFailureLeavesPullQueueUntouched() (gas: 235948)\n[PASS] test_claimWindowUsesInclusiveStartAndExclusiveEnd() (gas: 526420)\n[PASS] test_claimedAgentCanExitBeforeReleaseWithoutSpendingUnclaimedBacking() (gas: 597034)\n[PASS] test_duplicateAndForeignNftsRevertTheWholeBatch() (gas: 1070926)\n[PASS] test_failedDepositPaymentRollsBackNftCustodyAndIncomingQueue() (gas: 696351)\n[PASS] test_failedFeeTransferRollsBackEvenTheCompletedUserPayment() (gas: 719779)\n[PASS] test_failedPullDoesNotChargeOrReserveAndCanRetry() (gas: 712609)\n[PASS] test_failedTraitFeeDoesNotLockPortraitOrConsumeReroll() (gas: 650450)\n[PASS] test_seatApprovalDoesNotGrantClaimAndSeatTransferCannotDoubleClaim() (gas: 583414)\n[PASS] test_transferFromCallbackCannotReenterPull() (gas: 439401)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 297.70ms (591.61ms CPU time)\n\nRan 4 tests for test/SepoliaLaunch.t.sol:SepoliaLaunchTest\n[PASS] test_allFiveRuntimesMeetProtectedFloor() (gas: 15835385)\n[PASS] test_existingOwnerSetupRemainsRequiredAndAvailable() (gas: 430748)\n[PASS] test_factoryDeploymentPreservesOwnersRoyaltiesAndReferences() (gas: 183912)\n[PASS] test_factoryTreasuryAndOutsiderHaveNoAdminRole() (gas: 1700815)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 297.85ms (24.26ms CPU time)\n\nRan 6 tests for test/LiquidityFailurePaths.t.sol:LiquidityFailurePathsInk0Test\n[PASS] testFuzz_ladderConservesEveryWei(uint256,uint24) (runs: 1000, μ: 2674597, ~: 2678121)\n[PASS] test_feeCollectionLeavesAllPositionPrincipalAndCustodyIntact() (gas: 4373261)\n[PASS] test_invalidFeeRangesRevertWithoutMovingFunds() (gas: 1956141)\n[PASS] test_ownerTreasuryAndCallerCannotTransferApproveOrBurnLockedPosition() (gas: 2442041)\n[PASS] test_positionManagerFailureRollsBackAndCanBeRetried() (gas: 2079521)\n[PASS] test_thresholdRejectsZeroAndDustWithoutMovingFunds() (gas: 2711365)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 297.57ms (304.26ms CPU time)\n\nRan 6 tests for test/LiquidityFailurePaths.t.sol:LiquidityFailurePathsInk1Test\n[PASS] testFuzz_ladderConservesEveryWei(uint256,uint24) (runs: 1000, μ: 2672032, ~: 2676330)\n[PASS] test_feeCollectionLeavesAllPositionPrincipalAndCustodyIntact() (gas: 4370461)\n[PASS] test_invalidFeeRangesRevertWithoutMovingFunds() (gas: 1957083)\n[PASS] test_ownerTreasuryAndCallerCannotTransferApproveOrBurnLockedPosition() (gas: 2442983)\n[PASS] test_positionManagerFailureRollsBackAndCanBeRetried() (gas: 2073880)\n[PASS] test_thresholdRejectsZeroAndDustWithoutMovingFunds() (gas: 2712307)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 315.63ms (309.25ms CPU time)\n\nRan 11 tests for test/AuditRegression.t.sol:AuditRegressionTest\n[PASS] testFuzz_admissionCompletesWithBothRevealEntryPoints(uint16,bool) (runs: 256, μ: 19365370, ~: 14293235)\n[PASS] test_directSafeTransfersRejectedForBothOverloadsAndOtherNFTs() (gas: 861937)\n[PASS] test_hashExpiryDuringAdmissionRenewsAndEventuallyReveals() (gas: 37066158)\n[PASS] test_initializedPoolAtZeroTickIsValid() (gas: 4545874)\n[PASS] test_largeAdmissionQueueMakesProgressWithinGasBudget() (gas: 362048933)\nLogs:\n  largest admission/reveal step gas (including call overhead): 3453631\n\n[PASS] test_partialAdmissionPreservesDrawAndExcludesSameBlockAndLaterDeposits() (gas: 19507307)\n[PASS] test_pendingRerollBlocksApprovedOperatorsAndReportsMetadataThroughRenewal() (gas: 1876582)\n[PASS] test_pendingRerollCannotBeSoldWithOldTraits() (gas: 735826)\n[PASS] test_releaseFailureRollsBackUntilLiquidityIsConfigured() (gas: 4776497)\n[PASS] test_safeTransferToVaultIsRejected() (gas: 519349)\n[PASS] test_uninitializedPoolCannotSetPermanentFloor() (gas: 4639773)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 365.01ms (495.85ms CPU time)\n\nRan 2 tests for test/VaultInvariants.t.sol:VaultInvariantsTest\n[PASS]\nVaultInvariantsTest invariants:\n[PASS] invariant_agentBalancesMatch\n[PASS] invariant_capAndSlots\n[PASS] invariant_custody\n[PASS] invariant_feesToLiquidity\n[PASS] invariant_fullyBacked\n[PASS] invariant_inkConserved\n[PASS] invariant_queueCovered\n VaultInvariantsTest invariants (runs: 256, calls: 30720, reverts: 0)\n\n╭--------------+------------------+-------+---------+----------╮\n| Contract     | Selector         | Calls | Reverts | Discards |\n+==============================================================+\n| VaultHandler | claim            | 2148  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | deposit          | 2073  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | donate           | 2108  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | moveSeat         | 2017  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pull             | 2064  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pullAll          | 2035  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | pullTooMany      | 1979  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | release          | 2063  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | rerollTraits     | 1993  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | reveal           | 2007  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | revealNext       | 1989  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | revealTraits     | 2022  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | roll             | 2062  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | setFees          | 2080  | 0       | 0        |\n|--------------+------------------+-------+---------+----------|\n| VaultHandler | transferPortrait | 2080  | 0       | 0        |\n╰--------------+------------------+-------+---------+----------╯\n\n[PASS] test_donationRemainsSurplusThroughEarlyRedemptionReleaseAndPull() (gas: 1711101)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 11.94s (11.94s CPU time)\n\nRan 2 tests for test/LiquidityStateful.t.sol:LiquidityStatefulInk1Test\n[PASS]\nLiquidityStatefulInk1Test invariants:\n[PASS] invariant_allTokensConservedAcrossTradingListingAndFees\n[PASS] invariant_onlyImdFeesReachTreasuryAndInkCompounds\n[PASS] invariant_positionsCannotLosePrincipalOrCustody\n LiquidityStatefulInk1Test invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------------------+-------------------------+-------+---------+----------╮\n| Contract                 | Selector                | Calls | Reverts | Discards |\n+=================================================================================+\n| LiquiditySequenceHandler | add                     | 2311  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | changeWidth             | 2362  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | collect                 | 2354  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | donate                  | 2329  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | rejectRepricing         | 2360  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | rejectUnauthorizedAdmin | 2347  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | trade                   | 2321  | 0       | 0        |\n╰--------------------------+-------------------------+-------+---------+----------╯\n\n[PASS] test_sequenceActuallyTradesCollectsAndReinvests() (gas: 2910899)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 12.05s (12.05s CPU time)\n\nRan 2 tests for test/LiquidityStateful.t.sol:LiquidityStatefulInk0Test\n[PASS]\nLiquidityStatefulInk0Test invariants:\n[PASS] invariant_allTokensConservedAcrossTradingListingAndFees\n[PASS] invariant_onlyImdFeesReachTreasuryAndInkCompounds\n[PASS] invariant_positionsCannotLosePrincipalOrCustody\n LiquidityStatefulInk0Test invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------------------+-------------------------+-------+---------+----------╮\n| Contract                 | Selector                | Calls | Reverts | Discards |\n+=================================================================================+\n| LiquiditySequenceHandler | add                     | 2311  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | changeWidth             | 2362  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | collect                 | 2354  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | donate                  | 2329  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | rejectRepricing         | 2360  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | rejectUnauthorizedAdmin | 2347  | 0       | 0        |\n|--------------------------+-------------------------+-------+---------+----------|\n| LiquiditySequenceHandler | trade                   | 2321  | 0       | 0        |\n╰--------------------------+-------------------------+-------+---------+----------╯\n\n[PASS] test_sequenceActuallyTradesCollectsAndReinvests() (gas: 2827543)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 12.28s (12.27s CPU time)\n\nRan 21 test suites in 12.29s (38.63s CPU time): 106 tests passed, 0 failed, 28 skipped (134 total tests)\n","passed":true},{"durationMs":95,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"InkFeeRouter.claimAndRoute(address,uint64)\",\"InkFeeRouter.route()\",\"InkHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"InkHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"InkHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"InkHook.collectFees()\",\"InkHook.unlockCallback(bytes)\",\"InkLiquidity.addLiquidity()\",\"InkLiquidity.collectFees(uint256,uint256)\",\"InkLiquidity.renounceOwnership()\",\"InkLiquidity.setPool((address,address,uint24,int24,address),address)\",\"InkLiquidity.setPremium(int24)\",\"InkLiquidity.setWidth(int24)\",\"InkLiquidity.transferOwnership(address)\",\"InkPortraits.approve(address,uint256)\",\"InkPortraits.lockRenderer()\",\"InkPortraits.mintAgent(uint256,address,(uint32,uint16,uint32,uint32,bool))\",\"InkPortraits.mintSwarm(address,bytes32)\",\"InkPortraits.renounceOwnership()\",\"InkPortraits.safeTransferFrom(address,address,uint256)\",\"InkPortraits.safeTransferFrom(address,address,uint256,bytes)\",\"InkPortraits.setApprovalForAll(address,bool)\",\"InkPortraits.setContractURI(string)\",\"InkPortraits.setRenderer(address)\",\"InkPortraits.setSwarmSeed(uint256,bytes32)\",\"InkPortraits.setVault(address)\",\"InkPortraits.transferFrom(address,address,uint256)\",\"InkPortraits.transferOwnership(address)\",\"InkRenderer.addChunk(bytes)\",\"InkRenderer.lock()\",\"InkRenderer.renounceOwnership()\",\"InkRenderer.setPreviewBase(string)\",\"InkRenderer.transferOwnership(address)\",\"InkVault.claim(uint256,(uint32,uint16,uint32,uint32,bool),bytes32[])\",\"InkVault.deposit(uint256[],uint256)\",\"InkVault.pull(uint256)\",\"InkVault.release()\",\"InkVault.renounceOwnership()\",\"InkVault.rerollTraits(uint256,uint256)\",\"InkVault.reveal(uint256)\",\"InkVault.revealNext(uint256)\",\"InkVault.revealTraits(uint256)\",\"InkVault.setFees(uint16,uint16)\",\"InkVault.setInk(address)\",\"InkVault.setup(bytes32,uint64,address)\",\"InkVault.transferOwnership(address)\",\"TestSeats.approve(address,uint256)\",\"TestSeats.mint(uint256)\",\"TestSeats.safeTransferFrom(address,address,uint256)\",\"TestSeats.safeTransferFrom(address,address,uint256,bytes)\",\"TestSeats.setApprovalForAll(address,bool)\",\"TestSeats.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"ADAPTATION.md\":182,\"AUDIT_BRIEF.md\":71,\"BYTECODE_HASHES.json\":52,\"DEPENDENCIES.md\":10,\"PACKAGING.md\":16,\"README.md\":65,\"SETUP.md\":80,\"SHA256SUMS\":1638,\"SOURCE_PROVENANCE.json\":11363,\"VALIDATION.txt\":496,\"art/original-art.js\":418,\"foundry.toml\":33,\"launch/README.md\":28,\"launch/mainnet-1-contracts.json\":31,\"launch/mainnet-2-hook.json\":31,\"launch/sepolia-1-contracts.json\":31,\"launch/sepolia-2-hook.json\":31,\"remappings.txt\":8,\"script/Configure.s.sol\":63,\"script/InkSteps.sol\":122,\"script/LocalStack.s.sol\":135,\"script/Rehearse.s.sol\":236,\"script/UploadRenderer.s.sol\":33,\"src/InkFeeRouter.sol\":77,\"src/InkHook.sol\":245,\"src/InkLiquidity.sol\":295,\"src/InkPortraits.sol\":217,\"src/InkRenderer.sol\":304,\"src/InkVault.sol\":431,\"src/testnet/TestSeats.sol\":13,\"test/AuditRegression.t.sol\":334,\"test/InkRenderer.t.sol\":180,\"test/InkSystem.t.sol\":1241,\"test/LaunchDay.t.sol\":270,\"test/LaunchFeeHandoff.t.sol\":82,\"test/LiquidityFailurePaths.t.sol\":151,\"test/LiquidityStateful.t.sol\":287,\"test/MainnetFork.t.sol\":519,\"test/PROPERTY_COVERAGE.md\":40,\"test/RendererStorageProperties.t.sol\":129,\"test/SepoliaLaunch.t.sol\":186,\"test/SnapshotAllSeats.t.sol\":126,\"test/SnapshotProofs.t.sol\":82,\"test/SybilOptions.t.sol\":104,\"test/TokenURIPreview.t.sol\":70,\"test/VaultFailurePaths.t.sol\":373,\"test/VaultInvariants.t.sol\":434,\"test/fixtures/snapshot-sample.json\":98,\"test/fixtures/traits.json\":1,\"test/helpers/LiquidityFixture.sol\":110},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"c469c37b0faef2d14f844e70c7596fbc1f51bf8f573a9d0996219ff8248ca2a5","verifiedTreeHash":"577f721e326bff86511a9f6479f6b56755a73c77","verifierVersion":"0.1.0+6a374fcd"}]}