{"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":"b2372a3b-14e1-42cf-a91f-4a753c28d5cb","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"20272c56fb445c5b48d6bd4f3aa93b18fb54c4e28da6bb339da5eff2392ed070","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":"834d1bba335d2c9a48379f8c7150b46c30d821837f9895ca9881875637be3ad4","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":null,"dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","state":"waiting"},{"acceptedSubmissionHash":null,"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":"ready"},{"acceptedSubmissionHash":"562a6e4cffa8acd706c6379a81026e621f1c298c02b56afe71a5c3cbc503ea03","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":"7a22fe0868b2be9631d6da9d9fc922f2214d35d2a437d4f5955c28345f6de91a","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"6b8d2c2ebd3301cff2be8edb1e1cc6ba65c858c02fe7247be81722b969842265","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"6b8d2c2ebd3301cff2be8edb1e1cc6ba65c858c02fe7247be81722b969842265","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":null,"dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"failed"},{"acceptedSubmissionHash":"45b1f8398202f564b92d438052086f34a757029e597e7835a64cd3c236ac731b","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 kind univ4_hook on Ethereum mainnet, paired with IMD.\n\nToken name: SwarmPeg\nSymbol: SWARMPEG\nSupply: 1_000_000_000e18, fixed single mint.\nAllocation: 88% seeds the pool, 2% to requester, 10% to swarm (standard).\nNo owner, no mint, no pause, no upgrade after construction.\n\nHook: SwarmPegHook\n- afterSwap (and return deltas for fee accounting as needed)\n- Unipeg-style integer balance model: whole tokens own unique fully on-chain animated SVG glyphs\n- Seed generated from trade data + block + previous holder + swarm pulse value\n- Glyphs: abstract node-network style with CryptoPunk-inspired pixel agents (small generative faces/figures inside or as part of a node graph)\n- Fully on-chain animated SVG (CSS keyframes or SMIL). Animations include:\n  • pulsing central nodes or agent features\n  • connection lines that fade or draw\n  • subtle color breathing\n  • status blinks whose speed is influenced by the swarm pulse\n- Higher pulse → denser networks, brighter palettes, faster animations\n- On-chain SVG renderer only (no IPFS, no external assets)\n- Keep the renderer gas-efficient (layered pixel rules + simple keyframes)\n\nFee: 2% on every buy and sell taken in the paired asset, majority routed to a maintenance pot that can fund swarm jobs (pulse updates, renderer improvements, new generative rules).\nNo owner, no upgrades, no pause.\n\nAgents who ship accepted work receive the standard 10% allocation.\nInclude clean events/views for glyphs and pulse.\nOptional: minimal website also built by the swarm.\n\nToken name: SwarmPeg\nToken symbol: SWARMPEG","parentJobId":null,"planHash":"e730fe5eae906f0373c9e87d952dc8d0b1e08566f2414d3df39d77bccd758c25","previousHash":"7da5ccad634ce384479d2254c81e4f298f286bd414dd6ce33ccf1c1ce42461f6","projectId":"b2372a3b-14e1-42cf-a91f-4a753c28d5cb","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1849-swarmpeg"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"52189","feedbackHash":"15760435442f2d4bebdcf6b5a8fe77b1f1d4c3b2e611aee7c81369bca2fc4da2","nodeKey":"audit_economics","submissionHash":"20272c56fb445c5b48d6bd4f3aa93b18fb54c4e28da6bb339da5eff2392ed070","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52397","feedbackHash":"b0f1d5293c2314cf4a3f8cd44c5b3ca701f4d4d7851c337b514add237d088a21","nodeKey":"audit_flow","submissionHash":"834d1bba335d2c9a48379f8c7150b46c30d821837f9895ca9881875637be3ad4","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51141","feedbackHash":"a4cc33d671fa4a42554e10d808ea30c04a5a24f1ce0ca25559c439d44ff12acf","nodeKey":"audit_permissions","submissionHash":"562a6e4cffa8acd706c6379a81026e621f1c298c02b56afe71a5c3cbc503ea03","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51103","feedbackHash":"0da5531950cb38d214d1b24559e0a469994847cf71b002b5bf1c48147478afaf","nodeKey":"write_foundry_tests","submissionHash":"45b1f8398202f564b92d438052086f34a757029e597e7835a64cd3c236ac731b","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"bd6597006cc13a9491f2abec38165d6367147f864fc7d38c439e7d741f787cfb","state":"blocked","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"fe5e46fb044c6af1","findings":[{"citation":"resolved","description":"Economic Security: the pot receives 100% of the 2% hook fee and grows without bound, but the only gate on paying it out is `passed(id)` (line 146): yes >= QUORUM (10,000,000 SWARMPEG, 1% of supply) and yes > no, after 7 days. The quorum is fixed in tokens, not tied to the amount requested or to the pot's size. At the launch policy's 2,500-IMD whole-supply valuation, 10M SWARMPEG costs about 25 IMD to buy from the pool (plus ~3.25% LP+hook fee each way and price impact, roughly 2-3 IMD round trip), and every voting token is refundable through `withdrawVote` as soon as voting ends, before execution. So the cost to take the entire pot is a fixed ~2-3 IMD plus gas, while the prize is everything the pot has ever accrued; the attack is profitable as soon as the pot exceeds a few IMD (about 150 IMD of traded volume). The one-vote-per-address rule does not raise the cost: the 1M proposal stake and the 9M vote simply come from two keys of the same actor, as the proof shows. Defence requires other holders to lock more than the attacker's tokens as `no` within 7 days on every attempt, and the attacker can re-propose immediately after each failure (only one job at a time, but closing and proposing can be bundled in one transaction). Note also that the requester's 2% allocation (20M) and the contributors' 10% (100M) each exceed the quorum on their own, so either can pay out the pot unilaterally; the README calls threshold capture a disclosed governance risk, but at these numbers the pot's funds are effectively claimable by anyone, which contradicts the brief's purpose of a pot that funds swarm jobs. Minimal fix that keeps the ownerless design: make the threshold scale with the request (e.g. yes votes must be at least a share of circulating supply proportional to amount/managedAssets, or cap a single job at a small fraction of managedAssets per period), and/or require a supermajority of votes cast with a minimum turnout that excludes the proposer's own stake.","line":21,"path":"src/SwarmMaintenance.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {SwarmPeg} from \"src/SwarmPeg.sol\";\nimport {SwarmMaintenance} from \"src/SwarmMaintenance.sol\";\nimport {LaunchConstants} from \"src/LaunchConstants.sol\";\n\ncontract StandInIMD is ERC20 {\n    constructor() ERC20(\"IdentityMD\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @notice The maintenance pot's quorum is an absolute 1% of SWARMPEG supply while the pot's IMD grows\n/// without bound. One holder of 10,000,000 SWARMPEG (about 25 IMD at the 2,500-IMD opening valuation)\n/// can pay the entire pot to itself with nobody else's consent and get every voting token back.\ncontract PotCaptureTest is Test {\n    PoolManager manager;\n    SwarmPeg token;\n    StandInIMD imd;\n    SwarmMaintenance pot;\n    address attacker = makeAddr(\"attacker\");\n\n    function setUp() public {\n        manager = new PoolManager(address(this));\n        token = new SwarmPeg();\n        // The real IMD address is a constant in the pot; put a plain ERC-20 there, as the project's fixture does.\n        vm.etch(LaunchConstants.IMD, address(new StandInIMD()).code);\n        imd = StandInIMD(LaunchConstants.IMD);\n        pot = new SwarmMaintenance(IPoolManager(address(manager)), IERC20(address(token)));\n        // Accumulated hook fees: 1,000 IMD sitting in the pot (managedAssets counts liquid IMD and claims alike).\n        imd.mint(address(pot), 1_000 ether);\n        // Attacker holds exactly the quorum: 1% of the fixed supply.\n        token.transfer(attacker, 10_000_000 ether);\n    }\n\n    function test_oneQuorumHolderDrainsTheWholePotAtFixedCost() public {\n        address second = makeAddr(\"attacker-second-address\");\n        vm.startPrank(attacker);\n        token.approve(address(pot), type(uint256).max);\n        // The proposal stake (1,000,000) is the proposer's one vote; the other 9,000,000 vote from a second key.\n        token.transfer(second, 9_000_000 ether);\n        uint256 id = pot.proposePayment(attacker, pot.managedAssets(), keccak256(\"nothing\"));\n        vm.stopPrank();\n        vm.startPrank(second);\n        token.approve(address(pot), type(uint256).max);\n        pot.vote(id, true, 9_000_000 ether);\n        vm.warp(block.timestamp + 7 days);\n        pot.withdrawVote(id);\n        token.transfer(attacker, 9_000_000 ether);\n        vm.stopPrank();\n        vm.startPrank(attacker);\n        pot.withdrawVote(id);\n        vm.warp(block.timestamp + 2 days);\n        pot.executePayment(id);\n        vm.stopPrank();\n\n        // Expected: a payout of the whole pot cannot be authorised by a single 1%-of-supply holder.\n        // Actual on this code: 1,000 IMD paid out, all 10,000,000 voting tokens returned.\n        assertEq(imd.balanceOf(attacker), 0, \"attacker took the whole pot with a 1%-of-supply vote\");\n        assertEq(token.balanceOf(attacker), 10_000_000 ether);\n    }\n}","reproduction":"State: pot holds 1,000 IMD (fees redeemed or still as claims, `managedAssets()` counts both); attacker holds 10,000,000 SWARMPEG across two addresses A (1M) and B (9M). Calls: A: token.approve(pot); A: proposePayment(A, pot.managedAssets(), keccak256(\"nothing\")) -> yes = 1,000,000. B: token.approve(pot); B: vote(id, true, 9_000_000e18) -> yes = 10,000,000 = QUORUM. warp +7 days. A and B: withdrawVote(id) -> all 10M tokens back. warp +2 days. anyone: executePayment(id). Expected: a single 1%-of-supply holder cannot authorise paying out the whole pot. Actual: 1,000 IMD is transferred to A, totalPaid = 1,000e18, and A again holds all 10,000,000 SWARMPEG. Run `forge test --match-path test/scratch/PotCapture.t.sol` (fails on this code).","severity":"medium","snippet":"    uint256 public constant QUORUM = 10_000_000 ether; // 1% of fixed supply","title":"Maintenance pot quorum is an absolute 1% of supply, so any holder of 10M SWARMPEG (about 25 IMD at the opening valuation) can pay the whole pot to itself at near-zero cost"},{"citation":"resolved","description":"Flow Gap (execution x first principles): every fee is minted as an ERC-6909 IMD claim to `maintenancePot` (line 155, `poolManager.mint(maintenancePot, uint256(uint160(PAIRED_ASSET)), fee)`), and only `SwarmMaintenance.unlockCallback` on that same manager, driven by a vote of `votingToken` holders, can ever turn the claims into IMD. The constructor accepts any nonzero `maintenancePot_` without reading `SwarmMaintenance(pot).votingToken()` or `.poolManager()`. The launch flow makes a wiring mistake likely rather than hypothetical: the factory creates the launch token (`$token`) inside the launch transaction, the hook's `maintenancePot_` can only be written in launch.json as a literal address of an already-deployed contract, and that pot's constructor needs the launch token's address, which must therefore be predicted off-chain before the token exists (README: 'deploy SwarmPeg ... then SwarmMaintenance(manager, token), then SwarmPegHook'). If the prediction is off by one nonce, or the pot is built against another chain's manager, the hook still deploys, the pool still opens, trading works, and 100% of fees are minted to a pot whose voters hold a token nobody owns (or whose claims live on a different manager and whose `managedAssets()` is 0 so `proposePayment` always reverts with InvalidJob). There is no owner, rescue or re-pointing path, so all fees are permanently lost; the README states 'bad deployment wiring cannot be repaired by an owner' but relies on an off-chain `validateDeployedDependencies` call that nothing in the launch enforces. Minimal fix: in the constructor require `SwarmMaintenance(maintenancePot_).votingToken() == token_` and `.poolManager() == manager_` (the token probe exists before the hook is built in the verifier's floor tests, and the pot must exist to be named). Better: let the hook deploy the pot itself (`maintenancePot = address(new SwarmMaintenance(manager_, token_))`) so launch.json carries only `$poolManager`, `$token`, `$factory` and no hand-written address at all.","line":68,"path":"src/SwarmPegHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {SwarmPeg} from \"src/SwarmPeg.sol\";\nimport {SwarmPegHook} from \"src/SwarmPegHook.sol\";\nimport {SwarmMaintenance} from \"src/SwarmMaintenance.sol\";\nimport {HookFlags} from \"src/HookFlags.sol\";\nimport {LaunchConstants} from \"src/LaunchConstants.sol\";\n\ncontract StandInIMD is ERC20 {\n    constructor() ERC20(\"IdentityMD\", \"IMD\") {}\n}\n\ncontract OtherToken is ERC20 {\n    constructor() ERC20(\"Other\", \"OTHER\") {}\n}\n\n/// @notice 100% of hook fees are minted as claims to `maintenancePot`, yet the hook never checks that the\n/// pot is wired to the same manager and launch token. A pot built against a different token (the token the\n/// factory creates at launch does not exist when the pot must be deployed) or a different manager is\n/// accepted, and every fee claim then goes to a contract whose governance can never release them.\ncontract PotWiringTest is Test {\n    function _construct(address target, bytes memory creationCode) internal returns (bool ok) {\n        vm.etch(target, creationCode);\n        bytes memory runtime;\n        (ok, runtime) = target.call(\"\");\n        if (ok) vm.etch(target, runtime);\n    }\n\n    function test_hookRefusesAPotWiredToAnotherTokenOrManager() public {\n        PoolManager manager = new PoolManager(address(this));\n        PoolManager otherManager = new PoolManager(address(this));\n        vm.etch(LaunchConstants.IMD, address(new StandInIMD()).code);\n        SwarmPeg token = new SwarmPeg();\n        OtherToken other = new OtherToken();\n\n        // Pot deployed earlier against a wrongly predicted launch token.\n        SwarmMaintenance wrongTokenPot = new SwarmMaintenance(IPoolManager(address(manager)), IERC20(address(other)));\n        // Pot deployed against another chain's/another manager.\n        SwarmMaintenance wrongManagerPot = new SwarmMaintenance(IPoolManager(address(otherManager)), IERC20(address(token)));\n\n        address hookAddress = address(uint160(0x10000000) | HookFlags.SWARMPEG);\n        bool built = _construct(\n            hookAddress,\n            abi.encodePacked(\n                type(SwarmPegHook).creationCode, abi.encode(manager, token, address(wrongTokenPot), address(this))\n            )\n        );\n        // Expected: the constructor rejects a pot whose votingToken is not the launch token.\n        // Actual on this code: it is accepted, and every fee claim will be minted to it.\n        assertFalse(built, \"hook accepted a pot governed by a different token\");\n\n        built = _construct(\n            hookAddress,\n            abi.encodePacked(\n                type(SwarmPegHook).creationCode, abi.encode(manager, token, address(wrongManagerPot), address(this))\n            )\n        );\n        // Expected: the constructor rejects a pot bound to a different PoolManager (its claims live elsewhere).\n        assertFalse(built, \"hook accepted a pot bound to a different PoolManager\");\n    }\n}","reproduction":"State: manager M, launch token T, a second token X, a second manager M2. Deploy potA = new SwarmMaintenance(M, X) and potB = new SwarmMaintenance(M2, T). Construct SwarmPegHook(M, T, potA, factory) at an address carrying flags 0x20cc. Expected: the constructor reverts because potA's votingToken is X, not T (and likewise for potB because its manager is M2). Actual: both constructions succeed; after launch every swap mints IMD claims to potA, where `proposePayment` needs X tokens that have no relation to the launch, so the fees are never spendable. Run `forge test --match-path test/scratch/PotWiring.t.sol` (fails on this code).","severity":"medium","snippet":"        maintenancePot = maintenancePot_;","title":"Hook never checks that maintenancePot is wired to its own token and PoolManager, so a mis-deployed pot silently receives 100% of fee claims that can never be released"},{"citation":"resolved","description":"Economic Security (cheapest griefing vector): only one job may be active and a proposal needs just PROPOSAL_STAKE = 1,000,000 SWARMPEG (0.1% of supply, about 2.5 IMD at the opening valuation), fully refundable via `withdrawVote` once voting ends. A griefer proposes a payment of 1 wei to themselves; for 7 days nobody else can propose. At `voteEnd` the job has not passed (yes = 1M < QUORUM), so in one transaction the griefer calls `closeJob(id)` and then `proposePayment(...)` again, re-locking the pot for another 7 days; the stake from the previous job is withdrawn in the same transaction. Legitimate proposers can only win a block-level race at each 7-day boundary. The README attributes this to 'wealthy holders', but the stake is 2.5 IMD and is never at risk. Minimal fix: forfeit (to the pot) the stake of a proposal that fails to reach quorum, or allow several concurrent proposals bounded by the sum of their amounts against managedAssets().","line":102,"path":"src/SwarmMaintenance.sol","reproduction":"State: griefer G holds 1,000,000 SWARMPEG and has approved the pot. Calls: G: proposePayment(G, 1, keccak256(\"x\")) at t0. Any other holder H: proposePayment(worker, amount, hash) at t0+1 .. t0+7d -> reverts JobActive. At t0+7d, G in one tx: closeJob(1); withdrawVote(1); proposePayment(G, 1, keccak256(\"y\")) -> job 2 active until t0+14d. Repeat indefinitely. Expected: a legitimate job can be proposed within a bounded time; actual: H's proposePayment reverts with JobActive at every attempt except in the exact block of each 7-day boundary, where it competes with G's bundle; G's cost is gas only.","severity":"low","snippet":"        if (activeJob != 0) revert JobActive();","title":"Any holder of 1M SWARMPEG (about 2.5 IMD) can keep the pot permanently occupied by bundling closeJob with a new proposePayment, at gas cost only"}],"hash":"20272c56fb445c5b48d6bd4f3aa93b18fb54c4e28da6bb339da5eff2392ed070","nodeId":"a5ac2497-61a0-4a7c-b722-bb43d7858b22","outcome":"completed","summary":"The review is complete. The findings file at `.imd-findings.json` holds three substantiated findings and a coverage row for all 13 listed entry points plus three checked invariants, with none marked unreached.\n\n**What I found**\n\n- **Medium, pot capture.** The maintenance pot's quorum is a fixed 1% of supply (10M SWARMPEG), which costs about 25 IMD to buy at the opening valuation and is fully refundable after voting. Anyone can propose paying the whole pot to themselves, vote the quorum from two of their own keys, withdraw every token after 7 days, and execute two days later. The requester's 2% allocation alone also clears the quorum. Scratch test `test/scratch/PotCapture.t.sol` fails on the current code and is attached as proof.\n- **Medium, unchecked pot wiring.** The hook mints 100% of fee claims to `maintenancePot` but never verifies that the pot's voting token and PoolManager are its own. The launch flow forces the pot to be deployed before the factory creates the token, from a predicted address, so a wrong pot silently strands all fees forever. Scratch test `test/scratch/PotWiring.t.sol` fails on the current code and is attached as proof. The suggested fix is a constructor check, or having the hook deploy the pot itself.\n- **Low, pot occupancy griefing.** A refundable 0.1%-of-supply stake lets one actor bundle closeJob and a new proposePayment every 7 days, blocking all other proposals at gas cost only.\n\n**What holds**\n\nThe hook's fee accounting traces correctly on all four swap paths against v4's delta math, the mint-before-credit ordering nets the hook's transient delta to zero, and the one-sided launch pool needs no IMD in the manager. I pulled the mainnet IMD source from Sourcify and Blockscout: it is a LayerZero OFT over an unmodified OpenZeppelin ERC-20, with no transfer fee, pause, or blacklist, so the pot's settlement and payout assumptions are sound. The token's glyph batch invariants and the pot's vote escrow, redemption, and callback gating all behave as intended.\n\nNothing under `src/`, `lib/`, or configuration was changed. The only files written are the findings JSON and the two scratch tests under `test/scratch/`.","treeHash":null,"usage":{"cachedInputTokens":1805016,"inputTokens":482,"model":"claude-fable-5-1","outputTokens":41091,"runtime":"claude","turns":40,"wallClockMs":770642}},{"artifacts":[],"attempt":1,"bundleHash":"444446606c0c8731816f508bbf3eaca9d2e04ec518d0579fcda948ed8b02dae6","device":"fd4958ee1ca0d1f2","findings":[],"hash":"45b1f8398202f564b92d438052086f34a757029e597e7835a64cd3c236ac731b","nodeId":"8bdc7f08-cfff-4859-bf3a-d47986fc3cda","outcome":"completed","summary":"Added 35 tests under `test/adversarial/`, covering failure paths, fee accounting, governance boundaries, glyph metadata, and two stateful invariant suites totaling 20,480 randomized calls.\n\n`forge build` and offline `forge test` passed with the required seed: **86 passed, 0 failed**.\n\nOnly `test/` changed. No confirmed implementation defects found.","treeHash":"ae69ad59383b50b5ead30799dabd804128eb80e0","usage":{"cachedInputTokens":3656320,"inputTokens":147951,"model":"gpt-6-astra","outputTokens":31267,"runtime":"codex","turns":8,"wallClockMs":1155284}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"7d454e6cee88c390","findings":[{"citation":"resolved","description":"SwarmPegHook's constructor (src/SwarmPegHook.sol:61) takes `address maintenancePot_` and stores it with no check beyond nonzero: it never verifies that the address has code, is a SwarmMaintenance, or is bound to the same PoolManager and token (`SwarmMaintenance.poolManager()`, `votingToken()`). README.md line 31 states this explicitly: 'Constructors make no calls to these dependencies and do not require deployed code.' Every swap fee is then minted as an ERC-6909 claim to that address (src/SwarmPegHook.sol:155 `poolManager.mint(maintenancePot, uint256(uint160(PAIRED_ASSET)), fee)`). A claim can only be burned by its holder (PoolManager.burn requires msg.sender == from or an operator/allowance the holder set) inside an unlock callback, so a codeless or mis-wired recipient can never redeem, and 100% of the hook fee is unrecoverable with no owner or rescue path. This is a trust gap at the deployment seam, not a hypothetical: the launch flow cannot supply a correct value. The manifest resolves only $poolManager, $token, $owner and $factory; the pot has no placeholder and must be a literal (a stand-in is refused). SwarmMaintenance's constructor needs the launch token address, but $token is deployed by the factory in the same transaction that deploys the hook, so no correctly wired pot can exist when launch.json is written. docs/OPERATIONS.md step 3 asks 'the network' to deploy the pot between the token and the hook, which the factory does not do. Whatever literal is written, the hook constructor will accept it. Fix: remove the argument and have the hook deploy its own pot in the constructor (`maintenancePot = address(new SwarmMaintenance(manager_, token_));`), so constructorArgs become [$poolManager, $token, $factory] and wiring is enforced by construction; or, at minimum, require `maintenancePot_.code.length != 0 && SwarmMaintenance(maintenancePot_).poolManager() == manager_ && SwarmMaintenance(maintenancePot_).votingToken() == token_` in the constructor. The maintenance pot's own constructor has the mirror gap (src/SwarmMaintenance.sol:74) and would be covered by the same change.","line":68,"path":"src/SwarmPegHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {SwarmPeg} from \"src/SwarmPeg.sol\";\nimport {SwarmPegHook} from \"src/SwarmPegHook.sol\";\nimport {SwarmMaintenance} from \"src/SwarmMaintenance.sol\";\nimport {HookFlags} from \"src/HookFlags.sol\";\n\n/// @notice The hook's fee recipient is the only address in its constructor that the launch flow\n/// cannot resolve ($poolManager/$token/$factory are placeholders; the pot is a hand-written\n/// literal of a contract that must already know the launch token). The constructor accepts any\n/// nonzero value without checking that it is a SwarmMaintenance wired to this manager and token.\n/// Fee claims are ERC-6909 balances inside the PoolManager that only their holder can burn, so\n/// every fee minted to a wrong address is lost for good. This test fails on the current code\n/// (construction succeeds with a codeless pot) and passes once the constructor either refuses an\n/// unwired pot or deploys its own.\ncontract PotWiringProofTest is Test {\n    PoolManager manager;\n    SwarmPeg token;\n    address factory = address(0xFAC7);\n\n    function setUp() public {\n        manager = new PoolManager(address(this));\n        token = new SwarmPeg();\n    }\n\n    function test_hookRefusesOrReplacesAnUnwiredMaintenancePot() public {\n        address bogusPot = address(0xBEEF); // no code: an EOA, a typo, or a pot from another launch\n        address at = address(uint160(0x40000000) | HookFlags.SWARMPEG);\n        vm.etch(at, abi.encodePacked(type(SwarmPegHook).creationCode, abi.encode(manager, token, bogusPot, factory)));\n        (bool constructed, bytes memory runtime) = at.call(\"\");\n        if (!constructed) return; // refusing the unwired pot is the fix\n        vm.etch(at, runtime);\n        SwarmPegHook hook = SwarmPegHook(at);\n        address pot = hook.maintenancePot();\n        assertGt(pot.code.length, 0, \"fee recipient has no code: claims minted to it can never be redeemed\");\n        assertEq(address(SwarmMaintenance(pot).poolManager()), address(manager), \"pot bound to another manager\");\n        assertEq(address(SwarmMaintenance(pot).votingToken()), address(token), \"pot bound to another token\");\n    }\n}","reproduction":"1. manager = new PoolManager(this); token = new SwarmPeg(); 2. etch `abi.encodePacked(type(SwarmPegHook).creationCode, abi.encode(manager, token, address(0xBEEF), factory))` at an address carrying flags 0x20cc and run the constructor. Expected: revert (0xBEEF has no code and is not a pot bound to this manager and token). Actual: construction succeeds and `hook.maintenancePot() == 0xBEEF`. 3. With the pool initialized and seeded, a 100 IMD exact-input buy mints a 2 IMD claim: `manager.balanceOf(0xBEEF, uint160(IMD)) == 2e18`. No call sequence from any account can move that claim: `PoolManager.burn(0xBEEF, id, 2e18)` from anyone else reverts for lack of allowance, and 0xBEEF cannot execute `unlock`. The fee is permanently stranded. The attached test fails on the current tree and passes once the constructor refuses an unwired pot or deploys its own.","severity":"high","snippet":"        maintenancePot = maintenancePot_;","title":"Hook fee recipient is an unverifiable hand-written constructor address; fee claims minted to a wrong pot are lost forever"},{"citation":"resolved","description":"Trust gap across access, economics and asymmetry. Access: `proposePayment` is open to anyone who can escrow PROPOSAL_STAKE (1,000,000 SWARMPEG, 0.1% of supply, src/SwarmMaintenance.sol:20), and `closeJob` is open to anyone once `voteEnd` passes. Economics: the stake is fully refundable through `withdrawVote` the moment `voteEnd` is reached, whether the job passed or not, so a junk proposal costs only gas and a 7-day lock of about 2.5 IMD worth of tokens at the 2,500 IMD opening cap. Asymmetry: only one job may exist (`activeJob != 0` reverts every other proposer for the full VOTING_PERIOD), and the slot is handed to whoever calls `proposePayment` first after a close. A griefer bundles `closeJob(old) + withdrawVote(old) + proposePayment(self, 1, x)` in one transaction at the first eligible second, every 7 days, forever; honest proposals (renderer work, keeper pay) can never be opened and the entire fee stream is frozen in the pot. If the griefer also reaches quorum (see finding 3) and names a recipient that cannot receive IMD, `closeJob` is additionally blocked during the 2-day delay plus 14-day window (src/SwarmMaintenance.sol:169), stretching each cycle to 23 days. README.md line 106 discloses this as a cost 'wealthy holders' bear, but the actual requirement is 0.1% of supply with full refund. Fix options that keep the ownerless design: allow several concurrent proposals with the sum of open amounts bounded by managedAssets(); or forfeit (burn or keep in the pot) the stake of a proposal that fails to reach quorum; or let a proposal with more yes votes supersede the active one.","line":102,"path":"src/SwarmMaintenance.sol","reproduction":"State: pool seeded, 1,000 IMD of fees accrued (managedAssets() >= 1000e18); griefer holds exactly 1,000,000 SWARMPEG; honest holder holds 50,000,000 SWARMPEG. 1. griefer: proposePayment(griefer, 1, keccak256('junk')) -> id 1. 2. honest: proposePayment(worker, 100e18, hash) -> reverts JobActive (expected: a legitimate proposal can be opened). 3. warp to voteEnd; griefer in one tx: closeJob(1); withdrawVote(1); proposePayment(griefer, 1, keccak256('junk')) -> id 2, succeeds. 4. Repeat steps 2-3 for 10 cycles: every honest proposePayment reverts JobActive, totalPaid stays 0, griefer's wallet balance is 0 outside the stake at all times (only 1M tokens ever committed). Verified in a scratch test on this tree (test_lockPotForeverWithOneMillionTokens, 10 cycles).","severity":"medium","snippet":"        if (activeJob != 0) revert JobActive();","title":"Anyone holding the refundable 0.1% proposal stake can keep the maintenance pot locked indefinitely (single active job, permissionless close, first-come propose)"},{"citation":"resolved","description":"Access x economics seam. `passed()` needs only `job.yes >= QUORUM` (10,000,000 SWARMPEG, 1% of supply, src/SwarmMaintenance.sol:21) and `yes > no`; there is no minimum turnout relative to circulating supply, no cap on `amount` relative to the pot, no veto or guardian, and the proposer may name themselves as recipient (`proposePayment` only rejects address(0) and the pot). At the 2,500 IMD opening market cap, 1% of supply costs about 25 IMD plus price impact, while the pot accumulates 2% of all volume indefinitely. Once managedAssets() exceeds the cost of 10M tokens, any holder or buyer can propose `proposePayment(self, managedAssets(), anyHash)`, vote the remaining 9M from a second address, wait 7 + 2 days, call executePayment and withdraw all 10M tokens afterwards, keeping the full pot. The only defense is other holders locking more than 10M tokens in a 'no' vote within 7 days, and combined with finding 2 the attacker can repeat the proposal every 7 days at no cost, so defenders must win every round forever while the attacker needs to win once. The requester's 2% (20M) can block a single round only if it is watching. README.md line 104 and docs/SECURITY.md describe capture 'with economically acquired tokens' as a disclosed governance risk; this finding records the concrete break-even so the author can decide whether a 25 IMD attack budget against an unbounded pot is acceptable. Fixes that keep the design: cap each job at a fraction of managedAssets() per period; require yes votes >= a share of circulating (non-pool) supply; add a short objection window in which a larger stake can veto; or let a proposal be superseded by one with more support.","line":146,"path":"src/SwarmMaintenance.sol","reproduction":"State: pool seeded; five 10,000 IMD exact-input buys accrue 1,000 IMD of claims (managedAssets() == 1000e18); attacker address A holds 1,000,000 SWARMPEG, address B (same actor) holds 9,000,000 SWARMPEG, both approved to the pot. 1. A: proposePayment(A, 1000e18, keccak256('self')) -> id 1 (amount <= managedAssets passes). 2. B: vote(1, true, 9_000_000e18). 3. warp 9 days. 4. anyone: executePayment(1) -> claims are redeemed and 1,000 IMD are transferred to A; managedAssets() == 0. 5. A and B: withdrawVote(1) -> all 10M SWARMPEG returned. Expected: a payment of the entire pot to the proposer should need more than 1% of supply or a bounded amount. Actual: 10M tokens (about 25 IMD at launch) take 1,000 IMD. Verified in a scratch test on this tree (test_tenMillionTokensTakeTheWholePot).","severity":"medium","snippet":"        return id != 0 && job.voteEnd != 0 && block.timestamp >= job.voteEnd && job.yes >= QUORUM && job.yes > job.no;","title":"Absolute 1% quorum lets 10M tokens pay the whole maintenance pot to the proposer unless more than 10M tokens oppose within 7 days"}],"hash":"562a6e4cffa8acd706c6379a81026e621f1c298c02b56afe71a5c3cbc503ea03","nodeId":"0888c5c8-70e9-4234-b9bc-866267a9575d","outcome":"completed","summary":"The review is complete. The findings file is in place and the working tree has no stray files.\n\n**Outcome:** three substantiated findings in the Access Control, Trust Gap and Asymmetry area, written to `.imd-findings.json` with a coverage row for every listed entry point.\n\n- **High. Fee recipient is an unverified hand-written constructor address.** The hook stores `maintenancePot_` without checking it has code or is a SwarmMaintenance bound to the same manager and token. Every fee is minted as an ERC-6909 claim that only its holder can burn, so a wrong address strands 100% of fees with no rescue path. The launch flow cannot supply a correct value: the pot needs the launch token in its constructor, and the token is deployed by the factory in the same transaction as the hook. A proof test is attached that fails on the current tree and passes once the constructor refuses an unwired pot or deploys its own.\n- **Medium. The pot can be locked forever by anyone with the refundable 0.1% stake.** One active job, permissionless close, first-come propose, and a stake refunded at `voteEnd` let a griefer bundle close, withdraw and re-propose every 7 days. Confirmed over 10 cycles in a scratch test with only 1M tokens ever committed.\n- **Medium. The absolute 1% quorum pays the whole pot to the proposer.** 10M tokens, about 25 IMD at the opening cap, voted a 1,000 IMD pot to the proposer in a scratch test, with all tokens withdrawn afterwards. Disclosed in the README as a governance risk, reported here with the concrete break-even.\n\n**Coverage.** All thirteen entry points have a row. Hook initialization, pulse updates, claim redemption, the unlock callback, vote withdrawal and the token's ERC-20 paths hold. The static-analysis leads in my area, the strict-equality and uninitialized-local lines, were traced and are not defects. I did not go deep on the renderer's casts or on the pulse economics, which belong to other specialists.","treeHash":null,"usage":{"cachedInputTokens":1786291,"inputTokens":482,"model":"claude-fable-5-1","outputTokens":40541,"runtime":"claude","turns":39,"wallClockMs":638127}},{"artifacts":[],"attempt":1,"bundleHash":"d8bc988cddb6031ee05aadc71e5c283444498d0a761da09486003d29b847c2ba","device":"d7953fb8f7e2c68b","findings":[],"hash":"7a22fe0868b2be9631d6da9d9fc922f2214d35d2a437d4f5955c28345f6de91a","nodeId":"dbd202db-d027-4606-8d90-eb894296de13","outcome":"completed","summary":"Implemented the token, 2% IMD fee hook, animated on-chain glyphs, pulse tracking, and ownerless maintenance pot. Deployment and operating assumptions are documented.\n\nValidation: **51 tests passed**, plus `forge build` and `forge fmt --check`.\n\nMainnet fork rehearsal could not run: Foundry returned **“Connection refused.”** No deployment was broadcast.","treeHash":"e2ad481bcc1a0d179a2eed67d854d6abebd3deb6","usage":{"cachedInputTokens":3093760,"inputTokens":124872,"model":"gpt-6-astra","outputTokens":52635,"runtime":"codex","turns":9,"wallClockMs":2168162}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"827152cce766ca0d","findings":[{"citation":"resolved","description":"SwarmPegHook takes `maintenancePot_` as a literal constructor address, and every fee is minted as ERC-6909 claims to it forever (afterSwap: `poolManager.mint(maintenancePot, ...)`). The pot (`SwarmMaintenance(IPoolManager manager_, IERC20 token_)`, src/SwarmMaintenance.sol:74) must be constructed with the launch token's address. In the IMD flow the launch factory creates the token (`$token`) and deploys the hook and initializes the pool in a single transaction; the manifest has only `hook` and `token` contracts and placeholders `$poolManager/$token/$owner/$factory`. There is no placeholder or deployment step for a third contract, and the token address is not known before the launch transaction (so it cannot be written by hand either). The constructor args the hook needs therefore cannot be written: `[\"$poolManager\",\"$token\",<pot>,\"$factory\"]` where <pot> must be a contract that already exists with votingToken == $token. README/OPERATIONS step 3 ('Deploy SwarmPeg ... then SwarmMaintenance(manager, token), then SwarmPegHook') describes an ordering the factory does not perform. Either the launch is blocked, or the hook is deployed with a pot wired to some other token/address, and since the field is immutable all 2% fees go to the wrong place for good (claims minted to an address that cannot redeem or vote them).","line":61,"path":"src/SwarmPegHook.sol","reproduction":"Try to write launch.json for this tree: hook.constructorArgs must be 4 entries in order (manager_, token_, maintenancePot_, factory_). Entry 3 has no $-placeholder. A literal address must point at SwarmMaintenance(poolManager, token), but `token` is created by the factory in the same transaction that deploys the hook, so no such pot can exist when the manifest is written. Expected: the hook can be deployed from $poolManager/$token/$factory alone. Actual: it needs an address that cannot exist beforehand. A mis-wired value (e.g. a pot built for a guessed token) passes the constructor's non-zero check, and the fees are then minted permanently to a pot whose `votingToken` is not SWARMPEG, so `vote`/`proposePayment` can never spend them. Minimal fix that keeps the design: create the pot inside the hook constructor (`maintenancePot = address(new SwarmMaintenance(manager_, IERC20(address(token_))))`) and drop the argument. The constructor args then become `$poolManager,$token,$factory`.","severity":"medium","snippet":"    constructor(IPoolManager manager_, SwarmPeg token_, address maintenancePot_, address factory_) {","title":"Maintenance pot cannot be wired by the IMD launch flow: hook needs a pre-deployed SwarmMaintenance whose constructor needs the not-yet-existing launch token"},{"citation":"resolved","description":"Pulse adds floor(paired / 1e18) per trade (IMD_PER_PULSE = 1 ether, line 34), caps at 1000, and decays 1 point per block (~12 s). The launch opens at a 2,500 IMD market cap for the whole supply, with 880M tokens seeded one-sided. That gives virtual IMD depth of about L*sqrt(p0) = (880e6*sqrt(2.5e-6))*sqrt(2.5e-6) ≈ 2,200 IMD. Holding pulse at P takes about P IMD of volume within P blocks, i.e. about 1 IMD every block (≈7,200 IMD/day, ~3x the whole market cap daily) just to stay non-zero. Every trade under 1 IMD (0.04% of supply at opening price) adds 0. The brief's 'higher pulse → denser networks, brighter palettes, faster animations' and pulse-driven blink speed will therefore show the floor values (4 nodes, 3,200 ms, 48%) nearly all the time. The maximum is reachable mainly by one actor wash-trading ~1,000 IMD (~45% of pool depth), paying ~6.5% in LP and hook fees.","line":235,"path":"src/SwarmPegHook.sol","reproduction":"Fresh pool at the 2,500 IMD opening cap. A trader buys with 0.99 IMD every block for 100 blocks: each afterSwap computes addition = 0.99e18/1e18 = 0, so currentPulse() == 0 and glyphSVG shows nodes=4, duration=3200ms throughout. Expected: sustained organic trading raises pulse. Actual: pulse stays 0 unless single trades are >= 1 IMD and keep up with 1-point-per-block decay. Fix: scale IMD_PER_PULSE / decay to the pool's real depth (e.g. a fraction of 1 IMD, or decay per N blocks).","severity":"low","snippet":"        uint256 addition = paired / IMD_PER_PULSE;","title":"Pulse scaling is unreachable at the launch's own pool depth: needs >=1 whole IMD per block of volume just to stay above zero"},{"citation":"resolved","description":"passed() needs only yes >= 10M and yes > no. 88% of supply sits in the pool (held by the PoolManager, which cannot vote), and the 10% swarm allocation is claimed over time from a distributor. So the requester's 20M tokens (2x quorum) are the largest voting block at launch. The requester can propose `proposePayment(self, managedAssets(), h)` and add 19M yes votes. Unless other holders lock more than 20M SWARMPEG as 'no' within 7 days, they can execute after 9 days, and repeat every cycle. That makes the 'No owner' pot a de-facto requester-controlled treasury. Any outsider who buys ~1% of supply (≈25 IMD at the opening cap) can do the same if holders are inattentive. The README describes majority control, but the quorum is not sized to the supply that can actually vote.","line":21,"path":"src/SwarmMaintenance.sol","reproduction":"After launch: requester (20M SWARMPEG) approves the pot, calls proposePayment(requester, pot.managedAssets(), 0x01) (1M staked as yes), then vote(1, true, 19_000_000e18). Nobody votes no. warp 9 days, then executePayment(1) transfers all accrued IMD fees to the requester. Expected (brief): no owner-like control over fee funds. Actual: a single allocation holder satisfies quorum unilaterally. Fix (scope decision): size quorum against circulating/votable supply or require approval relative to turnout plus a longer veto window.","severity":"low","snippet":"    uint256 public constant QUORUM = 10_000_000 ether; // 1% of fixed supply","title":"Requester's 2% allocation alone meets the 1% QUORUM and can direct the whole maintenance pot to any recipient"}],"hash":"834d1bba335d2c9a48379f8c7150b46c30d821837f9895ca9881875637be3ad4","nodeId":"ee0927fe-0aed-4c59-bc69-85d569b51e08","outcome":"completed","summary":"I found no critical or high defects. There are three findings (one medium, two low), now written to `.imd-findings.json` together with a coverage row for all 13 entry points. The existing suite passes (51 tests). No repository file was changed.\n\n**Findings**\n1. **Medium: the launch can't wire up the maintenance pot** (`src/SwarmPegHook.sol:61`). The hook needs the pot's address as a constructor argument. The pot (`SwarmMaintenance`) needs the launch token's address in its own constructor. But the factory creates the token and deploys the hook in a single transaction, and `launch.json` has no placeholder or deploy step for a third contract. So either the launch is blocked, or the hook gets a pot built for the wrong token. That can't be changed later, and every 2% fee would then go permanently to a pot that can never spend it. Suggested fix: create the pot inside the hook's constructor and drop the argument, so the hook's arguments become `$poolManager, $token, $factory`.\n2. **Low: the pulse is effectively stuck at zero** (`src/SwarmPegHook.sol:235`). Each trade adds one point per whole IMD, and the pulse loses one point per block. At the 2,500 IMD opening market cap the pool holds roughly 2,200 IMD of depth. Staying above zero would take about 1 IMD of volume every block, around three times the market cap per day. Trades under 1 IMD add nothing, so glyphs would almost always render at the quietest settings. Reproduction: buy with 0.99 IMD every block for 100 blocks and the pulse stays at 0.\n3. **Low: the requester alone can spend the pot** (`src/SwarmMaintenance.sol:21`). Approval needs 10 million \"yes\" tokens, which is 1% of supply. 88% of supply sits in the pool and can't vote, so the requester's 20 million tokens meet the quorum alone. They could propose paying themselves the whole pot and collect it 9 days later unless others lock more than 20 million tokens as \"no\". This contradicts \"No owner\" in substance. Changing it is a design decision for the requester.\n\n**What I checked and found sound**\n- **Fee accounting:** all four swap shapes (buy/sell, exact input/output) net to zero, and partial fills are handled correctly.\n- **Pool binding:** only the pool manager and factory can initialize, and only the exact pair, fee 12,500 and tick spacing 60 are accepted.\n- **Fee claims:** fees are credited as claims, so no transfer is needed mid-swap, and they can be redeemed only into the pot.\n- **Pulse history:** the pulse history and its lookup behave correctly.\n- **Glyph tracking:** the per-holder glyph count stays equal to whole tokens held.\n- **Voting:** the vote/withdraw/execute/close timing holds.\n- **Static analysis:** the tool's medium-severity leads (strict equality checks, uninitialized locals, an ignored unlock return value) turned out harmless.\n\n**Not reported:** a holder with 1 million tokens can keep the single proposal slot occupied forever. The README already lists this as a governance tradeoff. Seed randomness is weak and can change within the same block, which the README also documents.\n\n**Limits:** none of the findings is high severity, so none carries a Foundry proof. Finding 1 can't be shown in a local test anyway, because it depends on how the factory deploys. I didn't simulate the actual factory transaction or real IMD token behavior.","treeHash":null,"usage":{"cachedInputTokens":665701,"inputTokens":20,"model":"claude-opus-5-5","outputTokens":22525,"runtime":"claude","turns":11,"wallClockMs":383535}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"6a7dea7fd5378a40","findings":[],"hash":"fa84ef62f429ed12a47b11c121a6891f0cf300a225f9003dde18d83795ebfe1f","nodeId":"0d3e0572-0ed7-453d-bb0d-222d50ac3828","outcome":"failed","summary":"needs input: SwarmPegHook's third constructor argument, maintenancePot_, requires the address of the ownerless SwarmMaintenance contract configured with this launch's PoolManager and token. Neither that address nor a supported deployer placeholder for it was supplied. The available $poolManager, $token, $owner and $factory placeholders do not identify this pot. Substituting $owner would redirect all hook fees to the owner wallet instead of the holder-governed maintenance pot. A complete launch.json cannot be written without inventing an address or changing contracts outside the permitted scope. — What real Ethereum mainnet address or supported deployer placeholder should supply maintenancePot_, resolving to SwarmMaintenance configured with this launch's $poolManager and $token?","treeHash":null,"usage":{"cachedInputTokens":599552,"inputTokens":71489,"model":"gpt-6-astra","outputTokens":3566,"runtime":"codex","turns":4,"wallClockMs":164924}}],"verification":[{"checks":[{"durationMs":96504,"exitCode":0,"name":"build","output":"Compiling 107 files with Solc 0.8.26\nSolc 0.8.26 finished in 96.38s\nCompiler run successful!\n","passed":true},{"durationMs":10050,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 7 tests for test/Maintenance.t.sol:MaintenanceTest\n[PASS] test_approvedJobExpiresWithoutSeizingVotes() (gas: 935031)\n[PASS] test_callbackCannotBeForgedAndRedemptionCannotRedirect() (gas: 265076)\n[PASS] test_holderApprovedJobPaidFromClaimsAndVotesRefunded() (gas: 1239616)\n[PASS] test_invalidJobsAndUnfundedVotingRollBack() (gas: 645028)\n[PASS] test_majorityOppositionRejectsEvenWithQuorum() (gas: 1107433)\n[PASS] test_rejectedPairTransferRollsBackAndCanRetry() (gas: 1551490)\n[PASS] test_votesLockedDuplicateAndLateVotesRefused() (gas: 693783)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 4.79ms (2.16ms CPU time)\n\nRan 9 tests for test/adversarial/MaintenanceBoundaries.t.sol:MaintenanceBoundariesTest\n[PASS] testFuzz_quorumAndStrictMajorityAtTheirOneWeiEdges(uint8,uint8) (runs: 256, μ: 924930, ~: 933359)\n[PASS] test_executionOpensExactlyAtDelayAndOnlyPaysFixedRecipient() (gas: 926640)\n[PASS] test_lastExecutableSecondAndExactExpiry() (gas: 1188814)\n[PASS] test_mixedLiquidFundsAndClaimsRedeemOnlyThePaymentShortfall() (gas: 1354985)\n[PASS] test_nonexistentZeroAndSelfRecipientPathsHaveExactErrors() (gas: 703079)\n[PASS] test_oldReceiptRemainsRefundableDuringANewActiveJob() (gas: 1019844)\n[PASS] test_proposalStakeFailureRollsBackAllJobAndReceiptState() (gas: 805516)\n[PASS] test_unfundedVoteRollsBackTalliesReceiptsAllowanceAndGlyphs() (gas: 863479)\n[PASS] test_votingClosesAndVotesUnlockExactlyAtVoteEnd() (gas: 974695)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 57.21ms (57.56ms CPU time)\n\nRan 3 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_badFlagsAndZeroConstructorParametersRefused() (gas: 95283)\n[PASS] test_realCreate2MiningDeploymentAndFactoryInitialization() (gas: 7557084)\n[PASS] test_runtimeHasNoDelegatecallSelfdestructOrCallcode() (gas: 7113613)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 71.83ms (39.57ms CPU time)\n\nRan 5 tests for test/Glyphs.t.sol:GlyphsTest\n[PASS] test_higherPulseHasMoreNodesBrighterColorsFasterAnimation() (gas: 20148507)\n[PASS] test_livePulseDecayHistoryAndPermissionlessUpdate() (gas: 1013406)\n[PASS] test_noOwnerPauseOrRendererReplacement() (gas: 150283)\n[PASS] test_seedCombinesTransferTradeBlockPreviousHolderAndPulse() (gas: 1136356)\n[PASS] test_svgAndMetadataAreOnChainAndMissingGlyphReverts() (gas: 9670832)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 71.74ms (76.52ms CPU time)\n\nRan 8 tests for test/adversarial/HookAccounting.t.sol:HookAccountingPairFirstTest\n[PASS] testFuzz_coreDeltaAndWalletFeeAgree(bool,bool,uint256) (runs: 512, μ: 586169, ~: 593891)\nLogs:\n  Bound result 8368820411760831100767\n\n[PASS] test_allLiquidityCanExitWhileAccruedMaintenanceClaimsRemainRedeemable() (gas: 1434534)\n[PASS] test_allSwapModesRejectAmountsOutsideSignedDeltaDomain() (gas: 636130)\n[PASS] test_failedSlippageRestoresExistingClaimsPulsePriceAndBalances() (gas: 1707200)\n[PASS] test_minorUnitRoundingBoundariesInEverySwapMode() (gas: 15330725)\n[PASS] test_specifiedFeeCannotOverflowInt128AndBeforeSwapCannotConsumeWholeInput() (gas: 98198)\n[PASS] test_uninitializedHookRefusesTradesAndOnlyExactFactoryPoolCanBind() (gas: 428079)\n[PASS] test_wrongSignedPairDeltaRejectedBeforeMintingOrRecording() (gas: 124296)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 73.28ms (91.87ms CPU time)\n\nRan 13 tests for test/Hook.t.sol:HookPairFirstTest\n[PASS] testFuzz_feeAndSettlement(bool,bool,uint256) (runs: 256, μ: 625874, ~: 621163)\nLogs:\n  Bound result 2200000000000000000000\n\n[PASS] test_buyExactInput() (gas: 615108)\n[PASS] test_buyExactOutput() (gas: 613505)\n[PASS] test_callbacksRejectUnauthorizedAndWrongPools() (gas: 168246)\n[PASS] test_claimRedemptionOnlyPaysPot() (gas: 645352)\n[PASS] test_dustAndExtremeAmountsRefused() (gas: 79750)\n[PASS] test_liquidityCollectionAndRemovalUnrestricted() (gas: 1358968)\n[PASS] test_partialPairedSpecifiedRevertsWithoutFees() (gas: 404246)\n[PASS] test_partialUnspecifiedPairChargesActualFill() (gas: 570865)\n[PASS] test_permissionsInitializationAndSize() (gas: 38734)\n[PASS] test_sellExactInput() (gas: 690139)\n[PASS] test_sellExactOutput() (gas: 691246)\n[PASS] test_slippageFailureRollsBackFeesAndPulse() (gas: 442887)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 84.83ms (77.38ms CPU time)\n\nRan 13 tests for test/Hook.t.sol:HookTokenFirstTest\n[PASS] testFuzz_feeAndSettlement(bool,bool,uint256) (runs: 256, μ: 629829, ~: 621564)\nLogs:\n  Bound result 9677162755185640313839\n\n[PASS] test_buyExactInput() (gas: 609482)\n[PASS] test_buyExactOutput() (gas: 609182)\n[PASS] test_callbacksRejectUnauthorizedAndWrongPools() (gas: 167715)\n[PASS] test_claimRedemptionOnlyPaysPot() (gas: 639685)\n[PASS] test_dustAndExtremeAmountsRefused() (gas: 79748)\n[PASS] test_liquidityCollectionAndRemovalUnrestricted() (gas: 1358477)\n[PASS] test_partialPairedSpecifiedRevertsWithoutFees() (gas: 403604)\n[PASS] test_partialUnspecifiedPairChargesActualFill() (gas: 575842)\n[PASS] test_permissionsInitializationAndSize() (gas: 38722)\n[PASS] test_sellExactInput() (gas: 695380)\n[PASS] test_sellExactOutput() (gas: 695161)\n[PASS] test_slippageFailureRollsBackFeesAndPulse() (gas: 437245)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 84.86ms (88.33ms CPU time)\n\nRan 8 tests for test/adversarial/HookAccounting.t.sol:HookAccountingTokenFirstTest\n[PASS] testFuzz_coreDeltaAndWalletFeeAgree(bool,bool,uint256) (runs: 512, μ: 585960, ~: 591753)\nLogs:\n  Bound result 8368820411760831100767\n\n[PASS] test_allLiquidityCanExitWhileAccruedMaintenanceClaimsRemainRedeemable() (gas: 1426789)\n[PASS] test_allSwapModesRejectAmountsOutsideSignedDeltaDomain() (gas: 633314)\n[PASS] test_failedSlippageRestoresExistingClaimsPulsePriceAndBalances() (gas: 1705476)\n[PASS] test_minorUnitRoundingBoundariesInEverySwapMode() (gas: 15301225)\n[PASS] test_specifiedFeeCannotOverflowInt128AndBeforeSwapCannotConsumeWholeInput() (gas: 98109)\n[PASS] test_uninitializedHookRefusesTradesAndOnlyExactFactoryPoolCanBind() (gas: 426669)\n[PASS] test_wrongSignedPairDeltaRejectedBeforeMintingOrRecording() (gas: 123797)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 84.93ms (117.85ms CPU time)\n\nRan 1 test for test/Hook.t.sol:HookOneSidedTest\n[PASS] test_oneSidedTokenOnlySeedBuyWithoutAnyPairInventory(bool) (runs: 256, μ: 6663668, ~: 6658088)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 93.42ms (92.96ms CPU time)\n\nRan 1 test for test/LaunchPrice.t.sol:LaunchPriceTest\n[PASS] test_policyMarketCapTradesInBothCurrencyOrders(bool) (runs: 256, μ: 7761515, ~: 7760562)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 139.48ms (138.94ms CPU time)\n\nRan 7 tests for test/SwarmPeg.t.sol:SwarmPegTest\n[PASS] testFuzz_transferConservesSupplyAndWholeGlyphs(uint256,uint256) (runs: 256, μ: 305591, ~: 317236)\nLogs:\n  Bound result 579399255585763856142254252\n  Bound result 732327\n\n[PASS] test_allowanceAndFailureAtomicity() (gas: 526985)\n[PASS] test_batchesRetainPrefixAndDiscardManyInLogTime() (gas: 6776161)\n[PASS] test_fixedSupplyAndLaunchAllocation() (gas: 448599)\n[PASS] test_fractionalBoundariesRetireAndRecreate() (gas: 676041)\n[PASS] test_noAdminOrMintSelectors() (gas: 179442)\n[PASS] test_zeroAndSelfTransfersDoNotReroll() (gas: 300005)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 139.38ms (14.17ms CPU time)\n\nRan 6 tests for test/adversarial/GlyphMetadata.t.sol:GlyphMetadataTest\n[PASS] testFuzz_finalizedGlyphSeedSurvivesFutureTradesAndKeeperUpdates(uint256) (runs: 128, μ: 1112717, ~: 1112833)\nLogs:\n  Bound result 5569\n\n[PASS] testFuzz_rendererTraitsAreMonotoneAndSaturate(uint256,uint256) (runs: 512, μ: 17525, ~: 17653)\nLogs:\n  Bound result 105\n  Bound result 536\n\n[PASS] test_glyphRangeEventsDescribeExactlyTheSlotsThatChanged() (gas: 459542)\n[PASS] test_keeperIsIdempotentAndHistorySurvivesCoalescingAndDecay() (gas: 965584)\n[PASS] test_metadataDecodesToTheActualHolderSlotPulseAndRenderedSVG() (gas: 157640858)\n[PASS] test_zeroSelfAndSubWholeTransfersEmitNoSpuriousGlyphEvents() (gas: 350276)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 252.56ms (396.36ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:SwarmInvariantTest\n[PASS]\nSwarmInvariantTest invariants:\n[PASS] invariant_feesConservedAndAllTransientDeltasSettled\n[PASS] invariant_fixedSupplyAndGlyphsMatchEveryWholeBalance\n SwarmInvariantTest invariants (runs: 64, calls: 3072, reverts: 0)\n\n╭--------------+-----------------+-------+---------+----------╮\n| Contract     | Selector        | Calls | Reverts | Discards |\n+=============================================================+\n| SwarmHandler | advancePulse    | 728   | 0       | 0        |\n|--------------+-----------------+-------+---------+----------|\n| SwarmHandler | redeem          | 783   | 0       | 0        |\n|--------------+-----------------+-------+---------+----------|\n| SwarmHandler | trade           | 758   | 0       | 0        |\n|--------------+-----------------+-------+---------+----------|\n| SwarmHandler | transferBetween | 803   | 0       | 0        |\n╰--------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3873\n  Bound result 8388608\n  Bound result 200\n  Bound result 88748\n  Bound result 12500\n  Bound result 142\n  Bound result 3126\n  Bound result 140\n  Bound result 60245\n  Bound result 9440\n  Bound result 172\n  Bound result 1566\n  Bound result 113\n  Bound result 0\n  Bound result 777600\n  Bound result 131\n  Bound result 20\n  Bound result 200\n  Bound result 0\n  Bound result 10000\n  Bound result 11138978288383400386\n  Bound result 33\n  Bound result 159074650666247584\n  Bound result 136\n  Bound result 1\n  Bound result 4267\n  Bound result 2163258444\n  Bound result 2197\n  Bound result 18400659645204006650\n  Bound result 0\n  Bound result 2764\n  Bound result 79785616639305117713396765\n  Bound result 1100\n  Bound result 200000\n  Bound result 1181\n  Bound result 200\n  Bound result 1099\n  Bound result 360\n  Bound result 189\n  Bound result 144\n  Bound result 244\n  Bound result 805306368\n  Bound result 372682035443708736\n  Bound result 1000000000000000000\n  Bound result 136\n  Bound result 99999999999999999005\n  Bound result 128\n  Bound result 95\n  Bound result 14974\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 966.20ms (962.09ms CPU time)\n\nRan 2 tests for test/adversarial/MaintenanceStateMachine.t.sol:MaintenanceStateMachineTest\n[PASS] invariant_votesFeesAndPaymentsConservedAcrossJobs() (runs: 128, calls: 10240, reverts: 0)\n\n╭-------------------------+-------------+-------+---------+----------╮\n| Contract                | Selector    | Calls | Reverts | Discards |\n+====================================================================+\n| MaintenanceStateHandler | advanceTime | 1323  | 0       | 0        |\n|-------------------------+-------------+-------+---------+----------|\n| MaintenanceStateHandler | donate      | 1278  | 0       | 0        |\n|-------------------------+-------------+-------+---------+----------|\n| MaintenanceStateHandler | propose     | 1298  | 0       | 0        |\n|-------------------------+-------------+-------+---------+----------|\n| MaintenanceStateHandler | redeem      | 1334  | 0       | 0        |\n|-------------------------+-------------+-------+---------+----------|\n| MaintenanceStateHandler | settle      | 1255  | 0       | 0        |\n|-------------------------+-------------+-------+---------+----------|\n| MaintenanceStateHandler | trade       | 1282  | 0       | 0        |\n|-------------------------+-------------+-------+---------+----------|\n| MaintenanceStateHandler | vote        | 1225  | 0       | 0        |\n|-------------------------+-------------+-------+---------+----------|\n| MaintenanceStateHandler | withdraw    | 1245  | 0       | 0        |\n╰-------------------------+-------------+-------+---------+----------╯\n\nLogs:\n  Bound result 100000000000000000000\n  Bound result 1345355501247354112\n  Bound result 99999999999999999243\n  Bound result 2588\n  Bound result 99999999999999999123\n  Bound result 72009000361\n  Bound result 5122769350346359040\n  Bound result 16384\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 10000000000000000\n  Bound result 72055355738293757914\n  Bound result 10654\n  Bound result 1160370045808138709\n  Bound result 11086\n  Bound result 1\n  Bound result 17300\n  Bound result 729938893599895071\n  Bound result 1\n  Bound result 1\n  Bound result 9400991329115885834\n  Bound result 62381\n  Bound result 575160070\n  Bound result 2976884855084950644\n  Bound result 13565\n  Bound result 105\n  Bound result 360325958730932433\n  Bound result 1\n  Bound result 149148\n  Bound result 60\n  Bound result 2977\n  Bound result 1\n  Bound result 3703\n  Bound result 107004\n  Bound result 100000000000000000\n  Bound result 8455\n  Bound result 65\n  Bound result 6474\n  Bound result 1\n  Bound result 10610\n  Bound result 14992\n  Bound result 2709\n  Bound result 123303\n  Bound result 2\n  Bound result 6283139468163511351\n  Bound result 329157395988\n  Bound result 12568864\n  Bound result 16384\n  Bound result 14269\n  Bound result 1209600\n  Bound result 75539603023779993\n  Bound result 3\n  Bound result 99999999999999999513\n  Bound result 2910\n  Bound result 3\n  Bound result 9454\n  Bound result 4\n  Bound result 12305\n  Bound result 7348\n  Bound result 4\n  Bound result 3169\n  Bound result 1818\n  Bound result 1\n\n[PASS] test_handlerReachesPaymentRejectionRedemptionAndOldVoteWithdrawal() (gas: 3249307)\nLogs:\n  Bound result 100000000000000000000\n  Bound result 1000000000000000000\n  Bound result 9000000000000000000000000\n  Bound result 1\n  Bound result 500000000000000000\n  Bound result 1000000000000000000\n  Bound result 12000000000000000000000000\n  Bound result 1\n  Bound result 1\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.03s (3.02s CPU time)\n\nRan 2 tests for test/adversarial/GlyphStateMachine.t.sol:GlyphStateMachineTest\n[PASS] invariant_balancesAndEveryGlyphMatchTheTransferHistory() (runs: 128, calls: 10240, reverts: 0)\n\n╭-------------------+-----------------+-------+---------+----------╮\n| Contract          | Selector        | Calls | Reverts | Discards |\n+==================================================================+\n| GlyphStateHandler | advanceBlock    | 3422  | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| GlyphStateHandler | move            | 3442  | 0       | 0        |\n|-------------------+-----------------+-------+---------+----------|\n| GlyphStateHandler | rejectOverspend | 3376  | 0       | 0        |\n╰-------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 128\n  Bound result 5075\n  Bound result 48\n  Bound result 1000000000000000000\n  Bound result 51\n  Bound result 1\n  Bound result 1\n  Bound result 6233\n  Bound result 73\n  Bound result 399\n  Bound result 4999999999998533334\n  Bound result 9000000000000000000\n  Bound result 1617\n  Bound result 88\n  Bound result 3000\n  Bound result 5075\n  Bound result 0\n  Bound result 0\n  Bound result 2\n  Bound result 32\n  Bound result 100999996768\n  Bound result 71\n  Bound result 311384120638949554\n  Bound result 31\n  Bound result 0\n  Bound result 12\n  Bound result 129\n  Bound result 74\n  Bound result 48\n  Bound result 100\n  Bound result 59\n  Bound result 4047833394056068929\n  Bound result 0\n  Bound result 44\n  Bound result 16311384120638947937\n  Bound result 9419592370182826594\n  Bound result 99\n  Bound result 100000\n  Bound result 0\n  Bound result 100\n  Bound result 51\n  Bound result 4916600628508238695\n  Bound result 0\n  Bound result 2090141662902583567\n  Bound result 16\n  Bound result 3912\n  Bound result 1\n  Bound result 2096267918\n  Bound result 95\n\n[PASS] test_handlerVisitsFractionalRetirementReacquisitionAndFailure() (gas: 3040657)\nLogs:\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 1000000000000000000\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 9.92s (9.92s CPU time)\n\nRan 15 test suites in 9.92s (15.07s CPU time): 86 tests passed, 0 failed, 0 skipped (86 total tests)\n","passed":true},{"durationMs":108,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"SwarmMaintenance.closeJob(uint256)\",\"SwarmMaintenance.executePayment(uint256)\",\"SwarmMaintenance.proposePayment(address,uint256,bytes32)\",\"SwarmMaintenance.redeemFees(uint256)\",\"SwarmMaintenance.unlockCallback(bytes)\",\"SwarmMaintenance.vote(uint256,bool,uint256)\",\"SwarmMaintenance.withdrawVote(uint256)\",\"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.updatePulse()\"],\"files\":{\".gitignore\":4,\"LICENSE\":23,\"README.md\":108,\"docs/OPERATIONS.md\":33,\"docs/SECURITY.md\":73,\"docs/dependencies.json\":22,\"docs/glyph-active.svg\":1,\"docs/glyph-quiet.svg\":1,\"foundry.toml\":26,\"rehearsal/Mainnet.t.sol\":52,\"remappings.txt\":4,\"script/PrepareDeployment.s.sol\":54,\"src/GlyphRenderer.sol\":112,\"src/HookFlags.sol\":31,\"src/LaunchConstants.sol\":9,\"src/SwarmMaintenance.sol\":195,\"src/SwarmPeg.sol\":108,\"src/SwarmPegHook.sol\":282,\"test/Deployment.t.sol\":86,\"test/Fixture.sol\":103,\"test/Glyphs.t.sol\":116,\"test/Hook.t.sol\":226,\"test/Invariants.t.sol\":135,\"test/LaunchPrice.t.sol\":38,\"test/Maintenance.t.sol\":184,\"test/SwarmPeg.t.sol\":138,\"test/adversarial/GlyphMetadata.t.sol\":256,\"test/adversarial/GlyphStateMachine.t.sol\":170,\"test/adversarial/HookAccounting.t.sol\":261,\"test/adversarial/MaintenanceBoundaries.t.sol\":254,\"test/adversarial/MaintenanceStateMachine.t.sol\":340,\"test/adversarial/README.md\":34,\"test/mocks/MockERC20.sol\":14,\"test/mocks/PoolHarness.sol\":80},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"45b1f8398202f564b92d438052086f34a757029e597e7835a64cd3c236ac731b","verifiedTreeHash":"ae69ad59383b50b5ead30799dabd804128eb80e0","verifierVersion":"0.1.0+8fbf00a5"},{"checks":[{"durationMs":45311,"exitCode":0,"name":"build","output":"Compiling 101 files with Solc 0.8.26\nSolc 0.8.26 finished in 45.22s\nCompiler run successful!\n","passed":true},{"durationMs":909,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 7 tests for test/Maintenance.t.sol:MaintenanceTest\n[PASS] test_approvedJobExpiresWithoutSeizingVotes() (gas: 935031)\n[PASS] test_callbackCannotBeForgedAndRedemptionCannotRedirect() (gas: 265076)\n[PASS] test_holderApprovedJobPaidFromClaimsAndVotesRefunded() (gas: 1239616)\n[PASS] test_invalidJobsAndUnfundedVotingRollBack() (gas: 645028)\n[PASS] test_majorityOppositionRejectsEvenWithQuorum() (gas: 1107433)\n[PASS] test_rejectedPairTransferRollsBackAndCanRetry() (gas: 1551490)\n[PASS] test_votesLockedDuplicateAndLateVotesRefused() (gas: 693783)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 3.36ms (2.52ms CPU time)\n\nRan 3 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_badFlagsAndZeroConstructorParametersRefused() (gas: 95283)\n[PASS] test_realCreate2MiningDeploymentAndFactoryInitialization() (gas: 7557084)\n[PASS] test_runtimeHasNoDelegatecallSelfdestructOrCallcode() (gas: 7113613)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 21.37ms (22.18ms CPU time)\n\nRan 5 tests for test/Glyphs.t.sol:GlyphsTest\n[PASS] test_higherPulseHasMoreNodesBrighterColorsFasterAnimation() (gas: 20148507)\n[PASS] test_livePulseDecayHistoryAndPermissionlessUpdate() (gas: 1013406)\n[PASS] test_noOwnerPauseOrRendererReplacement() (gas: 150283)\n[PASS] test_seedCombinesTransferTradeBlockPreviousHolderAndPulse() (gas: 1136356)\n[PASS] test_svgAndMetadataAreOnChainAndMissingGlyphReverts() (gas: 9670832)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 33.41ms (55.35ms CPU time)\n\nRan 13 tests for test/Hook.t.sol:HookTokenFirstTest\n[PASS] testFuzz_feeAndSettlement(bool,bool,uint256) (runs: 256, μ: 626969, ~: 621564)\nLogs:\n  Bound result 9677162755185640313839\n\n[PASS] test_buyExactInput() (gas: 609482)\n[PASS] test_buyExactOutput() (gas: 609182)\n[PASS] test_callbacksRejectUnauthorizedAndWrongPools() (gas: 167715)\n[PASS] test_claimRedemptionOnlyPaysPot() (gas: 639685)\n[PASS] test_dustAndExtremeAmountsRefused() (gas: 79748)\n[PASS] test_liquidityCollectionAndRemovalUnrestricted() (gas: 1358477)\n[PASS] test_partialPairedSpecifiedRevertsWithoutFees() (gas: 403604)\n[PASS] test_partialUnspecifiedPairChargesActualFill() (gas: 575842)\n[PASS] test_permissionsInitializationAndSize() (gas: 38722)\n[PASS] test_sellExactInput() (gas: 695380)\n[PASS] test_sellExactOutput() (gas: 695161)\n[PASS] test_slippageFailureRollsBackFeesAndPulse() (gas: 437245)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 34.60ms (38.18ms CPU time)\n\nRan 13 tests for test/Hook.t.sol:HookPairFirstTest\n[PASS] testFuzz_feeAndSettlement(bool,bool,uint256) (runs: 256, μ: 623026, ~: 621116)\nLogs:\n  Bound result 9677162755185640313839\n\n[PASS] test_buyExactInput() (gas: 615108)\n[PASS] test_buyExactOutput() (gas: 613505)\n[PASS] test_callbacksRejectUnauthorizedAndWrongPools() (gas: 168246)\n[PASS] test_claimRedemptionOnlyPaysPot() (gas: 645352)\n[PASS] test_dustAndExtremeAmountsRefused() (gas: 79750)\n[PASS] test_liquidityCollectionAndRemovalUnrestricted() (gas: 1358968)\n[PASS] test_partialPairedSpecifiedRevertsWithoutFees() (gas: 404246)\n[PASS] test_partialUnspecifiedPairChargesActualFill() (gas: 570865)\n[PASS] test_permissionsInitializationAndSize() (gas: 38734)\n[PASS] test_sellExactInput() (gas: 690139)\n[PASS] test_sellExactOutput() (gas: 691246)\n[PASS] test_slippageFailureRollsBackFeesAndPulse() (gas: 442887)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 34.93ms (41.69ms CPU time)\n\nRan 1 test for test/Hook.t.sol:HookOneSidedTest\n[PASS] test_oneSidedTokenOnlySeedBuyWithoutAnyPairInventory(bool) (runs: 256, μ: 6663668, ~: 6658088)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 68.52ms (67.88ms CPU time)\n\nRan 7 tests for test/SwarmPeg.t.sol:SwarmPegTest\n[PASS] testFuzz_transferConservesSupplyAndWholeGlyphs(uint256,uint256) (runs: 256, μ: 303573, ~: 317041)\nLogs:\n  Bound result 579399255585763856142254252\n  Bound result 732327\n\n[PASS] test_allowanceAndFailureAtomicity() (gas: 526985)\n[PASS] test_batchesRetainPrefixAndDiscardManyInLogTime() (gas: 6776161)\n[PASS] test_fixedSupplyAndLaunchAllocation() (gas: 448599)\n[PASS] test_fractionalBoundariesRetireAndRecreate() (gas: 676041)\n[PASS] test_noAdminOrMintSelectors() (gas: 179442)\n[PASS] test_zeroAndSelfTransfersDoNotReroll() (gas: 300005)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 68.48ms (16.51ms CPU time)\n\nRan 1 test for test/LaunchPrice.t.sol:LaunchPriceTest\n[PASS] test_policyMarketCapTradesInBothCurrencyOrders(bool) (runs: 256, μ: 7761515, ~: 7760562)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 101.59ms (101.04ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:SwarmInvariantTest\n[PASS]\nSwarmInvariantTest invariants:\n[PASS] invariant_feesConservedAndAllTransientDeltasSettled\n[PASS] invariant_fixedSupplyAndGlyphsMatchEveryWholeBalance\n SwarmInvariantTest invariants (runs: 64, calls: 3072, reverts: 0)\n\n╭--------------+-----------------+-------+---------+----------╮\n| Contract     | Selector        | Calls | Reverts | Discards |\n+=============================================================+\n| SwarmHandler | advancePulse    | 728   | 0       | 0        |\n|--------------+-----------------+-------+---------+----------|\n| SwarmHandler | redeem          | 783   | 0       | 0        |\n|--------------+-----------------+-------+---------+----------|\n| SwarmHandler | trade           | 758   | 0       | 0        |\n|--------------+-----------------+-------+---------+----------|\n| SwarmHandler | transferBetween | 803   | 0       | 0        |\n╰--------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3873\n  Bound result 2400000000000000000\n  Bound result 200\n  Bound result 101\n  Bound result 41335123972817620\n  Bound result 48\n  Bound result 3257\n  Bound result 200\n  Bound result 101\n  Bound result 1032989298506024\n  Bound result 172\n  Bound result 1566\n  Bound result 61\n  Bound result 0\n  Bound result 101\n  Bound result 6858\n  Bound result 74\n  Bound result 44\n  Bound result 1000000000000000000000\n  Bound result 20000000000000000\n  Bound result 11138978288383400386\n  Bound result 182\n  Bound result 48589289318111380\n  Bound result 109\n  Bound result 200\n  Bound result 4538\n  Bound result 2548616525\n  Bound result 2092\n  Bound result 6844\n  Bound result 3000000000000000000\n  Bound result 1937\n  Bound result 4515\n  Bound result 1000000000000000999\n  Bound result 6\n  Bound result 10170\n  Bound result 112\n  Bound result 20000000000000000\n  Bound result 6222\n  Bound result 189\n  Bound result 120\n  Bound result 190195554987049352\n  Bound result 5\n  Bound result 1158437851692848\n  Bound result 99999999999999999061\n  Bound result 11\n  Bound result 1002\n  Bound result 59\n  Bound result 14\n  Bound result 14634\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 805.07ms (802.43ms CPU time)\n\nRan 9 test suites in 806.43ms (1.17s CPU time): 51 tests passed, 0 failed, 0 skipped (51 total tests)\n","passed":true},{"durationMs":53,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"SwarmMaintenance.closeJob(uint256)\",\"SwarmMaintenance.executePayment(uint256)\",\"SwarmMaintenance.proposePayment(address,uint256,bytes32)\",\"SwarmMaintenance.redeemFees(uint256)\",\"SwarmMaintenance.unlockCallback(bytes)\",\"SwarmMaintenance.vote(uint256,bool,uint256)\",\"SwarmMaintenance.withdrawVote(uint256)\",\"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.updatePulse()\"],\"files\":{\".gitignore\":4,\"LICENSE\":23,\"README.md\":108,\"docs/OPERATIONS.md\":33,\"docs/SECURITY.md\":73,\"docs/dependencies.json\":22,\"docs/glyph-active.svg\":1,\"docs/glyph-quiet.svg\":1,\"foundry.toml\":26,\"rehearsal/Mainnet.t.sol\":52,\"remappings.txt\":4,\"script/PrepareDeployment.s.sol\":54,\"src/GlyphRenderer.sol\":112,\"src/HookFlags.sol\":31,\"src/LaunchConstants.sol\":9,\"src/SwarmMaintenance.sol\":195,\"src/SwarmPeg.sol\":108,\"src/SwarmPegHook.sol\":282,\"test/Deployment.t.sol\":86,\"test/Fixture.sol\":103,\"test/Glyphs.t.sol\":116,\"test/Hook.t.sol\":226,\"test/Invariants.t.sol\":135,\"test/LaunchPrice.t.sol\":38,\"test/Maintenance.t.sol\":184,\"test/SwarmPeg.t.sol\":138,\"test/mocks/MockERC20.sol\":14,\"test/mocks/PoolHarness.sol\":80},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":4017,"exitCode":0,"name":"slither","output":"[medium/high] incorrect-equality at src/SwarmPegHook.sol:233: SwarmPegHook._recordTrade(address,SwapParams,BalanceDelta,uint256) (src/SwarmPegHook.sol#233-256) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/SwarmPeg.sol:61: SwarmPeg._update(address,address,uint256) (src/SwarmPeg.sol#61-96) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/SwarmPegHook.sol:258: SwarmPegHook._writePulse(uint256) (src/SwarmPegHook.sol#258-262) uses a dangerous strict equality:\n[medium/medium] uninitialized-local at src/GlyphRenderer.sol:41: GlyphRenderer.render(bytes32,bytes32,uint256).faces (src/GlyphRenderer.sol#41) is a local variable never initialized\n[medium/medium] uninitialized-local at src/SwarmPegHook.sol:169: SwarmPegHook.pulseAt(uint256).low (src/SwarmPegHook.sol#169) is a local variable never initialized\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[medium/medium] unused-return at src/SwarmMaintenance.sol:190: SwarmMaintenance._redeem(uint256) (src/SwarmMaintenance.sol#190-194) ignores return value by poolManager.unlock() (src/SwarmMaintenance.sol#192)\n[low/medium] reentrancy-benign at src/SwarmPegHook.sol:124: Reentrancy in SwarmPegHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/SwarmPegHook.sol#124-160):\n[low/medium] reentrancy-benign at src/SwarmMaintenance.sol:190: Reentrancy in SwarmMaintenance._redeem(uint256) (src/SwarmMaintenance.sol#190-194):\n[low/medium] reentrancy-events at src/SwarmPegHook.sol:124: Reentrancy in SwarmPegHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/SwarmPegHook.sol#124-160):\n[low/medium] reentrancy-events at src/SwarmMaintenance.sol:178: Reentrancy in SwarmMaintenance.unlockCallback(bytes) (src/SwarmMaintenance.sol#178-188):\n[low/medium] timestamp at src/SwarmMaintenance.sol:144: SwarmMaintenance.passed(uint256) (src/SwarmMaintenance.sol#144-147) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmMaintenance.sol:149: SwarmMaintenance.executePayment(uint256) (src/SwarmMaintenance.sol#149-163) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmMaintenance.sol:165: SwarmMaintenance.closeJob(uint256) (src/SwarmMaintenance.sol#165-176) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmMaintenance.sol:118: SwarmMaintenance.vote(uint256,bool,uint256) (src/SwarmMaintenance.sol#118-129) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmMaintenance.sol:133: SwarmMaintenance.withdrawVote(uint256) (src/SwarmMaintenance.sol#133-142) uses timestamp for comparisons","passed":true},{"durationMs":606,"exitCode":0,"name":"aderyn","output":"[high] unsafe-casting at src/GlyphRenderer.sol:85: Unsafe Casting of integers (3 places)\n[high] weak-randomness at src/SwarmPeg.sol:81: Weak Randomness (4 places)\n[low] internal-function-used-once at src/GlyphRenderer.sol:10: Internal Function Used Only Once (2 places)\n[low] large-numeric-literal at src/LaunchConstants.sol:6: Large Numeric Literal (3 places)\n[low] literal-instead-of-constant at src/GlyphRenderer.sol:11: Literal Instead of Constant (26 places)\n[low] state-change-without-event at src/SwarmMaintenance.sol:90: State Change Without Event\n[low] unchecked-return at src/SwarmMaintenance.sol:192: Unchecked Return\n[low] uninitialized-local-variable at src/GlyphRenderer.sol:42: Uninitialized Local Variable\n[low] unused-public-function at src/SwarmPeg.sol:52: Public Function Not Used Internally","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"7a22fe0868b2be9631d6da9d9fc922f2214d35d2a437d4f5955c28345f6de91a","verifiedTreeHash":"e2ad481bcc1a0d179a2eed67d854d6abebd3deb6","verifierVersion":"0.1.0+8fbf00a5"}]}