{"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":"2ebd5179-1bec-4e29-8c7a-53b41eeb3088","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"19793d738640d50e534be2c2ddae97e837cd5003c02ef8899663f1bc91c2fcd2","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":"7ded6207702211f9e04c7a5a77f503a5fdf9af41bf129c21a8330f0dfaaa9adb","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":"d34f8e4f315ab3dbcf6541f5ed7c2839fd45bf6858b55a0e27035106bc2c6d3c","dependsOn":["build_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":"f9d50ca6e96b25259309e7bc14b65314da55ae1c9fc66d002aa3e3be738de464","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":"361dcdab544c18efc5ecf067915a67bd0a5f93803954ed6c0ddf4f7e419f9147","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":"8be4b33f3aa146d65f9ddbac37ef21e03fceedc5e1d7ea5c8c9e6d6c5b7cee06","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"d59561da3b378461b7c749f6bd5084477d44993aa94972ddea90f542535751df","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"d59561da3b378461b7c749f6bd5084477d44993aa94972ddea90f542535751df","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"1904d1efd29c2e704d60161ca53fead62cbb864325e0fd0e118f1bb56b838039","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"60cd35cbbe0b93f1a54d6cb0ffee366beef089c4f369572c3707cb5a2c648040","dependsOn":["build_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":"Launch SwarmPeg as a univ4_hook on Ethereum mainnet, paired with IMD.\n\nToken\n- Name: SwarmPeg\n- Symbol: SWARMPEG\n- Supply: 1_000_000_000e18, fixed single mint\n- Allocation: 88% seeds the pool, 2% to requester, 10% to swarm\n- No owner, no mint after construction, no pause, no upgrade\n\nHook (SwarmPegHook)\n- afterSwap + return deltas for fee accounting\n- 2% fee on every buy and sell, taken in IMD\n- 100% of the fee goes to an ownerless maintenance pot that the hook deploys itself inside its constructor\n- Constructor arguments: only $poolManager, $token, and $factory — do not require or invent any external maintenancePot_ address\n- Unipeg-style integer balance model: whole tokens own unique fully on-chain animated SVG glyphs\n- Glyphs: abstract node-network style with CryptoPunk-inspired pixel agents\n- Fully on-chain animated SVG (CSS keyframes): pulsing nodes, drawing/fading connections, color breathing, status blinks\n- Higher pulse → denser networks, brighter palettes, faster animations\n- Seed from trade data + block + previous holder + swarm pulse\n- Pulse system that rises with trades and decays over time\n- Clean events and views for glyphs and pulse\n\nWrite the launch.json and deploy via the factory.\n\nToken name: SwarmPeg\nToken symbol: SWARMPEG","parentJobId":null,"planHash":"d8198199107b0b8ea0b24e90faeeb86852c2abca69504102d3bc11f3b8442d9f","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"2ebd5179-1bec-4e29-8c7a-53b41eeb3088","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1941-swarmpeg"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51317","feedbackHash":"4bb522096332ed7c7afec74f573a4454c9e12e49af92fbb1452d5b25888c1a02","nodeKey":"audit_economics","submissionHash":"19793d738640d50e534be2c2ddae97e837cd5003c02ef8899663f1bc91c2fcd2","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51346","feedbackHash":"14fd8e029700dc4cdeba7fe89e52fc253a8ce8eec2cca7a505264ce3667d4bb9","nodeKey":"audit_flow","submissionHash":"7ded6207702211f9e04c7a5a77f503a5fdf9af41bf129c21a8330f0dfaaa9adb","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52680","feedbackHash":"4ba75987fd83069b236a350c7d2e45840c4077680700e3b68a943041ddc21ab5","nodeKey":"audit_judge","submissionHash":"d34f8e4f315ab3dbcf6541f5ed7c2839fd45bf6858b55a0e27035106bc2c6d3c","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51214","feedbackHash":"26594b43c42a8f4389a29ef649e79b4f033a8216469cb2e92e1a03c9e9b2910f","nodeKey":"audit_math","submissionHash":"f9d50ca6e96b25259309e7bc14b65314da55ae1c9fc66d002aa3e3be738de464","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51140","feedbackHash":"d947698f4dc676fc688613a6a8179833979b47c53df9d182eeb1d226c6606067","nodeKey":"audit_permissions","submissionHash":"361dcdab544c18efc5ecf067915a67bd0a5f93803954ed6c0ddf4f7e419f9147","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52140","feedbackHash":"6e1ec8818d595a242d965e892a84b53e381690cd377caaab376b16d8e1fd0e75","nodeKey":"build_contract_project","submissionHash":"8be4b33f3aa146d65f9ddbac37ef21e03fceedc5e1d7ea5c8c9e6d6c5b7cee06","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52660","feedbackHash":"d2c452c3c5e42ed1dddbcad601d607c273e5a14314bb02abd40cd07e11b13dec","nodeKey":"manifest","submissionHash":"1904d1efd29c2e704d60161ca53fead62cbb864325e0fd0e118f1bb56b838039","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"53933","feedbackHash":"d09c6f1279cec2f743cc3bcf4e665598cbd7e74e64091f997781c9b9dd7d0c6f","nodeKey":"write_foundry_tests","submissionHash":"60cd35cbbe0b93f1a54d6cb0ffee366beef089c4f369572c3707cb5a2c648040","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"0f2eb6209932ad04518bfb9272eb5bd49dd755305517dcbe70f4618902a6b71a","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"61862857bedca1149f3743def8efd81971aa3d09eb156959f0f0c581ada36296","device":"1ec9f666356a7ad7","findings":[],"hash":"1904d1efd29c2e704d60161ca53fead62cbb864325e0fd0e118f1bb56b838039","nodeId":"43644ff3-740f-4b08-a11f-aa57a3b249c4","outcome":"completed","summary":"launch.json is written and verified. The only file changed in the tree is the manifest.\n\n**What the manifest declares**, matching the accepted source exactly:\n- Hook contract SwarmPegHook with constructor arguments `$poolManager`, `$token`, `$factory`, the declaration order in the source. The hook has no owner, so there is no `$owner` argument, and the maintenance pot is deployed inside the constructor rather than passed in.\n- Permissions beforeInitialize, beforeSwap, afterSwap, beforeSwapReturnDelta and afterSwapReturnDelta, which is what getHookPermissions returns. The address flags are 0x20cc.\n- Token contract SwarmPeg, name SwarmPeg, symbol SWARMPEG, 18 decimals, no constructor arguments.\n- Pool paired with IMD at the required address, LP fee 12500, tick spacing 60, and the one-to-one sentinel price the reference gives.\n\n**Notes** were rewritten to state the review-relevant decisions up front: no owner argument exists, the hook's 2% fee is taken and kept in IMD per the brief rather than converted to ETH, the fee is minted as ERC-6909 claims to the ownerless pot so token-only seeded pools work, beforeSwapReturnDelta is used only for the fee split and never fills or skips a trade, and no liquidity callbacks exist so the factory's fee collect is untouched. Notes are 3145 characters, under the 4000 cap.\n\n**Checks run**\n- `forge build` succeeds with only lint warnings on pre-existing code.\n- `forge test` with the verifier's fuzz seed: 39 passed, 0 failed.\n- The protected hook floor test, run from test/scratch against the compiled creation code with the IMD pair address, token probe and factory probe: 4 passed, including the permissions-match and factory-initialization checks.\n- The repository's own checker in tools/check_artifacts.py passes, and a schema-shape check confirmed the first key is `kind`, all uint256 and address values are strings, and fee, tickSpacing and decimals are numbers.\n\nNothing outside launch.json was modified. The scratch copy of the protected test lives in test/scratch, which is gitignored and deleted before submission.","treeHash":"f4d11c77d4c2c12ebb98d10f418d4bcb947c0563","usage":{"cachedInputTokens":467650,"inputTokens":194,"model":"claude-fable-5-1","outputTokens":8973,"runtime":"claude","turns":19,"wallClockMs":205111}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"0476c44a80aa9574","findings":[{"citation":"resolved","description":"Every ERC-20 transfer that raises the recipient's whole-token count writes a 3-word Batch and pushes a 6-word Issuance (nine cold SSTOREs, ~200k gas) plus two events. A swap through any router moves tokens twice (PoolManager->router->trader on a buy, trader->router->PoolManager on a sell), so the glyph mirror is paid twice per trade on Ethereum mainnet. Measured on the delivered fixture: exact-input buy of 100 IMD via the test router 636,858 gas; exact-input sell of 100 tokens 761,442 gas; a wallet transfer of 1 whole token 260,387 gas versus 53,287 for a 0.5-token transfer that does not cross a unit boundary. At 20 gwei and 3,000 USD/ETH a sell costs about 45 USD in gas, about 3x a plain v4 swap, which is a material friction on a 2%-fee launch pool and is paid by every trader, not only by holders who want glyphs. The model itself is requested by the brief; the cost is an implementation choice. Issuance can be packed into 3 slots (firstId/count/holder in one word, startSlot/previousHolder in a second, seed in a third) and Batch into 1 slot (three uint80 fields), roughly halving the per-crossing cost, or the per-recipient Batch can be dropped in favour of the issuance record alone. No funds are at risk; this is reported as economic friction, not a vulnerability.","line":122,"path":"src/SwarmPeg.sol","reproduction":"Deploy with test/helpers/Fixture.sol (_deploy(true); _initializeAndSeed()), warm the trader with one buy and one sell, then measure gasleft() around _swap(true,true,100 ether) and _swap(false,true,100 ether): 636,858 and 761,442 gas. Then vm.prank(a); token.transfer(b, 1 ether) to an empty wallet: 260,387 gas; token.transfer(b, 0.5 ether) without a crossing: 53,287 gas; the next 0.5 ether that crosses: 263,266 gas. Expected for a plain v4 swap with a fee hook: roughly 200k-260k gas.","severity":"low","snippet":"                _batches[to][batchCount[to]++] = Batch(oldTo, current, first);\n                _issuances.push(Issuance(first, count, oldTo, to, from, seed));","title":"Glyph bookkeeping adds ~207k gas per whole-balance crossing, so a routed swap costs 640k-760k gas"},{"citation":"resolved","description":"The brief routes 100% of the hook fee to an 'ownerless maintenance pot'. The delivered pot exposes only redeem(uint256) and unlockCallback(bytes); redeem burns ERC-6909 claims and takes IMD to the pot's own address, and no function, role, keeper bounty or distribution path can ever move IMD or claims out again. The README states this as a deliberate interpretation. Economically the pot is indistinguishable from burning 2% of every buy and sell in IMD: the 'maintenance' funds can never maintain anything (the only maintenance action, checkpointPulse, is unpaid), and the value is removed from circulation forever with no beneficiary. If the requester meant the pot to fund keepers, buybacks, LP top-ups or any later spend, that requirement is not met and cannot be added after deployment because nothing is upgradeable. This is a scope decision for the requester, not a permission bypass: either confirm the sink semantics explicitly, or specify a permissionless, rule-bound outflow (for example a fixed bounty to whoever calls checkpointPulse/redeem, or a donate-back-to-pool path via poolManager.donate from the pot's redeemed IMD) before launch.","line":54,"path":"src/MaintenancePot.sol","reproduction":"Fixture: _deploy(true); _initializeAndSeed(); _swap(true,true,100 ether) accrues totalFees() = 2e18 claims to pot. pot.redeem(2e18) moves 2e18 IMD into the pot contract. Afterwards pot.reserveBalance() == 2e18 and every attempt to move it fails: calls to withdraw(uint256), sweep(address), transfer, approve or any other selector revert (no such functions exist), there is no owner, and the hook never reads the pot. Expected for a 'maintenance pot': some rule-bound way to spend on maintenance; actual: funds are locked permanently.","severity":"low","snippet":"        poolManager.take(currency, address(this), amount);","title":"MaintenancePot is a terminal sink: 2% of all pool volume is permanently unspendable by anyone"},{"citation":"resolved","description":"Because the IMD leg is the specified currency in these two modes, the fee is committed through beforeSwapReturnDelta before execution and afterSwap can only adjust the unspecified currency, so the hook reverts the whole swap when the pool fills less than the adjusted IMD amount. The check is correct and necessary (v4 returns exactly amountToSwap as the specified delta only on a full fill, see Pool.sol swapDelta construction), and it prevents taxing unfilled volume. The economic consequence is that whenever the seeded range is exhausted on the token side (for example a concentrated factory position that price has moved beyond), every exact-input buy at the usual MIN/MAX price limit fails regardless of size, and routers that rely on partial fills (v4 quoter/aggregator paths using exact-in with a tight sqrtPriceLimitX96) see reverts rather than the partial execution they would get from a hookless pool. Traders can fall back to exact-output buys and exact-input sells. Documented in README; recorded here so integrators are told before launch. No loss of funds: the revert is atomic and rolls back fees, pulse and glyphs (covered by the delivered tests).","line":143,"path":"src/SwarmPegHook.sol","reproduction":"Fixture after _initializeAndSeed(): zeroForOne = (currency0 == IMD); router.swap(key, SwapParams(zeroForOne, -1000 ether, zeroForOne ? Q96 - 1 : Q96 + 1), 1000 ether, 0, \"\") reverts with PartialFillUnsupported and hook.totalFees() stays 0, whereas the same limit with amountSpecified = -1000 ether on the token side (exact-input sell) executes partially and charges only the executed IMD (test_partialUnspecifiedIMDChargesOnlyExecutedVolume).","severity":"info","snippet":"            if (imdAmount != expected) revert PartialFillUnsupported();","title":"Exact-input buys and exact-output sells revert instead of partially filling when liquidity or the price limit is reached"},{"citation":"resolved","description":"The brief says '2% fee on every buy and sell'. The hook can only tax swaps routed through its own pool (token/IMD, fee 12500, spacing 60, this hook), and the token is a standard unrestricted ERC-20, so anyone may initialize a second v4 pool for the same pair with hooks = address(0) (or a v2/v3 pool) and trade without the hook fee. If liquidity migrates there, the maintenance pot stops accruing. This is inherent to a hook-based fee on a standard token and is the reference's intended architecture, so it is not a defect in the code; it is recorded so the requester's expectation of 'every' trade being taxed is set correctly.","line":249,"path":"src/SwarmPegHook.sol","reproduction":"After launch: manager.initialize(PoolKey(currency0, currency1, 3000, 60, IHooks(address(0))), sqrtPrice) succeeds for the same two currencies (no hook is consulted), and swaps in that pool pay no IMD to the pot: pot.claimBalance() unchanged, hook.tradeCount() unchanged. Expected by a literal reading of the brief: all buys and sells pay 2%; actual: only swaps in the bound pool do.","severity":"info","snippet":"        if (PoolId.unwrap(key.toId()) != PoolId.unwrap(poolId)) revert InvalidPool();","title":"The 2% fee applies only to the single bound pool; a hookless SWARMPEG/IMD pool trades fee-free"}],"hash":"19793d738640d50e534be2c2ddae97e837cd5003c02ef8899663f1bc91c2fcd2","nodeId":"7350ee71-0379-4bd3-af6b-350d6111a2e2","outcome":"completed","summary":"The review is complete. The findings file is written at the repository root, every snippet matches the tree, and the working tree is otherwise unchanged. No file outside the findings file was modified, and the scratch test I used for gas measurement was removed.\n\n**Outcome: no fund-loss or accounting defect found in the economics area.** The fee flow is sound in all four swap modes. I traced each against v4's `Hooks.afterSwap` and `Pool.swap`: the hook sees the pool's delta before its own delta is subtracted, a full fill leaves `amountSpecifiedRemaining` at exactly zero so the strict equality on line 143 is correct, the ERC-6909 mint to the pot debits the hook by exactly the delta it returns, and the pot's redeem path burns and takes the same amount so the manager always settles. The delivered invariant suites reproduced clean, and the manifest matches the implementation in constructor order, permissions, flags, fee tier and tick spacing.\n\nFour findings were recorded, none above low:\n\n- **Low, gas friction of the glyph mirror.** Each whole-token crossing writes nine cold storage slots. A routed swap crosses twice, so a buy measured 636,858 gas and a sell 761,442 gas, against about 53k for a non-crossing transfer. The brief asks for the model, but Issuance and Batch could be packed into four slots instead of nine.\n- **Low, the maintenance pot is a terminal sink.** Redeem only moves IMD into the pot itself, and nothing can move it out. Economically this burns 2% of all volume. The README calls it deliberate, so it is a scope decision the requester should confirm before an unupgradeable launch.\n- **Info, partial-fill reverts.** Exact-input buys and exact-output sells revert when the IMD leg cannot fully fill. Correct and documented, but integrators need to know.\n- **Info, pool-local fee.** A hookless pool for the same pair trades fee-free, which is inherent to a hook fee on a standard token.\n\nCoverage has sixteen rows: all eight listed entry points are answered, seven as holds and the two token transfer functions pointing to the gas finding, plus rows for the fee conservation, settlement, liquidity, manifest, supply, rounding and pulse invariants I checked. The SVG renderer is marked unreached as outside my area. The static-analysis leads were all examined and none rose to a finding: the strict equalities and divide-before-multiply are intentional, the pot's post-call state write is behind a reentrancy guard, and the randomness is cosmetic by design.","treeHash":null,"usage":{"cachedInputTokens":1077452,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":39002,"runtime":"claude","turns":33,"wallClockMs":750313}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"ddc5542bddcad225","findings":[{"citation":"resolved","description":"Trust-assumption note, not a permission bypass. The brief asks for an ownerless maintenance pot receiving 100% of the 2% IMD fee. The implementation (MaintenancePot) exposes only redeem(uint256), unlockCallback(bytes) and views; redeem burns the pot's own ERC-6909 claims and takes IMD to address(this), and no function transfers, approves or otherwise moves IMD or claims out of the pot. README.md and docs/SECURITY.md state this is deliberate because the brief names no spending policy. The consequence is that the economic effect is identical to burning the fee: nobody, including the requester, factory or any future governance, can use the reserve for maintenance. This is documented and consistent with 'ownerless', so it is recorded here as a material trust/economic assumption for the judge and requester to confirm rather than as a defect. If the requester expected the pot to be usable (e.g. permissionless payment of keepers, or a fixed-rule sweep), that is a scope decision which would change the design; no code change is recommended without it.","line":54,"path":"src/MaintenancePot.sol","reproduction":"State: after any taxed swap, e.g. an exact-input buy of 100e18 IMD through the launch pool, hook.totalFees() == 2e18 and pot.claimBalance() == 2e18. Anyone calls pot.redeem(2e18): pot now holds 2e18 IMD (imd.balanceOf(pot) == 2e18). Expected under a 'maintenance' reading: some actor or rule can spend it. Actual: the pot's ABI has no transfer/withdraw/approve/execute function (test_permissionsAddressSizeAndNoOwner already shows withdraw(address,uint256) and owner() revert), the hook has no function that calls the pot, and the pot never sets ERC-6909 operator approval, so PoolManager.transferFrom/burn by third parties also revert. The 2e18 IMD is locked forever.","severity":"info","snippet":"        poolManager.take(currency, address(this), amount);","title":"MaintenancePot has no disbursement path: every IMD fee is an irrecoverable reserve, so no maintenance can ever be funded from it"}],"hash":"361dcdab544c18efc5ecf067915a67bd0a5f93803954ed6c0ddf4f7e419f9147","nodeId":"b1f0bd3c-b140-443f-b7fe-15fd378c74a9","outcome":"completed","summary":"The review is complete and `.imd-findings.json` is written at the repository root. No source files were changed and no scratch files remain.\n\n**Outcome: no exploitable defect in the Access Control, Trust Gap or Asymmetry area.** The permission model is minimal and consistent, and I found no guard gap, escalation path, confused deputy, or paired-function asymmetry that an unprivileged actor can turn into loss.\n\nWhat I verified, with concrete traces:\n\n- **Hook callbacks.** All three enabled callbacks require the PoolManager as caller and the exact immutable pool id. The initialize callback additionally requires the factory passed as `$factory` and runs once. The `sender` and `hookData` arguments grant nothing.\n- **Fee accounting symmetry.** The two specified-IMD modes compute the fee identically in beforeSwap and afterSwap. The other two modes use real core deltas. The claim mint to the pot exactly offsets the returned hook delta, so the hook's own delta is always zero. The full-fill equality check is exact, since a filled swap's specified delta equals the adjusted amount.\n- **Maintenance pot.** Redemption is permissionless but has a fixed currency and a fixed recipient, which is the pot itself. The callback refuses any caller but the manager and any call outside an active redemption. No ERC-6909 operator approval is ever set, so third parties cannot burn or move the pot's claims.\n- **Pulse checkpoint.** The decay formula is invariant under any sequence of checkpoint calls, so nobody can slow or speed it, and pulse drives nothing financial.\n- **Token.** Unmodified OpenZeppelin transfers and approvals. The glyph bookkeeping cannot change amounts, supply, or revert a valid transfer.\n- **Manifest.** Constructor args and permissions match the implementation in order. Paired currency equals the hook's IMD constant, the fee is the policy tier the hook accepts, and the format rules hold.\n\nOne item is recorded at **info** severity for the judge and requester to confirm. The pot has no disbursement path at all, so every IMD fee is economically a burn and can never fund maintenance. The README and security notes state this is a deliberate reading of an \"ownerless\" pot with no spending policy. I treated it as a documented trust assumption rather than a defect.\n\nCoverage rows answer all eight listed entry points plus the beforeSwap view, the ownership invariant, the factory fee-collect invariant, and the manifest. No proof test was written because no finding reaches high or critical.","treeHash":null,"usage":{"cachedInputTokens":885907,"inputTokens":258,"model":"claude-fable-5-1","outputTokens":27612,"runtime":"claude","turns":35,"wallClockMs":450528}},{"artifacts":[],"attempt":1,"bundleHash":"1a779929c7791e5bd0a1bd21633078de949291c36fc55fe4e9bbad24f0412e37","device":"67b207ccd71ed641","findings":[],"hash":"60cd35cbbe0b93f1a54d6cb0ffee366beef089c4f369572c3707cb5a2c648040","nodeId":"0afd8abb-d148-4d03-9145-cf282be69c55","outcome":"completed","summary":"Added 34 tests under `test/`, covering failure paths, atomic factory launches, fee accounting, glyph metadata, and randomized balance/reserve invariants.\n\n`forge build` passes. All 73 tests pass offline with the supplied seed, including 49,152 new invariant calls.\n\nNo confirmed implementation defects found. Only `test/` files were added.","treeHash":"8a42c952605c5b835f7cf17ab930200710f09c5e","usage":{"cachedInputTokens":4472192,"inputTokens":150926,"model":"gpt-6-astra","outputTokens":36225,"runtime":"codex","turns":8,"wallClockMs":1203897}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"78d8eb9b16352a18","findings":[{"citation":"resolved","description":"First-principles/periphery cost trace. Any transfer whose recipient's whole-token count rises appends a 3-slot Batch, a 6-slot Issuance (firstId, count, startSlot, holder, previousHolder, seed are six separate 32-byte slots; the two addresses are not packed), bumps batchCount and nextGlyphId and emits two events: about eleven cold SSTOREs. A v4 swap moves the launch token at least twice (PoolManager -> router via take, router -> trader), so every buy pays this twice and every sell pays it once for the PoolManager's own batch. Measured on the repository fixture (full-range liquidity, TestRouter): exact-input buy 803,281 gas, exact-input sell 877,456, exact-output buy 648,911, exact-output sell 743,336; a bare ERC-20 transfer that crosses one whole unit to a fresh holder 259,521 gas versus 47,912 for a fractional transfer. The brief asked for a Unipeg-style integer model so the bookkeeping itself is intended, but the per-swap cost is roughly five times a normal v4 swap and the Issuance array grows without bound (one record per crossing receipt, never pruned). This is a usability/economics defect of the accepted design rather than a safety bug: at 20 gwei a buy costs ~0.016 ETH in gas, which can exceed the 2% hook fee on small trades and will dominate trade size on mainnet. A cheaper layout (pack holder/previousHolder/count into fewer slots, or derive the seed lazily from the batch instead of storing it) preserves the glyph semantics.","line":122,"path":"src/SwarmPeg.sol","reproduction":"forge test on a scratch file that inherits test/helpers/Fixture.sol: _deploy(true); _initializeAndSeed(); measure gasleft() around _swap(true, true, 100 ether) -> 803,281 gas; _swap(false, true, 10 ether) -> 877,456; _swap(true, false, 10 ether) -> 648,911; _swap(false, false, 10 ether) -> 743,336. Plain token: new SwarmPeg(); transfer(a, 10 ether); prank(a) transfer(b, 1 ether) -> 259,521 gas; transfer(b, 0.1 ether) -> 47,912 gas. Expected: a swap on the launch pool in the low hundreds of thousands of gas. Actual: 650k-880k.","severity":"low","snippet":"                _batches[to][batchCount[to]++] = Batch(oldTo, current, first);\n                _issuances.push(Issuance(first, count, oldTo, to, from, seed));","title":"Every whole-token receipt writes a 6-slot Issuance plus a Batch, so a swap costs 650k-880k gas and a plain transfer ~260k"},{"citation":"resolved","description":"Trust-assumption record, not a bypass. The brief says 100% of the fee goes to an ownerless maintenance pot the hook deploys in its constructor. The implementation honours that literally: redeem(amount) burns the pot's ERC-6909 IMD claims and takes the IMD to the pot itself, and the contract exposes no transfer, approve, call, or owner. The pot therefore cannot fund any maintenance; the 2% fee is economically a burn into a contract address. The notes and docs/SECURITY.md state this interpretation openly. If the requester expected the pot to be spendable (the word 'maintenance' suggests so), this is a scope decision they must make before launch, because the system is unupgradeable and the pot address is fixed by the hook constructor; after deployment nothing can be changed. Reporting so the judge can confirm the interpretation with the requester rather than as a code defect.","line":54,"path":"src/MaintenancePot.sol","reproduction":"State: after any fee-bearing swap, pot.claimBalance() > 0. Call pot.redeem(pot.claimBalance()) from any address: IMD.balanceOf(pot) rises by that amount and claimBalance() becomes 0. There is no function on MaintenancePot (ABI: redeem, unlockCallback, claimBalance, reserveBalance, totalRedeemed, poolManager, currency) that can move IMD out, and the hook never references the pot except to mint claims. Expected per a 'maintenance' pot: some distribution or spending rule. Actual: funds are locked forever.","severity":"info","snippet":"        poolManager.take(currency, address(this), amount);","title":"MaintenancePot has no spending path: 100% of hook fees are permanently unreachable by anyone"},{"citation":"resolved","description":"Execution-trace note on cosmetic state. currentPulse() subtracts whole elapsed minutes from storedPulse. checkpointPulse() is careful to advance pulseUpdatedAt only by elapsed*60 so fractional minutes are preserved, and its comment says callers cannot slow decay. afterSwap, however, writes block.timestamp directly, so every trade forgets up to 59 seconds of decay progress. A trader submitting a 1-wei-output-sized trade every 59 seconds keeps the pulse from ever decaying while still adding 25 per trade; conversely a quiet pool that traded at second 59 of a minute loses nothing but the fractional minute. The pulse is explicitly cosmetic and never gates funds, so this is informational: the stated guarantee ('callers cannot slow it') holds for checkpointPulse but not for afterSwap. Fix: pulseUpdatedAt += ((block.timestamp - pulseUpdatedAt) / DECAY_SECONDS) * DECAY_SECONDS in afterSwap as well, or set it to block.timestamp only when the pulse was zero.","line":153,"path":"src/SwarmPegHook.sol","reproduction":"Fixture _deploy(true); _initializeAndSeed(); _swap(true,true,1 ether) -> currentPulse()==25. Then 50 times: vm.warp(block.timestamp+59); _swap(true,true,1 ether). Measured: currentPulse()==1000 after the loop (would be 25*51 - 49 = 1226 capped to 1000 either way, but pulseUpdatedAt == block.timestamp each time), and after a further vm.warp(+59) it is still 1000 with zero decay accrued over 50 minutes of wall-clock time. Expected per the checkpointPulse comment: 49 whole minutes of decay (49 points) would have accrued across the sequence.","severity":"info","snippet":"            pulseUpdatedAt = block.timestamp;","title":"afterSwap resets pulseUpdatedAt to block.timestamp, discarding partial-minute decay; sustained sub-minute trading never decays"},{"citation":"resolved","description":"Periphery/dependency record. The hook, pot and manifest all bind to IMD at this constant, which is correct for the mainnet launch and matches launch.json pairedCurrency. I checked the live contract (eth_getCode via a public RPC): 22.9 KB of runtime, name 'Identity.md', owner() = 0x047F606fD5b2BaA5f5C6c4aB8958E45CB6B054B7 (an EOA), with renounceOwnership/transferOwnership present and no pause, unpause, mint, burn, blacklist, isBlacklisted, permit, upgradeTo, maxWallet, excludeFromFees or AMM-pair selectors in the bytecode, and no EIP-1967 implementation slot. So today IMD behaves as a plain ERC-20 and the repository's fork rehearsal at block 26,203,520 agrees. The residual assumption is that the IMD owner never deploys logic that taxes or blocks transfers between routers, the PoolManager and the pot; if that ever happened, pot.redeem would revert (claims stay intact, no loss) and router settlement would under-deliver. No code change is needed; recorded so the judge has the evidence.","line":26,"path":"src/SwarmPegHook.sol","reproduction":"cast code 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7 --rpc-url https://ethereum-rpc.publicnode.com | wc -c -> 45983 hex chars; cast call <IMD> 'owner()(address)' -> 0x047F606fD5b2BaA5f5C6c4aB8958E45CB6B054B7; cast storage <IMD> 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc -> 0 (not an EIP-1967 proxy); grep of the runtime for selectors 8456cb59 (pause), 40c10f19 (mint), f9f92be4 (blacklist), 3659cfe6 (upgradeTo) -> none present.","severity":"info","snippet":"    address public constant IMD = 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7;","title":"Pair currency is a hardcoded Ownable mainnet token; hook and pot depend on its owner never adding transfer restrictions"}],"hash":"7ded6207702211f9e04c7a5a77f503a5fdf9af41bf129c21a8330f0dfaaa9adb","nodeId":"23100bac-38b7-4902-810d-8fa14b179f07","outcome":"completed","summary":"The review is complete. The findings file is written, all four snippets verified against the tree, and the scratch directory removed.\n\n**Outcome:** no critical, high or medium defects in the control flow. The hook's fee path, the pot's redeem path, the token's glyph accounting and the manifest all hold under the Execution Trace, Periphery and First Principles checklists.\n\n**What I verified**\n- Fee accounting in all four swap modes nets the hook's delta to zero. The fee is minted as an ERC-6909 claim to the pot, so no manager inventory is needed.\n- Full-fill checks on the two IMD-specified modes revert before any state write. A sweep of 14 exact-output amounts in both directions never trips the check through core rounding.\n- The pot's redeem is reentrancy-safe, burns only its own claims, and takes only to itself.\n- The token's batch and issuance structures stay synchronized under last-in-first-out retirement.\n- launch.json constructor order, permissions, fee tier, tick spacing and paired currency match the implementation and the protected tests.\n- The real IMD token on mainnet is a plain Ownable ERC-20 with an EOA owner and no pause, mint, blacklist, tax or proxy selectors.\n\n**Findings reported in `.imd-findings.json`**\n1. **Low.** Every whole-token receipt writes about eleven cold storage slots. Measured swap cost is 650k to 880k gas and a plain crossing transfer is about 260k. Design-intended, but the cost is roughly five times a normal v4 swap.\n2. **Info.** The maintenance pot has no spending path, so fees are permanently locked. The docs say this openly. The requester should confirm that reading before an unupgradeable launch.\n3. **Info.** afterSwap resets the pulse timestamp to the current block, discarding partial-minute decay, unlike checkpointPulse. Cosmetic only.\n4. **Info.** The pair currency is a hardcoded owner-controlled token. Recorded as an external dependency with the on-chain evidence.\n\n**Coverage:** all eight listed entry points have rows, plus rows for beforeSwap, the constructor and manifest, and delta conservation. The launch script, artifact checker and SVG renderer are marked unreached in depth.","treeHash":null,"usage":{"cachedInputTokens":1150789,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":39914,"runtime":"claude","turns":35,"wallClockMs":712519}},{"artifacts":[],"attempt":1,"bundleHash":"3b66a0f57203b10e6d6e1c9d07773b67fe9d751f963054a1c7b41d39a84daf13","device":"12d084f65a6fbbd8","findings":[],"hash":"8be4b33f3aa146d65f9ddbac37ef21e03fceedc5e1d7ea5c8c9e6d6c5b7cee06","nodeId":"77d71442-403b-4f80-9e7e-3b36c616c59c","outcome":"completed","summary":"Implemented contracts, animated glyphs, tests, documentation, and [launch.json](/home/seat/.identitymd/work/2ebd5179-1bec-4e29-8c7a-53b41eeb3088/77d71442-403b-4f80-9e7e-3b36c616c59c/launch.json).\n\nVerified: clean build, 39 tests, formatting, 11 pinned admission checks, and real-IMD mainnet fork rehearsal.\n\nDocumented limits: the pot is permanently reserved; exact-input buys and exact-output sells require full fills.\n\nNo mainnet transaction was broadcast. Production deployment remains with IdentityMD’s authorized factory pipeline.","treeHash":"1ad78d0aae61bdf371b56558d2959a5e9deb7792","usage":{"cachedInputTokens":5668480,"inputTokens":302653,"model":"gpt-6-astra","outputTokens":74086,"runtime":"codex","turns":8,"wallClockMs":2497902}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"d7e6877fd9e388bb","findings":[{"citation":"resolved","description":"Merged from audit_economics and audit_flow (same root cause). Any ERC-20 transfer that raises the recipient's whole-token count appends a 3-slot Batch and a 6-slot Issuance (firstId, count, startSlot, holder, previousHolder, seed are each a full slot; the two addresses are not packed), bumps batchCount and nextGlyphId, and emits two events. A v4 swap moves SWARMPEG at least twice (PoolManager -> router -> trader on a buy; trader -> router -> PoolManager on a sell), so the mirror is paid on each hop. The integer-glyph model is what the brief asks for, but the storage layout is an implementation choice: packing Issuance into 3 slots (firstId, count and startSlot as uint80s in one word; holder in a second with previousHolder derivable from the batch event or stored as a third; seed) and Batch into one slot of three uint80 fields roughly halves the per-crossing cost without changing glyph semantics. No funds are at risk; this is economic friction paid by every trader of the launch pool on mainnet (at 20 gwei a sell is ~0.016 ETH of gas, several times a plain v4 swap), and the issuance array grows by one record per crossing receipt and is never pruned.","line":122,"path":"src/SwarmPeg.sol","reproduction":"test/scratch/Judge.t.sol on the repository Fixture (_deploy(true); _initializeAndSeed(); one warm buy and sell), gasleft() around each router call: exact-input buy of 100 IMD 651,897 gas; exact-input sell of 100 tokens 761,435; exact-output buy of 10 tokens 597,572; exact-output sell of 10 IMD 813,612; an exact-output buy of 0.1 token that crosses no whole unit 261,869. Plain token: trader -> a 10 tokens, then a -> b 1 token to a fresh holder 259,867 gas versus 50,267 for 0.5 token that crosses nothing. Expected for a v4 pool with a fee hook: 200k-300k per swap and ~50k per transfer.","severity":"low","snippet":"                _batches[to][batchCount[to]++] = Batch(oldTo, current, first);\n                _issuances.push(Issuance(first, count, oldTo, to, from, seed));","title":"Glyph bookkeeping writes ~10 cold storage slots per whole-token crossing, so every launch-pool swap costs 600k-815k gas and a plain 1-token transfer 260k"},{"citation":"resolved","description":"Merged from audit_economics, audit_permissions and audit_flow (same root cause). The pot's ABI is redeem(uint256), unlockCallback(bytes) and views. redeem burns the pot's own ERC-6909 IMD claims and takes the IMD to address(this); there is no transfer, approve, donate, bounty, sweep or arbitrary-call path, no owner, and the hook never reads the pot. Economically the fee is a burn into a contract address: it cannot fund keepers, buybacks, LP top-ups or any maintenance, and nothing is upgradeable so this cannot be added later. README.md and docs/SECURITY.md state this as the chosen interpretation because the brief names no spending policy. This is not a permission bypass; it is a scope decision the requester must confirm before launch, because the word 'maintenance' implies the reserve is usable. If spending is wanted, a rule-bound, ownerless outflow (fixed bounty to redeem/checkpointPulse callers, or donate-to-pool via poolManager.donate from the pot's redeemed IMD) is the minimal change that keeps the pot ownerless.","line":54,"path":"src/MaintenancePot.sol","reproduction":"Fixture: _deploy(true); _initializeAndSeed(); _swap(true,true,100 ether) -> hook.totalFees()==2e18, pot.claimBalance()==2e18. Any address calls pot.redeem(2e18): IMD.balanceOf(pot)==2e18, claimBalance()==0. Afterwards every attempt to move it fails: withdraw(uint256), sweep(address), transfer(address,uint256), approve(address,uint256), owner() all revert (selectors do not exist, as test_permissionsAddressSizeAndNoOwner shows for withdraw/owner), the pot never sets an ERC-6909 operator, and no contract holds a reference that could call it. Expected for a 'maintenance pot': some rule-bound way to spend; actual: funds locked forever.","severity":"low","snippet":"        poolManager.take(currency, address(this), amount);","title":"MaintenancePot is a terminal sink: 100% of the 2% IMD fee can never be spent on anything by anyone"},{"citation":"resolved","description":"Merged from audit_math (dust trades) and audit_flow (fractional-minute reset); the rounding note from audit_math is the enabling cause. The pulse branch is gated only on both legs moving, while the hook fee is floor(gross/50), so an exact-input buy of 1..49 wei of IMD pays 0 hook fee (and 1 wei LP fee) yet adds 25 pulse, increments tradeCount and sets pulseUpdatedAt = block.timestamp. Unlike checkpointPulse, which advances pulseUpdatedAt by whole minutes only, afterSwap writes the raw timestamp, so each counted trade forgets up to 59 s of decay; one dust trade every 59 s keeps the pulse from ever decaying. docs/SECURITY.md says wash trades raise pulse 'subject to swap fees' and the checkpointPulse comment says callers cannot slow decay; both fail at this boundary. Impact is cosmetic (pulse drives node count, palette, animation speed and the glyph seed) and the attacker pays ~600k gas per trade, so low. Minimal fixes that keep the design: count a trade toward pulse only when fee != 0 (or above a minimum gross), and in afterSwap set pulseUpdatedAt = block.timestamp - ((block.timestamp - pulseUpdatedAt) % DECAY_SECONDS) so the fractional minute carries over.","line":149,"path":"src/SwarmPegHook.sol","reproduction":"test/scratch/Judge.t.sol::test_dustPulse on the Fixture (pool 1:1, 1e24 full-range liquidity), after warping until currentPulse()==0: 40 times _swap(true,true,49) i.e. router.swap(key, SwapParams(zeroForOne=(currency0==IMD), amountSpecified=-49, MIN/MAX limit), 296, 0, ''). Measured: hook.currentPulse()==1000, hook.totalFees() unchanged (0 fee accrued), pot.claimBalance() unchanged, total IMD spent 1,960 wei. Then 10 times {vm.warp(+59); _swap(true,true,49)}: 590 s elapse and currentPulse() is still 1000 with zero decay applied. Expected per the docs: pulse rises only with fee-paying trades and 9 whole minutes of decay accrue across 590 s.","severity":"low","snippet":"        if (imdAmount > 0 && tokenDelta != 0) {\n            uint256 pulse = currentPulse() + PULSE_PER_TRADE;\n            if (pulse > MAX_PULSE) pulse = MAX_PULSE;\n            storedPulse = pulse;\n            pulseUpdatedAt = block.timestamp;","title":"Pulse counts fee-free dust trades and discards the partial minute on every trade, so pulse can be saturated and its decay frozen for 0 IMD of hook fee"},{"citation":"resolved","description":"From audit_economics; reproduced and kept as an integrator note. In the two modes where IMD is the specified currency the fee is committed as a beforeSwap specified delta, and afterSwap can only adjust the unspecified side, so the hook correctly refuses to tax unfilled volume by reverting when the core fills less than the adjusted IMD leg. The consequence is that routers relying on partial fills (exact-in with a tight sqrtPriceLimitX96, or a range exhausted on the token side) see PartialFillUnsupported rather than the partial execution a hookless pool would give. Documented in README; funds are safe and the revert rolls back fees, pulse and glyphs. Integrators should quote exact-output buys / exact-input sells where partial fills matter.","line":143,"path":"src/SwarmPegHook.sol","reproduction":"Fixture after _initializeAndSeed(): zeroForOne = (currency0 == IMD); router.swap(key, SwapParams(zeroForOne, -1000 ether, zeroForOne ? Q96 - 1 : Q96 + 1), 1000 ether, 0, '') reverts with PartialFillUnsupported and hook.totalFees() stays 0 (repository test test_priceLimitPartialSpecifiedIMDRevertsWithoutTax), while the same limit with amountSpecified = -1000 ether on the token side executes partially and charges only the executed IMD (test_partialUnspecifiedIMDChargesOnlyExecutedVolume).","severity":"info","snippet":"            if (imdAmount != expected) revert PartialFillUnsupported();","title":"Exact-input buys and exact-output sells revert on any partial fill at the price limit instead of filling what the pool can"},{"citation":"resolved","description":"From audit_economics; reproduced. The hook taxes only swaps routed through its own pool (token/IMD, fee 12500, spacing 60, this hook). SwarmPeg is a standard unrestricted ERC-20, so anyone can initialize a second v4 pool for the same pair with hooks = address(0) (or a v2/v3 pool) and trade without the hook fee; if liquidity migrates there the pot stops accruing. This is inherent to a hook-based fee on a standard token and is the architecture the launch reference prescribes, so it is not a code defect; recorded so the requester's reading of 'every buy and sell' is set correctly.","line":249,"path":"src/SwarmPegHook.sol","reproduction":"After launch: manager.initialize(PoolKey(currency0, currency1, 3000, 60, IHooks(address(0))), Q96) succeeds for the same two currencies (no hook consulted); a swap in that pool leaves pot.claimBalance() and hook.tradeCount() unchanged. Expected by a literal reading of the brief: every trade pays 2%; actual: only swaps in the bound pool do.","severity":"info","snippet":"        if (PoolId.unwrap(key.toId()) != PoolId.unwrap(poolId)) revert InvalidPool();","title":"The 2% fee binds only the single launch pool; a hookless SWARMPEG/IMD pool or any other venue trades fee-free"}],"hash":"d34f8e4f315ab3dbcf6541f5ed7c2839fd45bf6858b55a0e27035106bc2c6d3c","nodeId":"8d331d4f-76ab-423a-aee7-938075f5e9c6","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds five findings and eleven coverage rows, all snippets verified against the tree.\n\n**Outcome: no blocking defects.** The fee accounting, pot redemption, initialization gate, deployability and manifest all hold. All 73 local tests pass, the hook's runtime and initcode sizes are well under the limits, and the address flags match the declared permissions.\n\n**Findings kept, after reproducing each against the code:**\n\n1. **Low, gas cost of glyph bookkeeping** (`src/SwarmPeg.sol:122`). Merged from the economics and flow specialists. I measured swaps at roughly 600k to 815k gas and a whole-token transfer at 260k versus 50k without a crossing. The model is requested; the unpacked storage layout is not.\n2. **Low, the maintenance pot is a terminal sink** (`src/MaintenancePot.sol:54`). Merged from three specialists. Redeem moves IMD into the pot and nothing can ever move it out. Documented as deliberate, but it needs the requester's confirmation since \"maintenance\" implies spendable funds.\n3. **Low, pulse can be saturated and frozen for free** (`src/SwarmPegHook.sol:149`). Merged the math specialist's dust-trade point with the flow specialist's fractional-minute reset. Forty 49-wei buys reach pulse 1000 with zero hook fee, and one trade every 59 seconds stops decay entirely. Cosmetic only.\n4. **Info, partial fills revert in the two specified-IMD modes** (`src/SwarmPegHook.sol:143`). Correct and documented, kept as an integrator note.\n5. **Info, the fee binds only the launch pool** (`src/SwarmPegHook.sol:249`). Inherent to a hook fee on a standard token.\n\n**Dropped:** the fee-rounding note folded into finding 3 as its enabling cause, and the IMD dependency note since it reports a verified-safe dependency rather than a defect. The static analysis leads (divide-before-multiply, strict equality, encodePacked, weak randomness, reentrancy) were each traced and are intended behaviour or cosmetic entropy, not defects.\n\n**Coverage:** all eight listed entry points are answered, five as holds and three pointing at findings 1 and 3, plus rows for beforeSwap, deployability and the manifest. No proofs were attached since nothing reached high severity.","treeHash":null,"usage":{"cachedInputTokens":1129897,"inputTokens":322,"model":"claude-fable-5-1","outputTokens":33539,"runtime":"claude","turns":26,"wallClockMs":647281}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"dd2ee4882a1be950","findings":[{"citation":"resolved","description":"The pulse branch in afterSwap is gated by `imdAmount > 0 && tokenDelta != 0`, while the hook fee is `amount / 50` (floor) and the LP fee on a sub-100-wei input is 1 wei. Seam (boundary x precision x invariant): an exact-input buy of 49 wei IMD (any gross leg of 1..49 wei behaves the same, provided the net 1.25%-fee-adjusted input still buys at least 1 wei of token) pays a hook fee of 0 (49/50 = 0) and an LP fee of 1 wei, yet produces nonzero token output and therefore counts as a full trade: +25 pulse, ++tradeCount, pulseUpdatedAt = block.timestamp. Forty such buys (1,960 wei of IMD, about 2e-15 IMD) saturate the pulse at MAX_PULSE and leave totalFees at 0. Because line 153 also resets pulseUpdatedAt to block.timestamp on every counted trade (discarding the partial minute, unlike checkpointPulse which preserves it), one 49-wei buy every 59 seconds keeps the pulse from ever decaying: ten such trades over 590 s leave pulse at 275 (25 + 10*25) with zero decay applied. docs/SECURITY.md:42 states 'Wash trades can raise pulse, subject to swap fees' and README states 'repeated calls [to checkpointPulse] ... cannot postpone decay'; both guarantees fail at the fee-rounding boundary since a trade postpones decay and the dust trade pays no hook fee. Impact is cosmetic (pulse only drives glyph traits: node count, palette, animation speed, and the glyph seed), so this is low. Fix options that preserve the design: count a trade toward pulse only when it paid a nonzero hook fee (fee != 0), or require a minimum gross IMD per counted trade (e.g. >= 1e15 wei), and/or carry the fractional minute across trades the way checkpointPulse does (pulseUpdatedAt = block.timestamp - ((block.timestamp - pulseUpdatedAt) % DECAY_SECONDS)).","line":149,"path":"src/SwarmPegHook.sol","reproduction":"Fixture from test/helpers/Fixture.sol: _deploy(true); _initializeAndSeed() (pool at 1:1, 1e24 full-range liquidity). Loop 40 times: _swap(true, true, 49) i.e. router.swap(key, SwapParams(zeroForOne=(currency0==IMD), amountSpecified=-49, limit=MIN/MAX), 296, 0, ''). Each swap returns tokenDelta > 0. Expected per docs: pulse rises only with fee-paying trades and decay cannot be postponed. Actual: hook.currentPulse() == 1000, hook.tradeCount() == 40, hook.totalFees() == 0, pot.claimBalance() == 0, total IMD spent 1,960 wei. Second sequence: one _swap(true,true,100 ether) at t0 (pulse 25), then at t0+59, t0+118, ..., t0+590 one more trade each: currentPulse() == 275 although 590 s elapsed (9 minutes of decay would have applied had the fractional minutes been kept). Scratch test test/scratch/PulseDust.t.sol (functions test_dustBuysSaturatePulseForFree and test_tradeResetsFractionalMinute) passes on the current code and logs these values.","severity":"low","snippet":"        if (imdAmount > 0 && tokenDelta != 0) {","title":"Dust trades below the fee floor count as full trades: pulse can be saturated and its decay postponed forever at zero fee cost"},{"citation":"resolved","description":"All four fee formulas (amount/50, amount/49, imdAmount/49, imdAmount/50) floor. The Math Precision checklist asks fees to round in the protocol's favour (up). Every swap therefore pays floor(2% of gross) rather than ceil, and a gross IMD leg of 1..49 wei pays 0. The two gross-up forms are exact: for I = 49q + r, floor(I/49) = q = floor((I + q)/50), so all modes agree on floor(gross/50); there is no mode a trader can pick to pay less than another mode for the same gross. Loss per swap is < 1 wei of an 18-decimal token, non-compounding, and the behaviour is documented in README ('dust below 50 IMD minor units can round to zero'). Reported for completeness only; the only consequence of note is the pulse seam in finding 1.","line":115,"path":"src/SwarmPegHook.sol","reproduction":"Fixture as in finding 1. _swap(true, true, 49): hook.totalFees() stays 0 (exact 2% would be 0.98 wei). _swap(true, true, 99): totalFees becomes 1 (1.01% of gross; ceil would be 2). Scratch test test/scratch/PulseDust.t.sol::test_feeRoundsDown passes on the current code with these values.","severity":"info","snippet":"            fee = params.amountSpecified < 0 ? amount / 50 : amount / 49;","title":"Hook fee rounds down, so every swap under-collects by up to 1 wei of IMD and gross inputs below 50 wei pay nothing"}],"hash":"f9d50ca6e96b25259309e7bc14b65314da55ae1c9fc66d002aa3e3be738de464","nodeId":"bab998ab-4782-4371-a301-197c0f8789f5","outcome":"completed","summary":"Review complete. The findings file is written at `.imd-findings.json` and the working tree is otherwise unchanged (the scratch test lives under the gitignored `test/scratch/`).\n\n**Result: the math is sound. One low and one informational finding, no medium or above.**\n\nCovered in depth (Math Precision, Boundary, Numerical Gap guides):\n\n- **Hook fee math** holds in all four swap modes. The two gross-up formulas are exact identities: for I = 49q + r, floor(I/49) equals floor((I + floor(I/49))/50), so every mode charges floor(2% of gross IMD) and no mode is cheaper than another. The full-fill equality check is safe because Pool.swap drives the remaining amount to exactly zero. Hook deltas net to zero (the ERC-6909 mint debits exactly the positive return delta). Amount bounds, the int128 gross-up check and SafeCast all hold.\n- **Glyph integer-balance model** holds at every boundary: fractional crossings up and down, retirement to zero, last-in-first-out truncation, contiguous batch slots, fresh IDs, no-op self or zero transfers, and the billion-glyph mint in one batch.\n- **Pulse decay** cannot be slowed by repeated `checkpointPulse` calls, the SVG traits cannot underflow since pulse is capped at 1000, and the renderer loop is bounded.\n- **MaintenancePot** redeem path is symmetric and rolls back cleanly. Mainnet IMD is a plain LayerZero OFT ERC-20 with no transfer fee, pause or blocklist, so the take path has no token-behavior seam.\n- **launch.json** matches the implementation: constructor argument order, permissions and the 0x20cc flags, fee 12500 and spacing 60, lowercase pair address, notes under the limit.\n\nFindings:\n\n1. **Low.** Dust trades pay zero hook fee yet count as full trades. Forty 49-wei buys saturate the pulse with zero fees collected, and one such buy every 59 seconds postpones decay forever because a trade resets the timestamp and drops the partial minute. This contradicts the security notes' \"subject to swap fees\" and \"cannot postpone decay\" claims. Impact is cosmetic only.\n2. **Info.** Fees floor rather than round up, so each swap under-collects by under 1 wei. Documented, non-compounding.\n\nBoth are reproduced by `test/scratch/PulseDust.t.sol`, which passes on the current code and logs the concrete values. No proofs were attached since nothing reached high severity.","treeHash":null,"usage":{"cachedInputTokens":1622504,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":38707,"runtime":"claude","turns":32,"wallClockMs":646655}}],"verification":[{"checks":[{"durationMs":59781,"exitCode":0,"name":"build","output":"Compiling 99 files with Solc 0.8.26\nSolc 0.8.26 finished in 59.64s\nCompiler run successful!\n","passed":true},{"durationMs":8488,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 9 tests for test/SwarmPeg.t.sol:SwarmPegTest\n[PASS] testFuzz_transferConservesAndMirrors(uint256,uint256) (runs: 256, μ: 396259, ~: 410278)\nLogs:\n  Bound result 9554943053527040327060069\n  Bound result 2483619886987894055674523\n\n[PASS] test_bulkTransferBoundedGasAndEmptyHolderCannotReadGlyph() (gas: 362050)\n[PASS] test_failuresLeaveBalancesIntact() (gas: 77250)\n[PASS] test_fixedSupplySingleMintAndMetadata() (gas: 66747)\n[PASS] test_fractionalCrossingsRetireAndCreateOnlyWholeSlots() (gas: 1047440)\n[PASS] test_noOwnerMintPauseUpgradeOrIndependentGlyphTransfer() (gas: 174861)\n[PASS] test_transferAndAllowanceNoTax() (gas: 374673)\n[PASS] test_truncatingManyBatchesAndAppendingDoesNotResurrectOldIds() (gas: 10215864)\n[PASS] test_zeroSelfAndSubunitTransfersDoNotRerollExistingGlyphs() (gas: 509198)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 43.88ms (56.75ms CPU time)\n\nRan 5 tests for test/SwarmPegHook.t.sol:InitializationTest\n[PASS] test_factoryDeploymentAndAllocation() (gas: 688408)\n[PASS] test_freshManagerTokenOnlyLiquidityBuyCanAccrueAndRedeem() (gas: 1502034)\n[PASS] test_noLiquiditySwapCannotProduceUnbackedClaims() (gas: 716527)\n[PASS] test_nonFactoryAndWrongPoolCannotInitialize() (gas: 237708)\n[PASS] test_swapsBeforeInitializationRefused() (gas: 23469)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 156.34ms (2.59ms CPU time)\n\nRan 15 tests for test/SwarmPegHook.t.sol:SwarmPegHookTest\n[PASS] testFuzz_feeAllModes(uint80,bool,bool) (runs: 256, μ: 632299, ~: 446774)\nLogs:\n  Bound result 3474\n\n[PASS] test_allFourSwapModes() (gas: 3138582)\n[PASS] test_callbacksRejectUnauthorizedIncludingAfterSwap() (gas: 147956)\n[PASS] test_claimsCanBeRedeemedByAnyoneOnlyToPot() (gas: 1041611)\n[PASS] test_dustRoundingHasNoMinimumTax() (gas: 1612580)\n[PASS] test_factoryCollectsLPFeesAndRemovesWithoutHookTax() (gas: 2177817)\n[PASS] test_failedRedemptionPreservesClaimsAndReentrantRedemptionFails() (gas: 1171353)\n[PASS] test_foreignPoolAndInvalidAmountsRefused() (gas: 144340)\n[PASS] test_hookDataCannotAssignGlyphsToSpoofedHolder() (gas: 786744)\n[PASS] test_partialUnspecifiedIMDChargesOnlyExecutedVolume() (gas: 956346)\n[PASS] test_permissionsAddressSizeAndNoOwner() (gas: 100630)\n[PASS] test_priceLimitPartialSpecifiedIMDRevertsWithoutTax() (gas: 589835)\n[PASS] test_pulseDecayCheckpointDoesNotFreezeFractionalMinutes() (gas: 3476545)\n[PASS] test_pulseSaturatesAndGlyphsAreUniqueAnimatedAndOnChain() (gas: 37541304)\n[PASS] test_routerSlippageRollsBackPoolFeesPulseAndGlyphs() (gas: 611478)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 197.80ms (114.61ms CPU time)\n\nRan 3 tests for test/SwarmPegHook.t.sol:ReverseCurrencyOrderTest\n[PASS] testFuzz_feeAllModes(uint80,bool,bool) (runs: 256, μ: 614247, ~: 439015)\nLogs:\n  Bound result 8925819614629174826055\n\n[PASS] test_allFourSwapModes() (gas: 3126437)\n[PASS] test_dustRoundingHasNoMinimumTax() (gas: 1605864)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 197.85ms (151.54ms CPU time)\n\nRan 5 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorDoesNotCallExternalManagerOrToken() (gas: 25346785)\n[PASS] test_constructorRefusesMissingOrPairTokenArguments() (gas: 8720)\n[PASS] test_prepareLaunchPredictsActualFactoryAddress() (gas: 52762026)\n[PASS] test_prepareRejectsInvalidSearch() (gas: 3274871)\n[PASS] test_wrongAddressFlagsRefused() (gas: 36935)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 209.45ms (101.71ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:AccountingInvariantTest\n[PASS] invariant_feesConservedAndManagerSettled() (runs: 64, calls: 4096, reverts: 0)\n\n╭-------------+----------+-------+---------+----------╮\n| Contract    | Selector | Calls | Reverts | Discards |\n+=====================================================+\n| SwapHandler | advance  | 1370  | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| SwapHandler | redeem   | 1443  | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| SwapHandler | trade    | 1283  | 0       | 0        |\n╰-------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 9999999999999999247\n  Bound result 11082\n  Bound result 768936388214756\n  Bound result 0\n  Bound result 2807\n  Bound result 13491\n  Bound result 81332724126360531\n  Bound result 9999999999999999299\n  Bound result 18\n  Bound result 3861\n  Bound result 2417\n  Bound result 2899\n  Bound result 92609963087448152\n  Bound result 29232750597079314\n  Bound result 2427\n  Bound result 2330\n  Bound result 6316\n  Bound result 369589\n  Bound result 5\n  Bound result 1738\n  Bound result 9999999999999999679\n  Bound result 2107\n  Bound result 3117\n  Bound result 1001\n  Bound result 8010\n  Bound result 49\n  Bound result 2108\n  Bound result 1979\n  Bound result 2741\n  Bound result 8653\n  Bound result 1135\n  Bound result 200\n  Bound result 0\n  Bound result 10051\n  Bound result 1\n  Bound result 2\n  Bound result 3000\n  Bound result 321552982790986535\n  Bound result 132\n  Bound result 17\n  Bound result 4528\n  Bound result 39952502245\n  Bound result 16\n  Bound result 27567171111605415\n  Bound result 40\n  Bound result 31739415855994019\n  Bound result 10\n  Bound result 205\n  Bound result 9855\n  Bound result 137\n  Bound result 9999999999999999028\n  Bound result 265\n  Bound result 2533\n  Bound result 81638205611488378\n  Bound result 90143901562599809\n  Bound result 158\n  Bound result 9999999999999999614\n  Bound result 1808\n  Bound result 8734\n  Bound result 217450784012324758\n  Bound result 1000\n  Bound result 6860\n  Bound result 50\n  Bound result 49\n  Bound result 3067\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.27s (1.11s CPU time)\n\nRan 1 test for test/Invariants.t.sol:GlyphInvariantTest\n[PASS] invariant_supplyAndWholeBalanceOwnership() (runs: 64, calls: 4096, reverts: 0)\n\n╭----------------+----------+-------+---------+----------╮\n| Contract       | Selector | Calls | Reverts | Discards |\n+========================================================+\n| BalanceHandler | move     | 4096  | 0       | 0        |\n╰----------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 1210\n  Bound result 2\n  Bound result 4294967295\n  Bound result 257\n  Bound result 2652\n  Bound result 3900\n  Bound result 5506757738487822030\n  Bound result 38\n  Bound result 1000000000000000000\n  Bound result 4800\n  Bound result 255\n  Bound result 507191715817682180\n  Bound result 9253510434373454544\n  Bound result 4264\n  Bound result 678009306304\n  Bound result 4928082841823232780\n  Bound result 4096\n  Bound result 4288775753\n  Bound result 3071\n  Bound result 3\n  Bound result 16383\n  Bound result 2745\n  Bound result 339743544277035314\n  Bound result 386933588275851742\n  Bound result 2684094183\n  Bound result 120\n  Bound result 2500000000000000000\n  Bound result 5597\n  Bound result 2904\n  Bound result 45\n  Bound result 120\n  Bound result 128\n  Bound result 2616\n  Bound result 6205691439092643939\n  Bound result 40\n  Bound result 7425158787308431938\n  Bound result 2313304458289535816\n  Bound result 2181525798972244596\n  Bound result 8396\n  Bound result 1893163514849905035\n  Bound result 45\n  Bound result 16384\n  Bound result 3855\n  Bound result 4416\n  Bound result 6800834137\n  Bound result 215\n  Bound result 265\n  Bound result 1819645657257008262\n  Bound result 10000000673714323491\n  Bound result 1\n  Bound result 160\n  Bound result 12\n  Bound result 1483968889753348\n  Bound result 16649319526895706657\n  Bound result 1901\n  Bound result 200000000000000000\n  Bound result 1\n  Bound result 14\n  Bound result 4\n  Bound result 128\n  Bound result 6400469101809015858\n  Bound result 611\n  Bound result 25\n  Bound result 313\n  Bound result 1266\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 8.28s (8.27s CPU time)\n\nRan 7 test suites in 8.28s (10.35s CPU time): 39 tests passed, 0 failed, 0 skipped (39 total tests)\n","passed":true},{"durationMs":130,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"MaintenancePot.redeem(uint256)\",\"MaintenancePot.unlockCallback(bytes)\",\"SwarmPeg.approve(address,uint256)\",\"SwarmPeg.transfer(address,uint256)\",\"SwarmPeg.transferFrom(address,address,uint256)\",\"SwarmPegHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"SwarmPegHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"SwarmPegHook.checkpointPulse()\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.sha256\":115,\"LICENSE\":23,\"README.md\":137,\"dependencies.json\":32,\"docs/SECURITY.md\":55,\"docs/VALIDATION.md\":32,\"docs/art/index.html\":19,\"docs/art/pulse-0.svg\":1,\"docs/art/pulse-1000.svg\":1,\"docs/art/pulse-500.svg\":1,\"foundry.toml\":27,\"launch.json\":27,\"rehearsal/Mainnet.t.sol\":55,\"remappings.txt\":4,\"script/PrepareLaunch.s.sol\":32,\"script/RenderPreview.s.sol\":14,\"src/HookFlags.sol\":29,\"src/MaintenancePot.sol\":57,\"src/SwarmGlyphSVG.sol\":128,\"src/SwarmPeg.sol\":142,\"src/SwarmPegHook.sol\":265,\"test/Deployment.t.sol\":68,\"test/Invariants.t.sol\":176,\"test/SwarmPeg.t.sol\":165,\"test/SwarmPegHook.t.sol\":400,\"test/helpers/Fixture.sol\":108,\"test/helpers/LaunchFactoryHarness.sol\":78,\"test/helpers/MockIMD.sol\":34,\"test/helpers/TestRouter.sol\":64,\"test/mocks/MockERC20.sol\":10,\"tools/check_artifacts.py\":37},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"1904d1efd29c2e704d60161ca53fead62cbb864325e0fd0e118f1bb56b838039","verifiedTreeHash":"f4d11c77d4c2c12ebb98d10f418d4bcb947c0563","verifierVersion":"0.1.0+d952fceb"},{"checks":[{"durationMs":97445,"exitCode":0,"name":"build","output":"Compiling 106 files with Solc 0.8.26\nSolc 0.8.26 finished in 97.33s\nCompiler run successful!\n","passed":true},{"durationMs":13605,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 5 tests for test/TokenFailurePaths.t.sol:TokenFailurePathsTest\n[PASS] test_failedApprovedSpendRestoresAllowanceAndGlyphState() (gas: 494252)\n[PASS] test_firstOutOfRangeGlyphAndIssuanceIndicesRevert() (gas: 55933)\n[PASS] test_oneWeiCanRetireASlotWithoutCreatingARecipientSlot() (gas: 486025)\n[PASS] test_revokeAllowancePreventsReplayAndZeroSpenderIsInvalid() (gas: 216919)\n[PASS] test_zeroReceiverFailureDoesNotSpendAllowanceEvenForZeroAmount() (gas: 141745)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 1.06ms (1.35ms CPU time)\n\nRan 3 tests for test/SwarmPegHook.t.sol:ReverseCurrencyOrderTest\n[PASS] testFuzz_feeAllModes(uint80,bool,bool) (runs: 256, μ: 614247, ~: 439015)\nLogs:\n  Bound result 8925819614629174826055\n\n[PASS] test_allFourSwapModes() (gas: 3126437)\n[PASS] test_dustRoundingHasNoMinimumTax() (gas: 1605864)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 30.24ms (30.85ms CPU time)\n\nRan 5 tests for test/SwarmPegHook.t.sol:InitializationTest\n[PASS] test_factoryDeploymentAndAllocation() (gas: 688408)\n[PASS] test_freshManagerTokenOnlyLiquidityBuyCanAccrueAndRedeem() (gas: 1502034)\n[PASS] test_noLiquiditySwapCannotProduceUnbackedClaims() (gas: 716527)\n[PASS] test_nonFactoryAndWrongPoolCannotInitialize() (gas: 237708)\n[PASS] test_swapsBeforeInitializationRefused() (gas: 23469)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 57.86ms (1.88ms CPU time)\n\nRan 9 tests for test/SwarmPeg.t.sol:SwarmPegTest\n[PASS] testFuzz_transferConservesAndMirrors(uint256,uint256) (runs: 256, μ: 403584, ~: 433258)\nLogs:\n  Bound result 2333\n  Bound result 1734\n\n[PASS] test_bulkTransferBoundedGasAndEmptyHolderCannotReadGlyph() (gas: 362050)\n[PASS] test_failuresLeaveBalancesIntact() (gas: 77250)\n[PASS] test_fixedSupplySingleMintAndMetadata() (gas: 66747)\n[PASS] test_fractionalCrossingsRetireAndCreateOnlyWholeSlots() (gas: 1047440)\n[PASS] test_noOwnerMintPauseUpgradeOrIndependentGlyphTransfer() (gas: 174861)\n[PASS] test_transferAndAllowanceNoTax() (gas: 374673)\n[PASS] test_truncatingManyBatchesAndAppendingDoesNotResurrectOldIds() (gas: 10215864)\n[PASS] test_zeroSelfAndSubunitTransfersDoNotRerollExistingGlyphs() (gas: 509198)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 63.74ms (66.91ms CPU time)\n\nRan 5 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorDoesNotCallExternalManagerOrToken() (gas: 25346785)\n[PASS] test_constructorRefusesMissingOrPairTokenArguments() (gas: 8720)\n[PASS] test_prepareLaunchPredictsActualFactoryAddress() (gas: 52762026)\n[PASS] test_prepareRejectsInvalidSearch() (gas: 3274871)\n[PASS] test_wrongAddressFlagsRefused() (gas: 36935)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 102.29ms (46.97ms CPU time)\n\nRan 15 tests for test/SwarmPegHook.t.sol:SwarmPegHookTest\n[PASS] testFuzz_feeAllModes(uint80,bool,bool) (runs: 256, μ: 632299, ~: 446774)\nLogs:\n  Bound result 8253869815131163822288\n\n[PASS] test_allFourSwapModes() (gas: 3138582)\n[PASS] test_callbacksRejectUnauthorizedIncludingAfterSwap() (gas: 147956)\n[PASS] test_claimsCanBeRedeemedByAnyoneOnlyToPot() (gas: 1041611)\n[PASS] test_dustRoundingHasNoMinimumTax() (gas: 1612580)\n[PASS] test_factoryCollectsLPFeesAndRemovesWithoutHookTax() (gas: 2177817)\n[PASS] test_failedRedemptionPreservesClaimsAndReentrantRedemptionFails() (gas: 1171353)\n[PASS] test_foreignPoolAndInvalidAmountsRefused() (gas: 144340)\n[PASS] test_hookDataCannotAssignGlyphsToSpoofedHolder() (gas: 786744)\n[PASS] test_partialUnspecifiedIMDChargesOnlyExecutedVolume() (gas: 956346)\n[PASS] test_permissionsAddressSizeAndNoOwner() (gas: 100630)\n[PASS] test_priceLimitPartialSpecifiedIMDRevertsWithoutTax() (gas: 589835)\n[PASS] test_pulseDecayCheckpointDoesNotFreezeFractionalMinutes() (gas: 3476545)\n[PASS] test_pulseSaturatesAndGlyphsAreUniqueAnimatedAndOnChain() (gas: 37541304)\n[PASS] test_routerSlippageRollsBackPoolFeesPulseAndGlyphs() (gas: 611478)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 113.18ms (116.91ms CPU time)\n\nRan 5 tests for test/AtomicLaunch.t.sol:AtomicLaunchTest\n[PASS] test_atomicLaunchAndAllocationWhenTokenIsCurrency0() (gas: 23615471)\n[PASS] test_atomicLaunchAndAllocationWhenTokenIsCurrency1() (gas: 3885059)\n[PASS] test_atomicLaunchDoesNotGrantOriginOrRequesterAdministration() (gas: 23963116)\n[PASS] test_initializationFailureRevertsAllCreationsAndSaltsRemainUsable() (gas: 23573574)\n[PASS] test_predictedHookWithoutCodeCannotBePreinitialized() (gas: 23545648)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 116.66ms (100.39ms CPU time)\n\nRan 8 tests for test/HookFailurePaths.t.sol:HookFailurePathsTest\n[PASS] testFuzz_beforeSwapCannotConsumeWholeInput(uint256) (runs: 1000, μ: 27981, ~: 27905)\nLogs:\n  Bound result 9858286532931130217767686126157124426\n\n[PASS] test_eachPoolKeyFieldIsBoundForBothSwapCallbacks() (gas: 356083)\n[PASS] test_exactOutputGrossUpCannotOverflowInt128() (gas: 24665)\n[PASS] test_nestedUnlockCannotLeavePotRedemptionActive() (gas: 1483202)\n[PASS] test_partialSpecifiedTradesBubbleExactReasonAndRollBackEverything() (gas: 2600329)\n[PASS] test_settlementFailureRollsBackClaimsEntropyAndGlyphIssuances() (gas: 1574440)\n[PASS] test_signedAmountBoundariesAreRejectedInBothCallbacks() (gas: 288831)\n[PASS] test_wrongSignedDeltasRefusedBeforeAccruingFees() (gas: 415401)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 116.55ms (84.85ms CPU time)\n\nRan 3 tests for test/HookDifferential.t.sol:HookDifferentialTokenBelowTest\n[PASS] testFuzz_swapMatchesUnhookedExecution(uint256,bool,bool) (runs: 1000, μ: 1252076, ~: 1506449)\nLogs:\n  Bound result 1\n\n[PASS] test_fourModesRemainEquivalentAcrossChangingPrices() (gas: 7386328)\n[PASS] test_roundingBoundariesInEveryMode() (gas: 26359457)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 116.64ms (99.20ms CPU time)\n\nRan 3 tests for test/HookDifferential.t.sol:HookDifferentialTokenAboveTest\n[PASS] testFuzz_swapMatchesUnhookedExecution(uint256,bool,bool) (runs: 1000, μ: 1252772, ~: 1515692)\nLogs:\n  Bound result 4660985466942291215323\n\n[PASS] test_fourModesRemainEquivalentAcrossChangingPrices() (gas: 7399721)\n[PASS] test_roundingBoundariesInEveryMode() (gas: 26413946)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 116.69ms (148.35ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:AccountingInvariantTest\n[PASS] invariant_feesConservedAndManagerSettled() (runs: 64, calls: 4096, reverts: 0)\n\n╭-------------+----------+-------+---------+----------╮\n| Contract    | Selector | Calls | Reverts | Discards |\n+=====================================================+\n| SwapHandler | advance  | 1370  | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| SwapHandler | redeem   | 1443  | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| SwapHandler | trade    | 1283  | 0       | 0        |\n╰-------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 9999999999999999247\n  Bound result 11082\n  Bound result 768936388214756\n  Bound result 0\n  Bound result 2807\n  Bound result 13491\n  Bound result 81332724126360531\n  Bound result 9999999999999999299\n  Bound result 2798\n  Bound result 3862\n  Bound result 3062\n  Bound result 2899\n  Bound result 228406642058298768\n  Bound result 24018797138871902\n  Bound result 2427\n  Bound result 2383\n  Bound result 6315\n  Bound result 369589\n  Bound result 200\n  Bound result 1738\n  Bound result 9999999999999999679\n  Bound result 2106\n  Bound result 2741\n  Bound result 1001\n  Bound result 8010\n  Bound result 256\n  Bound result 1142\n  Bound result 1200\n  Bound result 2401\n  Bound result 8678\n  Bound result 1137\n  Bound result 495\n  Bound result 0\n  Bound result 10053\n  Bound result 1\n  Bound result 6\n  Bound result 17\n  Bound result 188254040790056479\n  Bound result 2542\n  Bound result 3600\n  Bound result 4688\n  Bound result 39952502245\n  Bound result 18\n  Bound result 66613009338340948\n  Bound result 10\n  Bound result 16081528017215013\n  Bound result 945\n  Bound result 1\n  Bound result 10002\n  Bound result 40\n  Bound result 9999999999999999028\n  Bound result 150\n  Bound result 2514\n  Bound result 107993064609351356\n  Bound result 1718779881427995\n  Bound result 1022583891\n  Bound result 9999999999999999615\n  Bound result 1808\n  Bound result 10417\n  Bound result 1\n  Bound result 1002\n  Bound result 6861\n  Bound result 64\n  Bound result 256\n  Bound result 423\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 916.79ms (854.68ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:GlyphInvariantTest\n[PASS] invariant_supplyAndWholeBalanceOwnership() (runs: 64, calls: 4096, reverts: 0)\n\n╭----------------+----------+-------+---------+----------╮\n| Contract       | Selector | Calls | Reverts | Discards |\n+========================================================+\n| BalanceHandler | move     | 4096  | 0       | 0        |\n╰----------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 1210\n  Bound result 12\n  Bound result 9\n  Bound result 3598\n  Bound result 2638\n  Bound result 2946\n  Bound result 2432533849284263754\n  Bound result 6225\n  Bound result 1000000000000000000\n  Bound result 360\n  Bound result 2000000000000000000\n  Bound result 2500000000000000000\n  Bound result 8334142693359167294\n  Bound result 4320\n  Bound result 678009306304\n  Bound result 297603581880678509\n  Bound result 1499999993790588701\n  Bound result 452\n  Bound result 3072\n  Bound result 3\n  Bound result 18\n  Bound result 2744\n  Bound result 3725817593337282311\n  Bound result 3375746615344670776\n  Bound result 2643337592\n  Bound result 88\n  Bound result 5000000000000000000\n  Bound result 5621\n  Bound result 1739\n  Bound result 1000000\n  Bound result 6274182409306049891\n  Bound result 1800000000\n  Bound result 2442\n  Bound result 1000000066\n  Bound result 10\n  Bound result 6005027892452429768\n  Bound result 9999999999999994415\n  Bound result 10456663084494070519\n  Bound result 11\n  Bound result 4728152903688329276\n  Bound result 1000000\n  Bound result 1200\n  Bound result 4219\n  Bound result 4445\n  Bound result 6800834137\n  Bound result 361\n  Bound result 150\n  Bound result 23\n  Bound result 13956663752650628844\n  Bound result 1\n  Bound result 8861923597161610103\n  Bound result 6000\n  Bound result 8388608\n  Bound result 1252850140024656828\n  Bound result 1559\n  Bound result 0\n  Bound result 1\n  Bound result 60000\n  Bound result 74969268865824734\n  Bound result 1800000000\n  Bound result 17\n  Bound result 3446\n  Bound result 11\n  Bound result 52028254855566764\n  Bound result 27\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.93s (5.92s CPU time)\n\nRan 6 tests for test/GlyphMetadata.t.sol:GlyphMetadataTest\n[PASS] testFuzz_densityBrightnessAndSpeedAreMonotonic(uint256,uint256) (runs: 128, μ: 39276, ~: 39387)\nLogs:\n  Bound result 527\n  Bound result 954\n\n[PASS] testFuzz_metadataDecodesToTheLiveSVGAndTraits(uint256,uint256) (runs: 128, μ: 70103385, ~: 70212303)\nLogs:\n  Bound result 527\n  Bound result 774660\n\n[PASS] test_allGlyphViewsRejectRetiredAndUnknownIds() (gas: 567301)\n[PASS] test_birthSeedChangesWhenOnlyBlockContextChanges() (gas: 1329659)\n[PASS] test_checkpointAndSelfTransferDoNotRerollArt() (gas: 5671950)\n[PASS] test_peakAndFullyDecayedTraits() (gas: 26396)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 6.64s (6.60s CPU time)\n\nRan 2 tests for test/ReserveSequenceInvariant.t.sol:ReserveSequenceInvariantTest\n[PASS] invariant_reserveSupplySettlementAndPulse() (runs: 256, calls: 24576, reverts: 0)\n\n╭------------------------+-------------------------+-------+---------+----------╮\n| Contract               | Selector                | Calls | Reverts | Discards |\n+===============================================================================+\n| ReserveSequenceHandler | advance                 | 3125  | 0       | 0        |\n|------------------------+-------------------------+-------+---------+----------|\n| ReserveSequenceHandler | donate                  | 3145  | 0       | 0        |\n|------------------------+-------------------------+-------+---------+----------|\n| ReserveSequenceHandler | failedTransferThenRetry | 3078  | 0       | 0        |\n|------------------------+-------------------------+-------+---------+----------|\n| ReserveSequenceHandler | liquidity               | 2960  | 0       | 0        |\n|------------------------+-------------------------+-------+---------+----------|\n| ReserveSequenceHandler | redeem                  | 3102  | 0       | 0        |\n|------------------------+-------------------------+-------+---------+----------|\n| ReserveSequenceHandler | rejectRedemption        | 3029  | 0       | 0        |\n|------------------------+-------------------------+-------+---------+----------|\n| ReserveSequenceHandler | roundTrip               | 3071  | 0       | 0        |\n|------------------------+-------------------------+-------+---------+----------|\n| ReserveSequenceHandler | trade                   | 3066  | 0       | 0        |\n╰------------------------+-------------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000\n  Bound result 256\n  Bound result 360\n  Bound result 807567919262342029451\n  Bound result 14\n  Bound result 29\n  Bound result 6340\n  Bound result 1235424239525298069\n  Bound result 8376213034055181227\n  Bound result 9999999999999998890\n  Bound result 5000000000000000000\n  Bound result 16090\n  Bound result 9999999999999999601\n  Bound result 9999999999999999010\n  Bound result 9086\n  Bound result 991\n  Bound result 2306253191748799651\n  Bound result 1999999999999999778\n  Bound result 3192\n  Bound result 2\n  Bound result 4999999999999999841\n  Bound result 8551\n  Bound result 50\n  Bound result 10000000000000000000\n  Bound result 9000000000000007201\n  Bound result 4999999999999999999\n  Bound result 3059\n  Bound result 2776\n  Bound result 9000000000000000041\n  Bound result 23591543863080038\n  Bound result 6791\n  Bound result 2503\n  Bound result 6914\n  Bound result 2295964837575885778\n  Bound result 9999999999999999417\n  Bound result 1000000000\n  Bound result 9999999999999999006\n  Bound result 3009\n  Bound result 9430\n  Bound result 5795\n  Bound result 149\n  Bound result 4295128741\n  Bound result 866436458917232064\n  Bound result 3000000000000000000\n  Bound result 8323283235649152548\n  Bound result 99900000000\n  Bound result 9005616122273865072\n  Bound result 1000000000000000000000\n  Bound result 200\n  Bound result 9968\n  Bound result 9000000000000000137\n  Bound result 265\n  Bound result 2429\n  Bound result 3000\n  Bound result 9000000003593354943\n  Bound result 4800\n  Bound result 664255470760424458\n  Bound result 7273295338832238083\n  Bound result 1713296073165375798\n  Bound result 3548\n  Bound result 1000000000000000001\n  Bound result 9000000000000017922\n  Bound result 124008023300544699820\n  Bound result 830\n  Bound result 9000000000000003601\n  Bound result 9000000000020000001\n  Bound result 1000000000000000003\n  Bound result 17256\n  Bound result 123456\n  Bound result 5943202923098186325\n  Bound result 3001\n  Bound result 600\n  Bound result 40\n  Bound result 5198\n  Bound result 819638328471574316\n\n[PASS] test_donationsFailuresAndFullRedemptionAfterLiquidityChanges() (gas: 3373524)\nLogs:\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000\n  Bound result 2000000000000000000\n  Bound result 61\n  Bound result 3000000000000000000\n  Bound result 10000000000000000000\n  Bound result 10000000000000000000\n  Bound result 1\n  Bound result 1\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 9.05s (8.98s CPU time)\n\nRan 2 tests for test/GlyphModelInvariant.t.sol:GlyphModelInvariantTest\n[PASS] invariant_balancesAllowancesAndGlyphHistoryMatchModel() (runs: 256, calls: 24576, reverts: 0)\n\n╭-------------------+-----------------------+-------+---------+----------╮\n| Contract          | Selector              | Calls | Reverts | Discards |\n+========================================================================+\n| GlyphModelHandler | approve               | 3569  | 0       | 0        |\n|-------------------+-----------------------+-------+---------+----------|\n| GlyphModelHandler | emptyHolder           | 3437  | 0       | 0        |\n|-------------------+-----------------------+-------+---------+----------|\n| GlyphModelHandler | rejectOverdraft       | 3539  | 0       | 0        |\n|-------------------+-----------------------+-------+---------+----------|\n| GlyphModelHandler | rejectUnapprovedSpend | 3466  | 0       | 0        |\n|-------------------+-----------------------+-------+---------+----------|\n| GlyphModelHandler | rejectZeroRecipient   | 3455  | 0       | 0        |\n|-------------------+-----------------------+-------+---------+----------|\n| GlyphModelHandler | transfer              | 3544  | 0       | 0        |\n|-------------------+-----------------------+-------+---------+----------|\n| GlyphModelHandler | transferFrom          | 3566  | 0       | 0        |\n╰-------------------+-----------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 2182\n  Bound result 0\n  Bound result 5\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 12\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 1024\n  Bound result 0\n  Bound result 3999999999999999917\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 10373881630528777162\n  Bound result 16383\n  Bound result 0\n  Bound result 0\n  Bound result 4583700398411741135\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 4768\n  Bound result 0\n  Bound result 1196\n  Bound result 0\n  Bound result 11281\n  Bound result 0\n  Bound result 3808\n  Bound result 265\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 72\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 916\n  Bound result 261\n  Bound result 39\n  Bound result 97\n  Bound result 143\n\n[PASS] test_fragmentDrainRefillAndDelegatedSelfTransfer() (gas: 3109193)\nLogs:\n  Bound result 999999999999999999\n  Bound result 1\n  Bound result 1000000000000000001\n  Bound result 2000000000000000000\n  Bound result 2000000000000000000\n  Bound result 1000000000000000000\n  Bound result 0\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 13.45s (13.45s CPU time)\n\nRan 15 test suites in 13.45s (36.81s CPU time): 73 tests passed, 0 failed, 0 skipped (73 total tests)\n","passed":true},{"durationMs":93,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"MaintenancePot.redeem(uint256)\",\"MaintenancePot.unlockCallback(bytes)\",\"SwarmPeg.approve(address,uint256)\",\"SwarmPeg.transfer(address,uint256)\",\"SwarmPeg.transferFrom(address,address,uint256)\",\"SwarmPegHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"SwarmPegHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"SwarmPegHook.checkpointPulse()\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.sha256\":115,\"LICENSE\":23,\"README.md\":137,\"dependencies.json\":32,\"docs/SECURITY.md\":55,\"docs/VALIDATION.md\":32,\"docs/art/index.html\":19,\"docs/art/pulse-0.svg\":1,\"docs/art/pulse-1000.svg\":1,\"docs/art/pulse-500.svg\":1,\"foundry.toml\":27,\"launch.json\":27,\"rehearsal/Mainnet.t.sol\":55,\"remappings.txt\":4,\"script/PrepareLaunch.s.sol\":32,\"script/RenderPreview.s.sol\":14,\"src/HookFlags.sol\":29,\"src/MaintenancePot.sol\":57,\"src/SwarmGlyphSVG.sol\":128,\"src/SwarmPeg.sol\":142,\"src/SwarmPegHook.sol\":265,\"test/AtomicLaunch.t.sol\":174,\"test/Deployment.t.sol\":68,\"test/GlyphMetadata.t.sol\":191,\"test/GlyphModelInvariant.t.sol\":220,\"test/HookDifferential.t.sol\":172,\"test/HookFailurePaths.t.sol\":214,\"test/Invariants.t.sol\":176,\"test/ReserveSequenceInvariant.t.sol\":354,\"test/SwarmPeg.t.sol\":165,\"test/SwarmPegHook.t.sol\":400,\"test/TESTING.md\":28,\"test/TokenFailurePaths.t.sol\":120,\"test/helpers/Fixture.sol\":108,\"test/helpers/LaunchFactoryHarness.sol\":78,\"test/helpers/MockIMD.sol\":34,\"test/helpers/TestRouter.sol\":64,\"test/mocks/MockERC20.sol\":10,\"tools/check_artifacts.py\":37},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"60cd35cbbe0b93f1a54d6cb0ffee366beef089c4f369572c3707cb5a2c648040","verifiedTreeHash":"8a42c952605c5b835f7cf17ab930200710f09c5e","verifierVersion":"0.1.0+d952fceb"},{"checks":[{"durationMs":38107,"exitCode":0,"name":"build","output":"Compiling 99 files with Solc 0.8.26\nSolc 0.8.26 finished in 38.02s\nCompiler run successful!\n","passed":true},{"durationMs":5634,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 9 tests for test/SwarmPeg.t.sol:SwarmPegTest\n[PASS] testFuzz_transferConservesAndMirrors(uint256,uint256) (runs: 256, μ: 396259, ~: 410278)\nLogs:\n  Bound result 614308449155064413971815508\n  Bound result 451299635019467668725686801\n\n[PASS] test_bulkTransferBoundedGasAndEmptyHolderCannotReadGlyph() (gas: 362050)\n[PASS] test_failuresLeaveBalancesIntact() (gas: 77250)\n[PASS] test_fixedSupplySingleMintAndMetadata() (gas: 66747)\n[PASS] test_fractionalCrossingsRetireAndCreateOnlyWholeSlots() (gas: 1047440)\n[PASS] test_noOwnerMintPauseUpgradeOrIndependentGlyphTransfer() (gas: 174861)\n[PASS] test_transferAndAllowanceNoTax() (gas: 374673)\n[PASS] test_truncatingManyBatchesAndAppendingDoesNotResurrectOldIds() (gas: 10215864)\n[PASS] test_zeroSelfAndSubunitTransfersDoNotRerollExistingGlyphs() (gas: 509198)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 9.55ms (12.06ms CPU time)\n\nRan 3 tests for test/SwarmPegHook.t.sol:ReverseCurrencyOrderTest\n[PASS] testFuzz_feeAllModes(uint80,bool,bool) (runs: 256, μ: 614247, ~: 439015)\nLogs:\n  Bound result 3533\n\n[PASS] test_allFourSwapModes() (gas: 3126437)\n[PASS] test_dustRoundingHasNoMinimumTax() (gas: 1605864)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 27.32ms (25.85ms CPU time)\n\nRan 5 tests for test/SwarmPegHook.t.sol:InitializationTest\n[PASS] test_factoryDeploymentAndAllocation() (gas: 688408)\n[PASS] test_freshManagerTokenOnlyLiquidityBuyCanAccrueAndRedeem() (gas: 1502034)\n[PASS] test_noLiquiditySwapCannotProduceUnbackedClaims() (gas: 716527)\n[PASS] test_nonFactoryAndWrongPoolCannotInitialize() (gas: 237708)\n[PASS] test_swapsBeforeInitializationRefused() (gas: 23469)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 32.09ms (1.67ms CPU time)\n\nRan 5 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorDoesNotCallExternalManagerOrToken() (gas: 25346785)\n[PASS] test_constructorRefusesMissingOrPairTokenArguments() (gas: 8720)\n[PASS] test_prepareLaunchPredictsActualFactoryAddress() (gas: 52762026)\n[PASS] test_prepareRejectsInvalidSearch() (gas: 3274871)\n[PASS] test_wrongAddressFlagsRefused() (gas: 36935)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 63.05ms (44.71ms CPU time)\n\nRan 15 tests for test/SwarmPegHook.t.sol:SwarmPegHookTest\n[PASS] testFuzz_feeAllModes(uint80,bool,bool) (runs: 256, μ: 632299, ~: 446774)\nLogs:\n  Bound result 3474\n\n[PASS] test_allFourSwapModes() (gas: 3138582)\n[PASS] test_callbacksRejectUnauthorizedIncludingAfterSwap() (gas: 147956)\n[PASS] test_claimsCanBeRedeemedByAnyoneOnlyToPot() (gas: 1041611)\n[PASS] test_dustRoundingHasNoMinimumTax() (gas: 1612580)\n[PASS] test_factoryCollectsLPFeesAndRemovesWithoutHookTax() (gas: 2177817)\n[PASS] test_failedRedemptionPreservesClaimsAndReentrantRedemptionFails() (gas: 1171353)\n[PASS] test_foreignPoolAndInvalidAmountsRefused() (gas: 144340)\n[PASS] test_hookDataCannotAssignGlyphsToSpoofedHolder() (gas: 786744)\n[PASS] test_partialUnspecifiedIMDChargesOnlyExecutedVolume() (gas: 956346)\n[PASS] test_permissionsAddressSizeAndNoOwner() (gas: 100630)\n[PASS] test_priceLimitPartialSpecifiedIMDRevertsWithoutTax() (gas: 589835)\n[PASS] test_pulseDecayCheckpointDoesNotFreezeFractionalMinutes() (gas: 3476545)\n[PASS] test_pulseSaturatesAndGlyphsAreUniqueAnimatedAndOnChain() (gas: 37541304)\n[PASS] test_routerSlippageRollsBackPoolFeesPulseAndGlyphs() (gas: 611478)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 63.10ms (58.59ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:AccountingInvariantTest\n[PASS] invariant_feesConservedAndManagerSettled() (runs: 64, calls: 4096, reverts: 0)\n\n╭-------------+----------+-------+---------+----------╮\n| Contract    | Selector | Calls | Reverts | Discards |\n+=====================================================+\n| SwapHandler | advance  | 1370  | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| SwapHandler | redeem   | 1443  | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| SwapHandler | trade    | 1283  | 0       | 0        |\n╰-------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 9999999999999999247\n  Bound result 11082\n  Bound result 768936388214756\n  Bound result 0\n  Bound result 2807\n  Bound result 13491\n  Bound result 81332724126360531\n  Bound result 9999999999999999299\n  Bound result 18\n  Bound result 3861\n  Bound result 2417\n  Bound result 2899\n  Bound result 92609963087448152\n  Bound result 29232750597079314\n  Bound result 2427\n  Bound result 2330\n  Bound result 6316\n  Bound result 369589\n  Bound result 5\n  Bound result 1738\n  Bound result 9999999999999999679\n  Bound result 2107\n  Bound result 3117\n  Bound result 1001\n  Bound result 8010\n  Bound result 49\n  Bound result 1448\n  Bound result 1979\n  Bound result 2741\n  Bound result 8653\n  Bound result 1135\n  Bound result 200\n  Bound result 0\n  Bound result 10051\n  Bound result 1\n  Bound result 2\n  Bound result 3000\n  Bound result 321552982790986535\n  Bound result 132\n  Bound result 17\n  Bound result 4528\n  Bound result 39952502245\n  Bound result 16\n  Bound result 27567171111605415\n  Bound result 40\n  Bound result 31739415855994019\n  Bound result 10\n  Bound result 205\n  Bound result 9855\n  Bound result 137\n  Bound result 9999999999999999028\n  Bound result 265\n  Bound result 411\n  Bound result 81638205611488378\n  Bound result 90143901562599809\n  Bound result 158\n  Bound result 9999999999999999614\n  Bound result 1808\n  Bound result 8736\n  Bound result 217450784012324764\n  Bound result 1000\n  Bound result 6862\n  Bound result 50\n  Bound result 49\n  Bound result 555\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 791.31ms (749.91ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:GlyphInvariantTest\n[PASS] invariant_supplyAndWholeBalanceOwnership() (runs: 64, calls: 4096, reverts: 0)\n\n╭----------------+----------+-------+---------+----------╮\n| Contract       | Selector | Calls | Reverts | Discards |\n+========================================================+\n| BalanceHandler | move     | 4096  | 0       | 0        |\n╰----------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 1210\n  Bound result 2\n  Bound result 4294967295\n  Bound result 257\n  Bound result 2652\n  Bound result 3900\n  Bound result 5506757738487822030\n  Bound result 38\n  Bound result 1000000000000000000\n  Bound result 4800\n  Bound result 255\n  Bound result 507191715817682180\n  Bound result 9253510434373454544\n  Bound result 4264\n  Bound result 678009306304\n  Bound result 4928082841823232780\n  Bound result 4096\n  Bound result 4288775753\n  Bound result 3071\n  Bound result 3\n  Bound result 16383\n  Bound result 2745\n  Bound result 4038985155155632947\n  Bound result 3411924343547635671\n  Bound result 406\n  Bound result 120\n  Bound result 2500000000000000000\n  Bound result 638212884\n  Bound result 2025938391\n  Bound result 45\n  Bound result 14595128970684188374\n  Bound result 128\n  Bound result 2787\n  Bound result 8323662866288470577\n  Bound result 40\n  Bound result 3895326027777963083\n  Bound result 3499575165300092516\n  Bound result 1301658533172299398\n  Bound result 8396\n  Bound result 14\n  Bound result 45\n  Bound result 16384\n  Bound result 3854\n  Bound result 4416\n  Bound result 6800834137\n  Bound result 217\n  Bound result 265\n  Bound result 2140\n  Bound result 10000000673714332145\n  Bound result 1\n  Bound result 160\n  Bound result 12\n  Bound result 800000000000000000\n  Bound result 13826633361629243966\n  Bound result 1901\n  Bound result 200000000000000000\n  Bound result 1\n  Bound result 14\n  Bound result 4\n  Bound result 128\n  Bound result 13723933308841263214\n  Bound result 611\n  Bound result 25\n  Bound result 313\n  Bound result 1266\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.52s (5.52s CPU time)\n\nRan 7 test suites in 5.52s (6.51s CPU time): 39 tests passed, 0 failed, 0 skipped (39 total tests)\n","passed":true},{"durationMs":81,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"MaintenancePot.redeem(uint256)\",\"MaintenancePot.unlockCallback(bytes)\",\"SwarmPeg.approve(address,uint256)\",\"SwarmPeg.transfer(address,uint256)\",\"SwarmPeg.transferFrom(address,address,uint256)\",\"SwarmPegHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"SwarmPegHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"SwarmPegHook.checkpointPulse()\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.sha256\":115,\"LICENSE\":23,\"README.md\":137,\"dependencies.json\":32,\"docs/SECURITY.md\":55,\"docs/VALIDATION.md\":32,\"docs/art/index.html\":19,\"docs/art/pulse-0.svg\":1,\"docs/art/pulse-1000.svg\":1,\"docs/art/pulse-500.svg\":1,\"foundry.toml\":27,\"launch.json\":27,\"rehearsal/Mainnet.t.sol\":55,\"remappings.txt\":4,\"script/PrepareLaunch.s.sol\":32,\"script/RenderPreview.s.sol\":14,\"src/HookFlags.sol\":29,\"src/MaintenancePot.sol\":57,\"src/SwarmGlyphSVG.sol\":128,\"src/SwarmPeg.sol\":142,\"src/SwarmPegHook.sol\":265,\"test/Deployment.t.sol\":68,\"test/Invariants.t.sol\":176,\"test/SwarmPeg.t.sol\":165,\"test/SwarmPegHook.t.sol\":400,\"test/helpers/Fixture.sol\":108,\"test/helpers/LaunchFactoryHarness.sol\":78,\"test/helpers/MockIMD.sol\":34,\"test/helpers/TestRouter.sol\":64,\"test/mocks/MockERC20.sol\":10,\"tools/check_artifacts.py\":37},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":3392,"exitCode":0,"name":"slither","output":"[medium/medium] divide-before-multiply at src/SwarmPegHook.sol:189: SwarmPegHook.checkpointPulse() (src/SwarmPegHook.sol#189-195) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/SwarmPegHook.sol:121: SwarmPegHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/SwarmPegHook.sol#121-181) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/SwarmPeg.sol:84: SwarmPeg._update(address,address,uint256) (src/SwarmPeg.sol#84-129) uses a dangerous strict equality:\n[medium/medium] uninitialized-local at src/SwarmPegHook.sol:113: SwarmPegHook.beforeSwap(address,PoolKey,SwapParams,bytes).fee (src/SwarmPegHook.sol#113) is a local variable never initialized\n[low/medium] reentrancy-benign at src/MaintenancePot.sol:39: Reentrancy in MaintenancePot.redeem(uint256) (src/MaintenancePot.sol#39-46):\n[low/medium] reentrancy-events at src/SwarmPegHook.sol:121: Reentrancy in SwarmPegHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/SwarmPegHook.sol#121-181):\n[low/medium] timestamp at src/SwarmPegHook.sol:183: SwarmPegHook.currentPulse() (src/SwarmPegHook.sol#183-186) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmPegHook.sol:121: SwarmPegHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/SwarmPegHook.sol#121-181) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmPeg.sol:60: SwarmPeg.glyphData(uint256) (src/SwarmPeg.sol#60-74) uses timestamp for comparisons","passed":true},{"durationMs":592,"exitCode":0,"name":"aderyn","output":"[high] abi-encode-packed-hash-collision at src/SwarmPegHook.sol:210: `abi.encodePacked()` Hash Collision\n[high] reentrancy-state-change at src/MaintenancePot.sol:44: Reentrancy: State change after external call\n[high] weak-randomness at src/SwarmPeg.sol:108: Weak Randomness (4 places)\n[low] internal-function-used-once at src/HookFlags.sol:22: Internal Function Used Only Once (2 places)\n[low] large-numeric-literal at src/SwarmPeg.sol:10: Large Numeric Literal\n[low] literal-instead-of-constant at src/SwarmGlyphSVG.sol:11: Literal Instead of Constant (21 places)\n[low] state-change-without-event at src/MaintenancePot.sol:48: State Change Without Event\n[low] uninitialized-local-variable at src/SwarmGlyphSVG.sol:47: Uninitialized Local Variable (3 places)\n[low] unused-public-function at src/SwarmPeg.sol:56: Public Function Not Used Internally","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"8be4b33f3aa146d65f9ddbac37ef21e03fceedc5e1d7ea5c8c9e6d6c5b7cee06","verifiedTreeHash":"1ad78d0aae61bdf371b56558d2959a5e9deb7792","verifierVersion":"0.1.0+d952fceb"}]}