{"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":"25f3079d-0b8f-4ff6-90c3-308be5e2a74b","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"fc4e45d8f4853ad69dc899de45402e5613d59895cf4e632992681d5f0a1dbf60","dependsOn":["build_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":"9f7f8983b4a35036cb1481674ad1b7b9cf52aebc05c558202986d886eea7112c","dependsOn":["build_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":null,"dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","state":"waiting"},{"acceptedSubmissionHash":"c79b7e7ac2ad47d11c56050f88c4487ba917d614633a116b998466c2dca6e63d","dependsOn":["build_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":"a85f3d92fa1f235cb7d13a4944db0ebcb8357c5f42d2616df3d90413ba8ac515","dependsOn":["build_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":"24c2ad66936801410d40b20b929aeb178dd96aa3995a47190c2a202b43ef8ef9","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"587567a5ef71546b5973ceaf6ef1dbbd590f49503b5ac9a6973ed86b89584857","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"587567a5ef71546b5973ceaf6ef1dbbd590f49503b5ac9a6973ed86b89584857","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":null,"dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"failed"},{"acceptedSubmissionHash":"42fe7d9cf6767852be93c7aba308e2a0b03ce87c7b1015be15f063fc18b69ad4","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","state":"accepted"}],"objective":"When the hook acts: after every swap (afterSwap), and once at pool initialization (beforeInitialize) to lock the hook to this launch's pool.\nFee rule: the hook takes no fee.\nWhere fees go: nowhere; the hook never takes or moves funds.\nWho can change it: nobody. No owner, no admin, no upgrades.\nToken name: Pixel Pool\nToken symbol: PIXEL\n\nEach swap paints one dot on a 32x32 on chain canvas (1024 dots), in order left to right, top to bottom; after 1024 swaps a new pass repaints from the top. A buy (IMD in, token out) paints green, a sell paints red. Brighter shade for bigger swaps, by the IMD amount: <5, <50, <500, >=500 IMD. At initialization the hook records which currency is IMD so buy and sell stay correct whatever the address order. Only the first pool initialized is accepted and only the PoolManager can call the hook. Store colors packed in uint256[32]; emit Painted(stroke, pixel, color, tx.origin); expose strokes(), pass(), pixelAt(i), canvas() and render() (an on chain SVG of the dots). Foundry tests for order, colors, brightness, second pass, single pool and access control.","parentJobId":null,"planHash":"0b802b66b09b10ba049be4ae6c4d724249abaa3b29fa869588ee17d825cd9a3d","previousHash":"b8a4b8f3d745a01816c157032ef6649cccee5836c2108bfb1651606ba90f074e","projectId":"25f3079d-0b8f-4ff6-90c3-308be5e2a74b","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1100-pixel-pool"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51319","feedbackHash":"317497b40832af51b3c7444ab6c0998f579d88c8aa0a774775055509c3383f59","nodeKey":"audit_economics","submissionHash":"fc4e45d8f4853ad69dc899de45402e5613d59895cf4e632992681d5f0a1dbf60","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52287","feedbackHash":"ed2d6d01a27453e29a8fc250d6d27274d38a025ebd074b3ee14f70f5d9120f3c","nodeKey":"audit_flow","submissionHash":"9f7f8983b4a35036cb1481674ad1b7b9cf52aebc05c558202986d886eea7112c","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52179","feedbackHash":"e86e9a76d33330beb8606f5610aeb18fc1531b713aa3817b23b2b46f0f7ea2c3","nodeKey":"audit_math","submissionHash":"c79b7e7ac2ad47d11c56050f88c4487ba917d614633a116b998466c2dca6e63d","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51139","feedbackHash":"a735e5470d4540169640bd1cad6b7462f9cc6a0b7032b8eca7e0389f10d02af4","nodeKey":"audit_permissions","submissionHash":"a85f3d92fa1f235cb7d13a4944db0ebcb8357c5f42d2616df3d90413ba8ac515","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51757","feedbackHash":"6b59edb737bc8abeec1339fb9b3906aaf49b6c38b6ab53c4b6642a65cad5b41d","nodeKey":"write_foundry_tests","submissionHash":"42fe7d9cf6767852be93c7aba308e2a0b03ce87c7b1015be15f063fc18b69ad4","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"e1a65dacc7dcf25cdae08b5fee8dd3ad020325b0a9cc047c7d60ba5737f77904","state":"blocked","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"87f8ad7f02c98c726cbc3df583818378bb325b5e905c01f2c89376603cdd3214","device":"964f2d8baaf7b03b","findings":[],"hash":"24c2ad66936801410d40b20b929aeb178dd96aa3995a47190c2a202b43ef8ef9","nodeId":"56679b6f-14d6-4b31-9d1e-96c9fb457e97","outcome":"completed","summary":"Implemented the fixed-supply PIXEL token, immutable canvas hook, SVG rendering, and CREATE2 salt helper. Dependencies are vendored for offline builds.\n\nValidation passed: `forge build`, all **40 tests**, and `forge fmt --check`.\n\nDeployment parameters, assumptions, and responsibilities are documented in `README.md`. No deployment was performed.","treeHash":"170a5db7291a424a5331813a07238fc7a31169c5","usage":{"cachedInputTokens":929664,"inputTokens":79038,"model":"gpt-6-astra","outputTokens":25148,"runtime":"codex","turns":6,"wallClockMs":601989}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"b312209aac1edeae","findings":[],"hash":"3641d706329ca960e759fd13827a3c22f558d49b8b8cd8f5e92f7cd2b6709f66","nodeId":"461d0bcb-bf55-49b2-b0fa-197c8a472b18","outcome":"failed","summary":"needs input: The launch-specific pool.tickSpacing and pool.initialPrice are missing. The repository uses tick spacing 60 and sqrtPriceX96 79228162514264337593543950336 only in test fixtures; its deployment description leaves both launch values to be selected. The manifest schema requires literal values and provides no placeholder for either, so using the test values would guess deployment parameters. — What tick spacing and initial price (decimal sqrtPriceX96, oriented as the square root of the sorted currency1/currency0 raw-unit ratio times 2^96) should the PIXEL/IMD launch pool use?","treeHash":null,"usage":{"cachedInputTokens":296448,"inputTokens":26375,"model":"gpt-6-astra","outputTokens":2349,"runtime":"codex","turns":3,"wallClockMs":96645}},{"artifacts":[],"attempt":1,"bundleHash":"88c35e73436647fa99da163cd283b6930bb0894686d491e1a41dbffdb3851ffd","device":"ad944e1953b3faa2","findings":[],"hash":"42fe7d9cf6767852be93c7aba308e2a0b03ce87c7b1015be15f063fc18b69ad4","nodeId":"cc0f66a2-5216-4b36-b2c2-380659d8d0b3","outcome":"completed","summary":"Added failure-path tests, complete canvas/SVG checks, and randomized token/pool invariants. All changes are under `test/`.\n\nVerified `forge build` and `forge test`: **55 passed, 0 failed, 0 skipped**. Invariants exercised 24,576 randomized calls with no unexpected reverts.\n\nNo confirmed defects found.","treeHash":"f22d7ef0110e75942dd694623aa050fbb7c57319","usage":{"cachedInputTokens":2261760,"inputTokens":89928,"model":"gpt-6-astra","outputTokens":9564,"runtime":"codex","turns":7,"wallClockMs":410182}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"99c6d0bcc495ad61","findings":[{"citation":"resolved","description":"The hook designates the non-PIXEL side of the first PoolKey as IMD and then requires it to be a deployed contract exposing IERC20Metadata.decimals() (lines 74-80). A PoolKey whose currency0 is address(0), which is how Uniswap v4 and the IMD deployer express the chain's native currency, fails `imd.code.length == 0` and reverts InvalidPool; the PoolManager bubbles that up and PoolManager.initialize fails. The launch factory deploys the hook and initializes the pool in the same transaction, so the whole launch transaction reverts and the hook, which locks to the first pool only, can never be recovered for a native pair. The same assumption also refuses an ERC-20 pair whose decimals() is missing or reverts. This is a defect only if the launch's manifest pool.pairedCurrency resolves to the native currency (0x0) or to a token without decimals(); the brief's wording ('whatever the address order') suggests IMD is an ERC-20, in which case the current behaviour is intended and this finding does not apply. No launch.json is in the tree, so the paired currency could not be checked here. The admission floor test test_initializesFromTheLaunchFactory builds its key with currency0 = address(0) whenever IMD_PAIRED_CURRENCY is unset or zero, and would then fail on this hook with 'the hook refused PoolManager.initialize from the launch factory'. Minimal fix that keeps the design: treat `imd == address(0)` as native with imdUnit = 1e18 (skip the code and decimals() checks for that case) and optionally fall back to 18 decimals when decimals() is absent; buy/sell and brightness logic need no change since Currency.wrap(address(0)) already sorts as currency0.","line":74,"path":"src/PixelHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {PixelHook} from \"src/PixelHook.sol\";\nimport {PixelToken} from \"src/PixelToken.sol\";\nimport {HookFlags} from \"src/HookFlags.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\n\n/// @dev Mirrors the admission floor's `test_initializesFromTheLaunchFactory` when the paired currency is the\n/// chain's native currency (address zero): the factory opens PIXEL/native on the manifest fee and the hook must\n/// accept it. Fails on the current code with `InvalidPool`.\ncontract NativePairScratchTest is Test {\n    uint160 constant SQRT_PRICE_1_1 = 79228162514264337593543950336;\n\n    PoolManager manager;\n    PixelToken token;\n    PixelHook hook;\n\n    function setUp() public {\n        manager = new PoolManager(address(this));\n        token = new PixelToken();\n        bytes memory creationCode =\n            abi.encodePacked(type(PixelHook).creationCode, abi.encode(IPoolManager(address(manager)), address(token)));\n        bytes32 initCodeHash = keccak256(creationCode);\n        for (uint256 i = 0; i < 200_000; i++) {\n            address predicted = address(\n                uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), bytes32(i), initCodeHash))))\n            );\n            if (!HookFlags.matches(predicted, HookFlags.PIXEL)) continue;\n            hook = new PixelHook{salt: bytes32(i)}(IPoolManager(address(manager)), address(token));\n            assertEq(address(hook), predicted);\n            break;\n        }\n        require(address(hook) != address(0), \"no salt\");\n    }\n\n    function test_factoryCanOpenNativePairedLaunchPool() public {\n        address factory = makeAddr(\"launch factory\");\n        PoolKey memory launchKey = PoolKey({\n            currency0: Currency.wrap(address(0)), // native currency always sorts first\n            currency1: Currency.wrap(address(token)),\n            fee: 12_500,\n            tickSpacing: 60,\n            hooks: IHooks(address(hook))\n        });\n        vm.prank(factory);\n        (bool ok,) = address(manager).call(abi.encodeCall(IPoolManager.initialize, (launchKey, SQRT_PRICE_1_1)));\n        assertTrue(ok, \"the hook refused PoolManager.initialize from the launch factory on a PIXEL/native pool\");\n        assertTrue(hook.initialized());\n        assertEq(Currency.unwrap(hook.imdCurrency()), address(0));\n        assertTrue(hook.imdIsCurrency0());\n        assertEq(hook.imdUnit(), 1 ether);\n    }\n}","reproduction":"State: PoolManager and PixelToken deployed; PixelHook deployed at a 0x2040-flag address with (manager, token). Call (from any address, e.g. the factory): manager.initialize(PoolKey{currency0: address(0), currency1: PIXEL, fee: 12500, tickSpacing: 60, hooks: hook}, 79228162514264337593543950336). Expected (if the paired currency is native): initialize succeeds, hook.initialized() == true, imdIsCurrency0 == true, imdUnit == 1e18. Actual: PixelHook.beforeInitialize reverts InvalidPool at src/PixelHook.sol:74, PoolManager.initialize reverts with HookCallFailed wrapping it, no pool is created, and the launch transaction fails. Run: forge test --match-path test/scratch/NativePair.t.sol (fails on current code).","severity":"medium","snippet":"        if (imd.code.length == 0) revert InvalidPool();","title":"beforeInitialize refuses a launch pool whose paired currency is the chain's native currency, so the launch reverts if IMD is native (or any pair without a callable decimals())"}],"hash":"9f7f8983b4a35036cb1481674ad1b7b9cf52aebc05c558202986d886eea7112c","nodeId":"18835372-1502-45cf-a399-4506d6934d65","outcome":"completed","summary":"The review is complete. The findings file is written and the working tree is otherwise untouched (the scratch test lives in the gitignored `test/scratch/` directory, and the only failing test is that scratch proof, by design).\n\n## Outcome\n\nThe control flow of the PixelHook is sound. All 40 project tests pass, and I traced both hook callbacks against the Execution Trace, Periphery and First Principles checklists plus the v4 security guide. I recorded one conditional finding and eleven coverage rows in `.imd-findings.json`.\n\n**Finding 1 (medium, conditional): native-currency pairing is refused.** `beforeInitialize` requires the non-PIXEL currency to have code and a callable `decimals()`. A pool key with currency0 at the zero address, which is how v4 expresses the chain's native currency, reverts with `InvalidPool` at `src/PixelHook.sol:74`. Because the factory deploys and initializes in one transaction and the hook locks to the first pool, the launch itself would fail. This only matters if the launch's paired currency is native or lacks `decimals()`. The brief's phrase \"whatever the address order\" suggests IMD is an ERC-20, in which case the behaviour is intended. No launch manifest is in the tree, so I could not settle it. I attached a scratch Foundry test that fails on the current code and explained the minimal fix.\n\n**What held, with the reasoning recorded per row:**\n- `afterSwap`: caller check, direction logic for both currency orders, widening before negation, threshold arithmetic for decimals up to 74, byte masking, zero return delta, no external calls so no swap-blocking path.\n- `beforeInitialize`: first-pool lock, key validation, rollback on manager rejection, and the `decimals()` read is a static call so the aderyn reentrancy lead is rejected.\n- Token entry points: unmodified OpenZeppelin v5.1.0 ERC20 with fixed supply.\n- Periphery: HookFlags masks, MineHook CREATE2 formula, render buffer bounds.\n\n**Static analysis leads:** all three aderyn high-impact lines were traced and rejected as false positives. The `encodePacked` is string concatenation, the \"reentrancy\" is after a STATICCALL, and the \"unsafe cast\" is an intentional byte extraction.\n\n**Trust assumptions noted, not findings:** the hook accepts any PIXEL/ERC-20 pool from any initializer, so atomic deploy-and-initialize by the factory is essential. The `Painted` event's stroke is one-based, which is documented.","treeHash":null,"usage":{"cachedInputTokens":1012019,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":24646,"runtime":"claude","turns":29,"wallClockMs":437102}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"8d26cb1a4eaeb75a","findings":[{"citation":"resolved","description":"Access-control/trust-gap finding on the initialization callback. The hook identifies the non-PIXEL currency of the first PoolKey as IMD and then requires that address to have code (line 74) and to answer IERC20Metadata.decimals() (line 77). The chain's native currency is Currency.wrap(address(0)), which has no code, so a PoolKey {currency0: 0x0, currency1: PIXEL, fee: 12500, hooks: this} is rejected with InvalidPool before the pool manager ever stores the pool. The same gate also rejects an ERC-20 paired currency that does not implement decimals() (the call reverts and bubbles as HookCallFailed). The brief says only that the hook 'records which currency is IMD so buy and sell stay correct whatever the address order'; it does not say native is excluded, and the reference material describes launch pools paired with the chain's ETH and seeded with tokens only. The launch factory deploys the hook and calls PoolManager.initialize in one transaction, so if pool.pairedCurrency in the manifest is the native currency the whole launch transaction reverts and the hook can never be used; the protected floor test test_initializesFromTheLaunchFactory also fails for this hook whenever IMD_PAIRED_CURRENCY resolves to address(0). This is conditional on the launch's paired currency: if IMD on the launch chain is an ERC-20 with decimals() the finding does not apply, and the judge can settle it from the manifest's pool.pairedCurrency. Minimal fix that keeps every other rule: treat imd == address(0) as a valid 18-decimal pair (skip the code and decimals() checks for it); optionally fall back to 18 when decimals() is absent. README line 12 ('Native currency is not an IMD pair') documents the current behaviour, so the author may instead decide it is intended, but then the manifest must name an ERC-20 pair or the launch is blocked.","line":74,"path":"src/PixelHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {PixelToken} from \"src/PixelToken.sol\";\nimport {PixelHook} from \"src/PixelHook.sol\";\nimport {HookFlags} from \"src/HookFlags.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\nimport {SwapParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {BalanceDelta, toBalanceDelta} from \"v4-core/src/types/BalanceDelta.sol\";\n\n/// @notice The launch factory opens PIXEL against the chain's native currency (currency0 == address(0)).\n/// @dev Fails on the current code: `beforeInitialize` reverts `InvalidPool` because address(0) has no code,\n/// so the launch transaction cannot initialize its pool. Passes once native is accepted as the IMD side\n/// (18 decimals, recorded as currency0).\ncontract NativePairTest is Test {\n    uint160 internal constant SQRT_PRICE = 1 << 96;\n\n    PoolManager internal manager;\n    PixelToken internal token;\n    PixelHook internal hook;\n\n    function setUp() public {\n        manager = new PoolManager(address(this));\n        token = new PixelToken();\n        bytes32 initHash = keccak256(abi.encodePacked(type(PixelHook).creationCode, abi.encode(manager, token)));\n        for (uint256 i; i < 300_000; ++i) {\n            address predicted = address(\n                uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), bytes32(i), initHash))))\n            );\n            if (HookFlags.matches(predicted, HookFlags.PIXEL)) {\n                hook = new PixelHook{salt: bytes32(i)}(manager, address(token));\n                break;\n            }\n        }\n        require(address(hook) != address(0), \"no salt\");\n    }\n\n    function test_nativePairedLaunchPoolInitializes() public {\n        PoolKey memory key = PoolKey(Currency.wrap(address(0)), Currency.wrap(address(token)), 12_500, 60, IHooks(hook));\n\n        // The launch factory's call. Expected: the pool opens and the hook locks to it.\n        manager.initialize(key, SQRT_PRICE);\n\n        assertTrue(hook.initialized(), \"hook did not lock to the native pool\");\n        assertTrue(hook.imdIsCurrency0(), \"native IMD is always currency0\");\n        assertEq(hook.imdUnit(), 1 ether, \"native currency has 18 decimals\");\n\n        // A 50 ETH buy must paint the third green shade.\n        SwapParams memory params = SwapParams(true, -50 ether, SQRT_PRICE / 2);\n        BalanceDelta delta = toBalanceDelta(-50 ether, 1 ether);\n        vm.prank(address(manager));\n        hook.afterSwap(address(this), key, params, delta, \"\");\n        assertEq(hook.pixelAt(0), 3);\n    }\n}","reproduction":"State: PixelToken and PixelHook deployed at a mined address with flags 0x2040. Call manager.initialize(PoolKey(Currency.wrap(address(0)), Currency.wrap(PIXEL), 12500, 60, IHooks(hook)), 1<<96) from any caller. Expected: the launch factory's pool opens and the hook locks to it with imdIsCurrency0 == true and imdUnit == 1e18. Actual: the call reverts with WrappedError(hook, 0xdc98354e /*beforeInitialize*/, 0x2083cd40 /*InvalidPool*/, 0xa9e35b2f /*HookCallFailed*/) and hook.initialized() stays false. Confirmed by test/scratch/NativePair.t.sol (fails on this code). Variant: pair PIXEL with a contract that has code but no decimals() function; initialize reverts (HookCallFailed) and the hook stays unlocked. Direct call: vm.prank(manager); hook.beforeInitialize(any, that key, 1<<96) reverts PixelHook.InvalidPool().","severity":"medium","snippet":"        if (imd.code.length == 0) revert InvalidPool();","title":"beforeInitialize refuses a native-currency pair (and any paired contract without decimals()), so the launch cannot open its pool if IMD is the chain's native currency"},{"citation":"resolved","description":"Trust assumption, recorded rather than a defect. beforeInitialize accepts the first PoolKey from any PoolManager.initialize caller as long as it contains the launch token, another contract with decimals() and this hook; it does not take or check a $factory address. The brief says 'Only the first pool initialized is accepted', and the reference says the launch factory deploys the hook and initializes the pool in the same transaction, which closes the window: nobody can initialize while the hook address has no code (InvalidHookResponse) and nobody can run between the two steps of a single transaction. The README (lines 14 and 71) documents this exact reliance. If the deployment plan ever splits deploy and initialize across transactions, anyone can lock the hook to PIXEL/<junk ERC-20> first, and the factory's own initialize then reverts with AlreadyInitialized with no recovery (no admin, no re-lock). No funds are at risk; the launch would simply fail in simulation. The judge should confirm the factory's atomic path; no code change is needed if it holds.","line":66,"path":"src/PixelHook.sol","reproduction":"State: hook deployed, not yet initialized, initialize scheduled in a later transaction. Attacker deploys Junk ERC-20 and calls manager.initialize(PoolKey(PIXEL/Junk ordered, 12500, 60, hook), 1<<96). hook.initialized() == true, hook.imdCurrency() == Junk. The factory's manager.initialize(PoolKey(PIXEL/IMD, 12500, 60, hook), price) now reverts (AlreadyInitialized wrapped in HookCallFailed). Confirmed by test/scratch/FirstPoolClaim.t.sol::test_outsiderClaimsFirstPoolWhenInitIsNotAtomic. Not reachable when deploy and initialize share one transaction.","severity":"info","snippet":"    function beforeInitialize(address, PoolKey calldata key, uint160) external onlyPoolManager returns (bytes4) {","title":"First-pool lock is first-come: beforeInitialize does not check the initializer, so the launch depends entirely on deploy and initialize being one transaction"}],"hash":"a85f3d92fa1f235cb7d13a4944db0ebcb8357c5f42d2616df3d90413ba8ac515","nodeId":"20b7faf8-e50e-4444-86f5-f33530e7d171","outcome":"completed","summary":"The review is complete. The findings file is written at the repository root, the working tree is clean, and no tracked file was changed.\n\n**Outcome.** The permission model of the Pixel Pool hook holds up well. Every callback, enabled or not, is gated on the pool manager, the one-shot pool lock is set only after all checks and rolls back on revert, and there is no owner, setter, upgrade, or fund-moving path. The token is unmodified OpenZeppelin v5.1.0 with a fixed supply. I report one conditional defect and one recorded trust assumption.\n\n**Finding 1, medium.** The initialization callback refuses a native-currency pair. It requires the non-PIXEL currency to have code and to answer `decimals()`, so a pool keyed with currency0 equal to the zero address reverts with InvalidPool inside the factory's initialize call. If the launch's paired currency is the chain's native currency, the launch transaction cannot open its pool and the hook is unusable. The same gate also refuses an ERC-20 pair that lacks a decimals function. This depends on the manifest's paired currency, which is not in the tree, so the judge can settle it from that field. A proof test is embedded in the findings file. It fails on the current code and passes once the zero address is accepted as an 18-decimal pair.\n\n**Finding 2, info.** The callback does not check who initializes, so the first pool is first-come. The window is closed when the factory deploys and initializes in one transaction, which the reference and README both state. If those steps were ever split, anyone could lock the hook to a junk pair and the factory's own initialize would revert permanently. Recorded as a trust assumption, not a defect.\n\n**Coverage.** All five listed entry points have rows, plus six invariant rows. Only `beforeInitialize` carries a finding. Static-analysis leads were checked and none held: the reentrancy lead at line 77 is a static call, and the casts and packed encoding are intentional and bounded.\n\n**Not reached.** No fork rehearsal against the real pool manager and IMD contract, and no gas profiling of the swap callback beyond a manual estimate.","treeHash":null,"usage":{"cachedInputTokens":1682474,"inputTokens":482,"model":"claude-fable-5-1","outputTokens":33576,"runtime":"claude","turns":38,"wallClockMs":469956}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"02f22d6f13810ca8","findings":[{"citation":"resolved","description":"The brief asks for `emit Painted(stroke, pixel, color, tx.origin)`. The implementation emits the post-increment count (`stroke + 1`, one-based) as `stroke` while `pixel` is `stroke % 1024` computed from the pre-increment value (zero-based). The two indices in the same event therefore use different bases: for stroke number s the painted pixel is (s - 1) % 1024, not s % 1024. The README documents this choice and the on-chain state is correct (the right byte is written), so this is a specification ambiguity for off-chain consumers rather than a contract defect. An indexer that derives the pixel from the stroke, or that filters the first stroke as `stroke == 0`, is off by one. If the author wants the event's two indices on the same base, emit `stroke` (zero-based, equal to the pixel's ordinal within the pass) or document the one-based convention next to the event in NatSpec.","line":114,"path":"src/PixelHook.sol","reproduction":"Initialize the pool and perform one swap (any direction, any size). Observed: `Painted(stroke=1, pixel=0, ...)` and `strokes()==1` (verified with vm.recordLogs: topics[1]==1, topics[2]==0). Expected under a literal reading of the brief: stroke 0 for pixel 0, or at least both indices on the same base. At the pass boundary the 1024th swap emits `Painted(1024, 1023, ...)` and the 1025th emits `Painted(1025, 0, ...)`: `1025 % 1024 == 1`, which is not the repainted pixel.","severity":"info","snippet":"        emit Painted(stroke + 1, pixel, color, tx.origin);","title":"Painted.stroke is one-based while pixel is zero-based; stroke % 1024 does not give the pixel"}],"hash":"c79b7e7ac2ad47d11c56050f88c4487ba917d614633a116b998466c2dca6e63d","nodeId":"6f98cc62-e923-4c2d-810a-14fc6f9f6803","outcome":"completed","summary":"Review complete. The findings file is written at `.imd-findings.json` and the scratch probe was removed, so the tree holds only that file beyond the original commit.\n\n**Outcome: no math defect found in the assigned area.** The hook's arithmetic is sound at every boundary the Math Precision, Boundary and Numerical Gap guides name. One info-level note is recorded.\n\nWhat I verified, with concrete values:\n\n- **Signed delta handling.** The int128 swap delta is widened to int256 before negation, so int128.min yields 2^127 and casts cleanly to uint256. The existing test covers this and I traced the manager's `Hooks.afterSwap` to confirm the hook receives the raw swapper delta, so the sign always matches `zeroForOne`.\n- **Threshold scaling.** Decimals are capped at 74, so 500 × 10^74 stays below 2^256. With 18, 6 and 74 decimals the shade boundaries at exactly 5, 50 and 500 units behave as the brief states, strict-less-than on the lower side.\n- **Byte packing.** The mask clears exactly one byte for shifts up to 248 and the fuzz test compares 1,100 strokes against an unpacked reference, including the wrap at 1024.\n- **SVG buffer.** Each circle is at most 48 bytes. With all 1,024 dots painted the output measured 48,516 bytes against a 57,472-byte buffer, so the internal assert is unreachable. The view costs about 2M gas.\n- **Static-analysis leads.** The reentrancy lead on the `decimals()` read is dead because the trace shows a STATICCALL. The encodePacked and unsafe-cast leads are intentional text concatenation and byte extraction.\n\n**The one note (info):** the `Painted` event emits a one-based stroke beside a zero-based pixel. The first swap emits stroke 1, pixel 0. State is correct and the README documents it, but an indexer computing the pixel from the stroke is off by one. I did not rate it as a defect.\n\n**Coverage record.** All five listed entry points have rows. Both hook callbacks and the three token functions hold, the token being unmodified OpenZeppelin v5.1.0. One deployment caveat is recorded on `beforeInitialize`: the hook refuses a native-currency pair, which is consistent with the brief naming IMD as a token but would block a native-paired manifest.","treeHash":null,"usage":{"cachedInputTokens":1318861,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":25751,"runtime":"claude","turns":36,"wallClockMs":382070}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"8eebc53449bafe7b","findings":[{"citation":"resolved","description":"The hook designates the non-launch currency as IMD and immediately requires it to have code, then calls IERC20Metadata(imd).decimals() on it. Native currency (Currency address 0) has no code, so any pool whose paired currency is native ETH is refused at initialization and, because the factory deploys and initializes in one transaction, the whole launch transaction reverts. The README documents this as intentional ('Native currency is not an IMD pair'), and the brief describes IMD as a token ('IMD in, token out'), so this is only a defect if the launch chain's IMD pair is native ETH. It is reported because it is the single input, within the economic/dependency area, on which the hook cannot launch, and because the verifier's own launch-factory floor fails on exactly that input. The manifest author must set pool.pairedCurrency to the IMD ERC-20 contract address; nothing in the tree records what that address is. No change is needed if IMD is an ERC-20 with a working decimals() in 0..74.","line":74,"path":"src/PixelHook.sol","reproduction":"State: hook deployed at a 0x2040-flag address with (manager, token). Call manager.initialize(PoolKey{currency0: Currency.wrap(address(0)), currency1: Currency.wrap(token), fee: 12500, tickSpacing: 60, hooks: hook}, 2**96). Expected (if IMD were native): the pool opens. Actual: PoolManager -> PixelHook.beforeInitialize computes imd = address(0), imd.code.length == 0, reverts InvalidPool(); the manager bubbles it as HookCallFailed and the launch transaction reverts. Concretely reproduced by running .imd/reads/protected/univ4_hook/Hook.protected.t.sol test_initializesFromTheLaunchFactory with IMD_PAIRED_CURRENCY=0x0000000000000000000000000000000000000000 (and IMD_POOL_FEE=12500, IMD_TICK_SPACING=60, IMD_SQRT_PRICE=79228162514264337593543950336, IMD_FACTORY_PROBE set): it fails with 'the hook refused PoolManager.initialize from the launch factory', trace shows [Revert] InvalidPool(). The same floor passes when IMD_PAIRED_CURRENCY is any nonzero address (the floor etches an ERC-20 there). The project's own test_nativePairRejected asserts this refusal.","severity":"low","snippet":"        if (imd.code.length == 0) revert InvalidPool();","title":"beforeInitialize refuses a native-ETH pair: launch is blocked if the manifest's pairedCurrency is the zero address"}],"hash":"fc4e45d8f4853ad69dc899de45402e5613d59895cf4e632992681d5f0a1dbf60","nodeId":"750fe19e-f4fb-409c-8895-e56f035793b5","outcome":"completed","summary":"The review is complete. The findings file is written and validated, the scratch directory is removed, and the working tree is unchanged.\n\n**Outcome:** no critical, high or medium defects in the economic, invariant or flow-gap areas. One low, conditional finding is recorded.\n\n**What I checked**\n\n- **Economic Security.** The hook has no value flows at all: no take, settle, mint, burn or transfer, all return-delta flags off, and `afterSwap` always returns zero. The only external dependency is a `decimals()` static call at initialization, so after launch no token misbehaviour, oracle or dependency failure can block swaps. There is no revert path in `afterSwap` for a legitimate swap on the locked pool, so the hook cannot brick trading. Spam painting costs only gas and the LP fee on dust, which is the brief's design.\n- **Invariants.** Strokes advance by exactly one per swap, pixel index is `(strokes-1) % 1024`, canvas bytes stay in 1..8 so the SVG palette lookup is bounded, and the pool lock is set once with rollback if the manager rejects the pool. The hook never calls the manager, so v4's self-call skip cannot bypass `beforeInitialize`.\n- **Flow gaps.** Buy and sell direction holds for both currency orders through real PoolManager swaps. Brightness uses the executed IMD delta, including LP fee on buys, as the README documents. The aderyn reentrancy lead at the `decimals()` read is not exploitable because it compiles to STATICCALL. `render()` costs about 2M gas, and hook overhead per swap is around 50k.\n- **Verifier floors.** I ran the protected hook and token floors with launch-like inputs (12500 fee, token probe, factory probe, ERC-20 pair, supply 10^27, 18 decimals). All 11 tests pass.\n\n**The one finding (low).** `beforeInitialize` refuses a native-ETH pair at `src/PixelHook.sol:74`. If the manifest's paired currency were the zero address, the launch transaction would revert and the verifier's launch-factory floor fails, which I reproduced. The brief and README treat IMD as an ERC-20, so this is a note for the manifest author rather than a code change, unless IMD on the launch chain is native.\n\n**Coverage.** All five listed entry points have rows: four hold, `beforeInitialize` carries the finding. Four extra rows record the invariants checked.","treeHash":null,"usage":{"cachedInputTokens":1015094,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":25976,"runtime":"claude","turns":25,"wallClockMs":373455}}],"verification":[{"checks":[{"durationMs":1985,"exitCode":0,"name":"build","output":"Compiling 86 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.85s\nCompiler run successful!\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/PixelHook.sol:153:21\n    │\n153 │ ┏                     abi.encodePacked(\n154 │ ┃                         '<circle cx=\"',\n155 │ ┃                         Strings.toString(col * 10 + 5),\n156 │ ┃                         '\" cy=\"',\n    ‡ ┃\n160 │ ┃                         '\"/>'\n161 │ ┃                     )\n    │ ┗━━━━━━━━━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\n","passed":true},{"durationMs":3361,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 5 tests for test/PixelToken.t.sol:PixelTokenTest\n[PASS] testFuzz_transfersConserveSupply(address,uint256) (runs: 256, μ: 92735, ~: 93049)\nLogs:\n  Bound result 1\n\n[PASS] test_invalidTransfersFail() (gas: 58652)\n[PASS] test_metadataAndSupply() (gas: 52761)\n[PASS] test_noMintOrAdministrationEvenForDeployer() (gas: 233312)\n[PASS] test_transferFromConsumesAllowance() (gas: 163551)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 3.38ms (4.14ms CPU time)\n\nRan 9 tests for test/PixelHookIntegration.t.sol:PixelHookIntegrationTest\n[PASS] testFuzz_realSwaps(bool,bool,uint96) (runs: 256, μ: 855290, ~: 852826)\nLogs:\n  Bound result 999000000000462264003\n\n[PASS] test_partialFillBrightnessUsesExecutedIMD() (gas: 684574)\n[PASS] test_realBuySellExactInputAndExactOutput() (gas: 2128240)\n[PASS] test_realSwapsWithIMDAsCurrency0() (gas: 2571183)\n[PASS] test_realSwapsWithIMDAsCurrency1() (gas: 1969730)\n[PASS] test_settlementFailureRollsBackStrokeAndPool() (gas: 721980)\n[PASS] test_successfulZeroFillStillPaintsOneDimDot() (gas: 645748)\n[PASS] test_uninitializedPredictedHookCannotOpenPool() (gas: 45048)\n[PASS] test_zeroRequestedSwapRevertsWithoutPainting() (gas: 513498)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 90.17ms (55.82ms CPU time)\n\nRan 26 tests for test/PixelHook.t.sol:PixelHookTest\n[PASS] testFuzz_brightnessAndDirection(bool,uint128) (runs: 256, μ: 293052, ~: 292878)\nLogs:\n  Bound result 11\n\n[PASS] testFuzz_sequenceMatchesUnpackedCanvas(uint256) (runs: 256, μ: 110229711, ~: 110230775)\n[PASS] test_allCallbacksRejectNonManager() (gas: 522076)\n[PASS] test_amountUsesIMDDeltaNotSpecifiedOrTokenAmount() (gas: 258361)\n[PASS] test_brightnessEveryBoundaryBothDirections() (gas: 1830069)\n[PASS] test_buysAndSellsWithReversedCurrencyOrder() (gas: 59791036)\n[PASS] test_constructorRejectsMissingDependencies() (gas: 7561)\n[PASS] test_duplicateInitializationAndSecondPoolRejected() (gas: 222271)\n[PASS] test_emitsStrokePixelPaletteAndOrigin() (gas: 261200)\n[PASS] test_exactPermissionsAndMinedAddress() (gas: 19304)\n[PASS] test_initializationRecordsPoolAndIMD() (gas: 225557)\n[PASS] test_invalidPoolAndFailedInitializationDoNotLock() (gas: 928906)\n[PASS] test_maximumSupportedDecimalsDoNotOverflow() (gas: 854908)\n[PASS] test_minInt128DeltaSafe() (gas: 265537)\n[PASS] test_nativePairRejected() (gas: 53893)\n[PASS] test_noAdminOrFundMovementEntrypoints() (gas: 145535)\n[PASS] test_otherPoolCannotPaint() (gas: 186668)\n[PASS] test_outOfRangePixelRejected() (gas: 17864)\n[PASS] test_recordsNon18DecimalIMD() (gas: 1128567)\n[PASS] test_rejectsUnrepresentableDecimalsWithoutLocking() (gas: 782338)\n[PASS] test_rowOrderAndBytePacking() (gas: 17112376)\n[PASS] test_runtimesHaveNoEscapeHatchOpcodes() (gas: 1857404)\n[PASS] test_secondPassOverwritesOnlyNextDotAndPreservesNeighbors() (gas: 193556501)\n[PASS] test_svgBlankAndPaintedDotsInRowOrder() (gas: 174951227)\n[PASS] test_swapBeforeInitializationRejected() (gas: 44748)\n[PASS] test_wrongAddressPermissionsRejectDeployment() (gas: 8155)\nSuite result: ok. 26 passed; 0 failed; 0 skipped; finished in 3.26s (3.67s CPU time)\n\nRan 3 test suites in 3.26s (3.35s CPU time): 40 tests passed, 0 failed, 0 skipped (40 total tests)\n","passed":true},{"durationMs":50,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"PixelHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"PixelHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"PixelToken.approve(address,uint256)\",\"PixelToken.transfer(address,uint256)\",\"PixelToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":83,\"dependencies.lock.json\":57,\"foundry.toml\":17,\"remappings.txt\":4,\"script/MineHook.s.sol\":25,\"src/HookFlags.sol\":30,\"src/PixelHook.sol\":252,\"src/PixelToken.sol\":11,\"test/PixelHook.t.sol\":361,\"test/PixelHookIntegration.t.sol\":163,\"test/PixelToken.t.sol\":72,\"test/helpers/HookFixture.sol\":63,\"test/helpers/PoolRouter.sol\":66,\"test/mocks/MockERC20.sol\":22},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1423,"exitCode":0,"name":"slither","output":"slither: no results at low impact or above","passed":true},{"durationMs":421,"exitCode":0,"name":"aderyn","output":"[high] abi-encode-packed-hash-collision at src/PixelHook.sol:153: `abi.encodePacked()` Hash Collision\n[high] reentrancy-state-change at src/PixelHook.sol:77: Reentrancy: State change after external call\n[high] unsafe-casting at src/PixelHook.sol:147: Unsafe Casting of integers\n[low] internal-function-used-once at src/HookFlags.sol:23: Internal Function Used Only Once\n[low] large-numeric-literal at src/PixelToken.sol:9: Large Numeric Literal\n[low] literal-instead-of-constant at src/PixelHook.sol:80: Literal Instead of Constant (21 places)\n[low] uninitialized-local-variable at src/PixelHook.sol:141: Uninitialized Local Variable (2 places)\n[low] unused-public-function at src/PixelHook.sol:124: Public Function Not Used Internally","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"24c2ad66936801410d40b20b929aeb178dd96aa3995a47190c2a202b43ef8ef9","verifiedTreeHash":"170a5db7291a424a5331813a07238fc7a31169c5","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":3209,"exitCode":0,"name":"build","output":"Compiling 90 files with Solc 0.8.26\nSolc 0.8.26 finished in 3.05s\nCompiler run successful!\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/PixelHook.sol:153:21\n    │\n153 │ ┏                     abi.encodePacked(\n154 │ ┃                         '<circle cx=\"',\n155 │ ┃                         Strings.toString(col * 10 + 5),\n156 │ ┃                         '\" cy=\"',\n    ‡ ┃\n160 │ ┃                         '\"/>'\n161 │ ┃                     )\n    │ ┗━━━━━━━━━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\n","passed":true},{"durationMs":19342,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 5 tests for test/PixelToken.t.sol:PixelTokenTest\n[PASS] testFuzz_transfersConserveSupply(address,uint256) (runs: 256, μ: 93087, ~: 93055)\nLogs:\n  Bound result 178075945958369867647766427\n\n[PASS] test_invalidTransfersFail() (gas: 58652)\n[PASS] test_metadataAndSupply() (gas: 52761)\n[PASS] test_noMintOrAdministrationEvenForDeployer() (gas: 233312)\n[PASS] test_transferFromConsumesAllowance() (gas: 163551)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 4.20ms (4.82ms CPU time)\n\nRan 9 tests for test/PixelHookIntegration.t.sol:PixelHookIntegrationTest\n[PASS] testFuzz_realSwaps(bool,bool,uint96) (runs: 256, μ: 855220, ~: 852824)\nLogs:\n  Bound result 18001581476480517884\n\n[PASS] test_partialFillBrightnessUsesExecutedIMD() (gas: 684574)\n[PASS] test_realBuySellExactInputAndExactOutput() (gas: 2128240)\n[PASS] test_realSwapsWithIMDAsCurrency0() (gas: 2571183)\n[PASS] test_realSwapsWithIMDAsCurrency1() (gas: 1969730)\n[PASS] test_settlementFailureRollsBackStrokeAndPool() (gas: 721980)\n[PASS] test_successfulZeroFillStillPaintsOneDimDot() (gas: 645748)\n[PASS] test_uninitializedPredictedHookCannotOpenPool() (gas: 45048)\n[PASS] test_zeroRequestedSwapRevertsWithoutPainting() (gas: 513498)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 82.98ms (52.00ms CPU time)\n\nRan 9 tests for test/PixelHookEdges.t.sol:PixelHookEdgesTest\n[PASS] testFuzz_managerOriginDoesNotAuthorizeCallback(address,bytes) (runs: 256, μ: 613152, ~: 612975)\n[PASS] testFuzz_thresholdNeighborsAcrossDecimalScales(uint8) (runs: 256, μ: 2422614, ~: 2422652)\nLogs:\n  Bound result 11\n\n[PASS] testFuzz_unsupportedDecimalMetadataNeverLocks(uint8) (runs: 256, μ: 1042069, ~: 1041940)\nLogs:\n  Bound result 194\n\n[PASS] test_completeSVGAndPackedCanvasAfterSecondPassRowBoundary() (gas: 117467932)\n[PASS] test_constructorRejectsZeroAndIdenticalDependencies() (gas: 6068)\n[PASS] test_everyPoolIdentityFieldIsBoundAfterPainting() (gas: 1656779)\n[PASS] test_initializationEmitsPoolIdentityAndCachesMetadata() (gas: 395042)\n[PASS] test_invalidCurrencyOrderingCannotPartiallyLockHook() (gas: 820066)\n[PASS] test_revertingAndMalformedMetadataLeaveHookAvailable() (gas: 1034574)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 140.35ms (239.84ms CPU time)\n\nRan 26 tests for test/PixelHook.t.sol:PixelHookTest\n[PASS] testFuzz_brightnessAndDirection(bool,uint128) (runs: 256, μ: 293009, ~: 292872)\nLogs:\n  Bound result 3\n\n[PASS] testFuzz_sequenceMatchesUnpackedCanvas(uint256) (runs: 256, μ: 110230849, ~: 110231560)\n[PASS] test_allCallbacksRejectNonManager() (gas: 522076)\n[PASS] test_amountUsesIMDDeltaNotSpecifiedOrTokenAmount() (gas: 258361)\n[PASS] test_brightnessEveryBoundaryBothDirections() (gas: 1830069)\n[PASS] test_buysAndSellsWithReversedCurrencyOrder() (gas: 59791036)\n[PASS] test_constructorRejectsMissingDependencies() (gas: 7561)\n[PASS] test_duplicateInitializationAndSecondPoolRejected() (gas: 222271)\n[PASS] test_emitsStrokePixelPaletteAndOrigin() (gas: 261200)\n[PASS] test_exactPermissionsAndMinedAddress() (gas: 19304)\n[PASS] test_initializationRecordsPoolAndIMD() (gas: 225557)\n[PASS] test_invalidPoolAndFailedInitializationDoNotLock() (gas: 928906)\n[PASS] test_maximumSupportedDecimalsDoNotOverflow() (gas: 854908)\n[PASS] test_minInt128DeltaSafe() (gas: 265537)\n[PASS] test_nativePairRejected() (gas: 53893)\n[PASS] test_noAdminOrFundMovementEntrypoints() (gas: 145535)\n[PASS] test_otherPoolCannotPaint() (gas: 186668)\n[PASS] test_outOfRangePixelRejected() (gas: 17864)\n[PASS] test_recordsNon18DecimalIMD() (gas: 1128567)\n[PASS] test_rejectsUnrepresentableDecimalsWithoutLocking() (gas: 782338)\n[PASS] test_rowOrderAndBytePacking() (gas: 17112376)\n[PASS] test_runtimesHaveNoEscapeHatchOpcodes() (gas: 1857404)\n[PASS] test_secondPassOverwritesOnlyNextDotAndPreservesNeighbors() (gas: 193556501)\n[PASS] test_svgBlankAndPaintedDotsInRowOrder() (gas: 174951227)\n[PASS] test_swapBeforeInitializationRejected() (gas: 44748)\n[PASS] test_wrongAddressPermissionsRejectDeployment() (gas: 8155)\nSuite result: ok. 26 passed; 0 failed; 0 skipped; finished in 3.41s (3.87s CPU time)\n\nRan 4 tests for test/PixelTokenInvariant.t.sol:PixelTokenInvariantTest\n[PASS]\nPixelTokenInvariantTest invariants:\n[PASS] invariant_allAllowancesMatchApprovalsAndSuccessfulSpending\n[PASS] invariant_supplyAndAllBalancesMatchTransferLedger\n PixelTokenInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭-------------------+-------------------------+-------+---------+----------╮\n| Contract          | Selector                | Calls | Reverts | Discards |\n+==========================================================================+\n| PixelTokenHandler | approve                 | 1836  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| PixelTokenHandler | rejectAdministration    | 1766  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| PixelTokenHandler | rejectApprovedOverspend | 1846  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| PixelTokenHandler | rejectOverspend         | 1819  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| PixelTokenHandler | rejectUnapprovedSpend   | 1896  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| PixelTokenHandler | rejectZeroReceiver      | 1763  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| PixelTokenHandler | rejectZeroSpender       | 1832  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| PixelTokenHandler | transfer                | 1828  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| PixelTokenHandler | transferFrom            | 1798  | 0       | 0        |\n╰-------------------+-------------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 18\n  Bound result 5611\n  Bound result 123093881055243396684705005\n  Bound result 228162514264337593543950257\n  Bound result 4999999\n  Bound result 7258\n  Bound result 5051\n  Bound result 2048\n  Bound result 2048\n  Bound result 539799758338109695441112354\n  Bound result 5000000\n  Bound result 4281\n  Bound result 12500\n  Bound result 2259309276434595622966150\n  Bound result 10153\n\n[PASS] test_finiteAllowanceRollsBackWhenTransferFails() (gas: 832483)\nLogs:\n  Bound result 1\n  Bound result 0\n\n[PASS] test_fullBalanceSelfTransferAndZeroBalanceTransfer() (gas: 442798)\n[PASS] test_infiniteApprovalSurvivesSpendingAndRevocationTakesEffect() (gas: 1013938)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 4.40s (4.40s CPU time)\n\nRan 2 tests for test/PixelHookInvariant.t.sol:PixelHookInvariantTest\n[PASS] invariant_canvasPoolLockAndConservationSurviveRandomSequences() (runs: 128, calls: 8192, reverts: 0)\n\n╭------------------+----------------------+-------+---------+----------╮\n| Contract         | Selector             | Calls | Reverts | Discards |\n+======================================================================+\n| PixelHookHandler | changeLiquidity      | 2051  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| PixelHookHandler | failSettlement       | 2056  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| PixelHookHandler | swap                 | 2013  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| PixelHookHandler | unauthorizedCallback | 2072  | 0       | 0        |\n╰------------------+----------------------+-------+---------+----------╯\n\n[PASS] test_handlerActionsExerciseBothSwapModesAndFailuresAcrossRollover() (gas: 4987735)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 19.21s (18.94s CPU time)\n\nRan 6 test suites in 19.21s (27.25s CPU time): 55 tests passed, 0 failed, 0 skipped (55 total tests)\n","passed":true},{"durationMs":51,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"PixelHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"PixelHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"PixelToken.approve(address,uint256)\",\"PixelToken.transfer(address,uint256)\",\"PixelToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":83,\"dependencies.lock.json\":57,\"foundry.toml\":17,\"remappings.txt\":4,\"script/MineHook.s.sol\":25,\"src/HookFlags.sol\":30,\"src/PixelHook.sol\":252,\"src/PixelToken.sol\":11,\"test/PixelHook.t.sol\":361,\"test/PixelHookEdges.t.sol\":241,\"test/PixelHookIntegration.t.sol\":163,\"test/PixelHookInvariant.t.sol\":117,\"test/PixelToken.t.sol\":72,\"test/PixelTokenInvariant.t.sol\":237,\"test/README.md\":37,\"test/helpers/HookFixture.sol\":63,\"test/helpers/PixelHookHandler.sol\":185,\"test/helpers/PoolRouter.sol\":66,\"test/mocks/MockERC20.sol\":22},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"42fe7d9cf6767852be93c7aba308e2a0b03ce87c7b1015be15f063fc18b69ad4","verifiedTreeHash":"f22d7ef0110e75942dd694623aa050fbb7c57319","verifierVersion":"0.1.0+ad90ce4c"}]}