{"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":"10902f10-444c-45f5-a995-36857b0db9f4","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"6eaf23b9ef6e2c7ca3fc5a9d4cf66eaaefd204a98daeb871fa6e50bd5c4d68e1","dependsOn":["manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","tools":[]},"key":"audit_imported_code","kind":"code","role":"review","skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","state":"accepted"},{"acceptedSubmissionHash":"00a3f5d21613322b46fbfcc27fd603c485085b98d78df5e84b6cc3321a8abc28","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"}],"objective":"Begin with an audit-imported-code step: audit both contracts, the library and the deploy script exactly as they arrived in this repository, before anything is adapted, and report findings rather than fixing them. Only then deploy the Ponzinomics ($PIMD) contracts to Robinhood Chain (4663) and open the pool, exactly as they are. Two contracts to deploy. PimdToken is an ERC-20 named Ponzinomics, symbol PIMD, with a fixed total supply of exactly 1,000,000,000 at 18 decimals, minted once in a constructor that takes no arguments, to whoever deploys it; there is no owner, no mint, no burn and no pause, and burning is a plain transfer to 0xdEaD so totalSupply never moves. PimdHook is a Uniswap V4 hook whose constructor takes the pool manager, the token address and the launch factory, so its constructorArgs are $poolManager, $token and $factory; everything else is a source constant. It does not gate the opening price: it accepts the pool the factory opens and reads the tick only for its Launched event. It taxes every trade in IMD, 2.4% on buys and 5.6% on sells, split 75% to holders and 25% to the team, taking its fees as ERC-6909 claims on the quote currency. The pool's own 1.25% fee sits on top of that tax and the hook never touches it. A third contract, PimdEngine, is already deployed at 0xFFDB5Dd341F692a29b5a0d3D1577DF683C1bACa4 and its address is baked into PimdHook as a constant: audit it as imported code, but do not deploy or redeploy it. Do not change the economics, the tax rates, the split, the tier ladder or the opening tick: they are settled, and the opening tick follows the chain policy's 2500 IMD initial market cap. If you find a defect, report it rather than redesigning around it. Three things deserve the most attention. First, the pool is only correct if IMD sorts below PIMD as currency0: beforeInitialize refuses the other ordering, refuses a second pool, refuses a wrong quote currency and refuses an opening tick more than 300 from 129000, and it is the single gate on all of it. Second, beforeAddLiquidity must let the factory's seed through exactly once and refuse every add after it, because 'third-party liquidity is blocked' is an advertised property; and beforeRemoveLiquidity must refuse every negative liquidity delta for ever while still allowing the zero delta that collects the pool's own fee. Third, the engine must never read holder weights while the PoolManager is unlocked, which is where a flash borrower would stand.","parentJobId":null,"planHash":"665dccc90ab8a85279df3bb7957587818ec475ad189e6a42092234d49b8a58c1","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"10902f10-444c-45f5-a995-36857b0db9f4","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1423-src-pimd-pimdtoken-sol-src-pimd-pimdhook"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51022","feedbackHash":"f84c7affee38f571aa64686a93483578284641ab88ef2c8ce11a509850c492df","nodeKey":"audit_imported_code","submissionHash":"6eaf23b9ef6e2c7ca3fc5a9d4cf66eaaefd204a98daeb871fa6e50bd5c4d68e1","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51756","feedbackHash":"7b5834ec47e4f387b5b7a20df072838d9bfffe9db43999c646ce1c53b3c9deef","nodeKey":"manifest","submissionHash":"00a3f5d21613322b46fbfcc27fd603c485085b98d78df5e84b6cc3321a8abc28","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"30bc3d36d7e32af8aac9b311412a72863b8363aa4c5a5ac0f3d7ee5d633d455e","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"af553d0efb67e13c5a0e6846c9d6dd368db09fc3e00e81adbdf0caee7b5c1db2","device":"83b36551000748bb","findings":[],"hash":"00a3f5d21613322b46fbfcc27fd603c485085b98d78df5e84b6cc3321a8abc28","nodeId":"149d7377-81ee-4a51-a35b-063fe7b3a773","outcome":"completed","summary":"Created [launch.json](/root/.identitymd/work/10902f10-444c-45f5-a995-36857b0db9f4/149d7377-81ee-4a51-a35b-063fe7b3a773/launch.json) with matching constructors, eight hook permissions, required pool values, and audit findings.\n\nOnly the manifest is changed for submission. `forge build` passed with warnings; all 92 tests passed, including six fork tests.\n\nNotes report token-ordering constraints, absent tick enforcement, and broader fee acceptance. No deployment was performed.","treeHash":"81a48aef963a34da1a6821f0efcc16e7e90c49bf","usage":{"cachedInputTokens":1580032,"inputTokens":81320,"model":"gpt-6-astra","outputTokens":12557,"runtime":"codex","turns":8,"wallClockMs":504483}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"c35be49d2f8f8def","findings":[{"citation":"resolved","description":"The README parameter table ('opening tick 129,000 +/- 300') and the launch brief ('refuses an opening tick more than 300 from 129000, and it is the single gate on all of it') both state that beforeInitialize refuses an opening price outside the tolerance. The constants LAUNCH_TICK (line 78) and LAUNCH_TICK_TOLERANCE (line 79) exist but nothing reads them: line 272 converts the price to a tick only to emit it in Launched. The factory, or any price the policy resolves to, opens the pool at any tick. The brief is internally inconsistent (it also says 'It does not gate the opening price'), and the hook's own comment at lines 73-77 says the gate was removed deliberately after launch 1405; this is reported so the adapter and the requester settle which statement is the design, rather than ship a README and brief that promise a check the bytecode does not make. If the policy's 2,500 IMD cap is the guarantee, the README table should say so; if the hook is meant to be the gate, the check is missing. Scope: read in full as imported, before adaptation: src/pimd/PimdToken.sol (49 lines), src/pimd/PimdHook.sol (555), src/pimd/PimdEngine.sol (880), src/libraries/L2Block.sol (21), script/DeployPimd.s.sol (118); skimmed script/PimdRehearsal.sol, script/RehearsalSeeder.sol, script/RehearsePimd.s.sol and the test suite (86 local tests pass). Reached on chain (Robinhood Chain 4663, robinhood-rpc.publicnode.com): the engine at 0xFFDB5Dd341F692a29b5a0d3D1577DF683C1bACa4, whose runtime bytecode is byte-identical to the compiled PimdEngine outside its 960 immutable bytes, and whose getters read poolManager 0x8366a39CC670B4001A1121B8F6A443A643e40951, imd 0x5F7Bb59365ce557C26dbcAa4EE9d39A4b95B7127, team = binder = 0x0960E8Bd80462e3842Bb6620c7C5289A44c4559B, bound false, drip 150 bps, minInterval 900, minBalance 1e24, fireTip 5e16, tipPerHolder 3e15, maxCatchup 21600, maxHolders 700, both script keepers enabled; the live PoolManager answers exttload on the engine's IS_UNLOCKED_SLOT, which equals keccak256('Unlocked')-1. Not reached: the launch factory ($factory is resolved at launch and is not in this tree), so whether it calls modifyLiquidity directly from its own address, makes exactly one positive add, and performs no swap in the initialize block is assumed, not verified; the IMD token's source (only its code presence was checked); v4-core and v4-periphery are trusted as vendored.","line":272,"path":"src/pimd/PimdHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IPoolManager} from \"@uniswap/v4-core/src/interfaces/IPoolManager.sol\";\nimport {PoolManager} from \"@uniswap/v4-core/src/PoolManager.sol\";\nimport {PoolKey} from \"@uniswap/v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"@uniswap/v4-core/src/types/Currency.sol\";\nimport {IHooks} from \"@uniswap/v4-core/src/interfaces/IHooks.sol\";\nimport {Hooks} from \"@uniswap/v4-core/src/libraries/Hooks.sol\";\nimport {TickMath} from \"@uniswap/v4-core/src/libraries/TickMath.sol\";\nimport {HookMiner} from \"v4-periphery/test/shared/HookMiner.sol\";\nimport {ERC20} from \"solmate/src/tokens/ERC20.sol\";\nimport {PimdToken} from \"src/pimd/PimdToken.sol\";\nimport {PimdHook} from \"src/pimd/PimdHook.sol\";\n\n/// A plain 18-decimal ERC-20 etched at the production IMD address, so the unmodified PimdHook accepts it as\n/// the quote currency.\ncontract QuoteStandIn is ERC20(\"Identity.md\", \"IMD\", 18) {}\n\n/// The README's parameter table and the launch brief both state that `beforeInitialize` refuses an opening\n/// tick more than `LAUNCH_TICK_TOLERANCE` (300) away from `LAUNCH_TICK` (129000). The constants exist but\n/// nothing reads them: the factory may open the pool at any price. This test opens it 14,580 ticks away\n/// (tick 143580, the value the stale deployment record carried) and expects the hook to refuse. On the\n/// code as it stands the initialize succeeds, so the test fails.\ncontract OpeningTickGateTest is Test {\n    address constant QUOTE = 0x5F7Bb59365ce557C26dbcAa4EE9d39A4b95B7127; // PimdHook.QUOTE_TOKEN\n    address constant FACTORY = address(0xFAC7);\n    uint160 constant HOOK_FLAGS = uint160(\n        Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.AFTER_SWAP_FLAG\n            | Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG | Hooks.AFTER_SWAP_RETURNS_DELTA_FLAG | Hooks.BEFORE_ADD_LIQUIDITY_FLAG\n            | Hooks.BEFORE_REMOVE_LIQUIDITY_FLAG\n    );\n\n    IPoolManager manager;\n    PimdToken token;\n    PimdHook hook;\n\n    function setUp() public {\n        manager = IPoolManager(address(new PoolManager(address(this))));\n        vm.etch(QUOTE, address(new QuoteStandIn()).code);\n\n        // PIMD has to sort above IMD for the hook to accept any key at all, so mine a salt that does.\n        bytes32 initHash = keccak256(type(PimdToken).creationCode);\n        for (uint256 i; i < 100_000; ++i) {\n            address predicted = vm.computeCreate2Address(bytes32(i), initHash, address(this));\n            if (uint160(predicted) > uint160(QUOTE)) {\n                token = new PimdToken{salt: bytes32(i)}();\n                break;\n            }\n        }\n        require(address(token) != address(0), \"no salt\");\n\n        bytes memory args = abi.encode(manager, address(token), FACTORY);\n        (address hookAddr, bytes32 salt) = HookMiner.find(address(this), HOOK_FLAGS, type(PimdHook).creationCode, args);\n        hook = new PimdHook{salt: salt}(manager, address(token), FACTORY);\n        require(address(hook) == hookAddr, \"hook addr\");\n    }\n\n    function _key() internal view returns (PoolKey memory) {\n        return PoolKey({\n            currency0: Currency.wrap(QUOTE),\n            currency1: Currency.wrap(address(token)),\n            fee: 12_500,\n            tickSpacing: 60,\n            hooks: IHooks(address(hook))\n        });\n    }\n\n    function test_opening_tick_outside_tolerance_is_refused() public {\n        int24 farTick = hook.LAUNCH_TICK() + hook.LAUNCH_TICK_TOLERANCE() + 14_280; // 143580\n        vm.prank(FACTORY);\n        vm.expectRevert();\n        manager.initialize(_key(), TickMath.getSqrtPriceAtTick(farTick));\n        assertFalse(hook.launched(), \"a pool 14,580 ticks from LAUNCH_TICK must not open\");\n    }\n}","reproduction":"State: PimdHook deployed with factory F, PIMD sorting above IMD. Input: F calls PoolManager.initialize(key{IMD, PIMD, fee 12500, spacing 60, hook}, TickMath.getSqrtPriceAtTick(143580)) (the tick the stale deployment record carried; 14,580 ticks from LAUNCH_TICK, about a 580 IMD market cap instead of 2,500). Expected per README/brief: revert, pool not opened. Actual: initialize succeeds, launched == true, Launched(id, 143580) emitted, and the pool is open at that price for ever (a second initialize is refused). Proof test test/scratch/OpeningTickGate.t.sol fails on this code with 'next call did not revert as expected'.","severity":"medium","snippet":"        int24 tick = TickMath.getTickAtSqrtPrice(sqrtPriceX96);","title":"beforeInitialize does not enforce the advertised 129000 +/- 300 opening-tick gate; LAUNCH_TICK_TOLERANCE is dead"},{"citation":"resolved","description":"Every fee calculation assumes IMD is currency0, so beforeInitialize requires currency1 == PIMD (line 259) and currency0 == IMD (line 262). The PoolManager requires currency0 < currency1. If the token address the factory deploys is numerically below IMD (0x5F7Bb593...), the only key the PoolManager accepts has PIMD as currency0 and is refused by line 259, and the key the hook wants is refused by the PoolManager with CurrenciesOutOfOrderOrEqual. Nothing in this tree influences the token address: the token takes no constructor argument and the factory deploys it with plain CREATE; the CREATE2 salt mining exists only in test/pimd/PimdBase.t.sol and script/PimdRehearsal.sol. The probability that a uniformly distributed address sorts below 0x5F7B... is about 0x5F7B/0xFFFF = 37%. The README acknowledges this as 'a retry condition'. It is reported because a paid launch whose pool can never open is a broken launch, and the adapter must either secure the ordering at the factory (salt or retry) or accept that roughly one launch attempt in three is lost.","line":259,"path":"src/pimd/PimdHook.sol","reproduction":"State: factory deploys PimdToken at any address T with uint160(T) < uint160(0x5F7Bb59365ce557C26dbcAa4EE9d39A4b95B7127), then the hook. Input A: initialize(key{currency0: IMD, currency1: T}) -> PoolManager reverts CurrenciesOutOfOrderOrEqual before the hook runs. Input B: initialize(key{currency0: T, currency1: IMD}) -> hook reverts PoolMustPairToken (line 259). Expected: a launch that always opens. Actual: no input opens the pool; launched stays false and the deployed token and hook are dead.","severity":"medium","snippet":"        if (c1 != address(token)) revert PoolMustPairToken();","title":"Launch fails closed whenever the factory's CREATE lands PIMD below IMD: no PoolKey exists that beforeInitialize accepts"},{"citation":"resolved","description":"The hook pushes 75% of every tax to the constant ENGINE_ADDRESS unconditionally (flush/flushHolders). Which token's balances that IMD is dripped by is set after deployment by bind(), callable once by binder, which on the live engine is the team wallet 0x0960E8Bd80462e3842Bb6620c7C5289A44c4559B (bound == false today). bind validates hook_ only through four getters it calls on hook_ itself (lines 286-289), so any contract the binder writes passes, and token_ is then whatever that contract returns. The binder may also pass alsoExclude, which marks arbitrary addresses as never payable with no way to reverse it. The launch reference lists 'anything configured after deployment rather than in a constructor' as a medium for the adapter; here it cannot be removed because the engine is already deployed and is not to be redeployed, so it must be recorded as a trust assumption on the team wallet: the party that receives the 25% slice also holds a one-shot power over the 75% slice. Two concrete states follow. (a) Before bind, every fire() reverts NotBound while flush() keeps delivering IMD to the engine; nothing but bind can ever release it (no owner, no rescue). (b) A bind to a binder-controlled token sends every future drip to holders of that token. Scratch test test/scratch/ZZBindTrust.t.sol reproduces (b): after binding a FakeHook whose token() is BinderCoin, one epoch over a 1,000 IMD pot paid 113.89 IMD to the BinderCoin holder.","line":286,"path":"src/pimd/PimdEngine.sol","reproduction":"State: live engine (bound false, binder 0x0960...). Input: binder deploys FakeHook with engine()=engine, quote()=IMD, token()=BinderCoin, poolManager()=0x8366...; calls engine.bind(BinderCoin, FakeHook, []). Expected: only the real PimdHook/PimdToken pair can be bound. Actual: bind succeeds (only getters are compared); engine.token == BinderCoin; register([sink holding 1e24 BinderCoin]); after the real hook's flush delivers 1,000 IMD and 2 hours pass: fire(), tally(700) by a keeper, pay(700) -> sink receives 113.89 IMD; every later drip goes the same way. Alternative input: bind(PIMD, PimdHook, [anyHolder]) -> anyHolder is excluded from every drip for ever. Alternative state: binder never calls bind -> fire() reverts NotBound indefinitely while hook.flush() keeps moving IMD into the engine.","severity":"medium","snippet":"        if (h.engine() != address(this)) revert BadConfig();","title":"Engine is configured after deployment by bind(): the binder (team wallet) can point it at any token and hook, redirecting all holder IMD, or exclude any holder for ever; until it acts, holder IMD accu"},{"citation":"resolved","description":"The brief fixes the engine at 0xFFDB5Dd341F692a29b5a0d3D1577DF683C1bACa4 and says not to deploy or redeploy it. The only deploy script in the tree does exactly that: it always creates a new PimdEngine (line 88), takes TEAM_MULTISIG, BINDER, POOL_MANAGER, IMD, KEEPER_A/B, DRIP_BPS, MIN_INTERVAL, MIN_BALANCE, FIRE_TIP, TIP_PER_HOLDER, MAX_CATCHUP and MAX_HOLDERS from env vars without echoing them against intent, and makes the EXPECT_ENGINE guard optional (line 97, default address(0) disables it). Its closing console lines instruct the operator to write the new address into PimdHook.ENGINE_ADDRESS, which changes the hook's bytecode. Any engine it deploys is unusable for this launch: PimdEngine.bind requires hook.engine() == address(this) and the hook's constant names the live engine.","line":88,"path":"script/DeployPimd.s.sol","reproduction":"Input: PRIVATE_KEY=<k> forge script script/DeployPimd.s.sol:DeployPimd --rpc-url robinhood --broadcast, with no EXPECT_ENGINE set. Expected: nothing deployed (the engine exists). Actual: a new PimdEngine E2 is deployed; E2.bind(token, hook, [...]) reverts BadConfig at line 286 because hook.engine() == 0xFFDB... != E2; the script prints 'write this address into PimdHook.ENGINE_ADDRESS'. With MIN_BALANCE=1 DRIP_BPS=5000 in the environment the same command deploys an engine with those values and prints them as deployed.","severity":"low","snippet":"        PimdEngine engine = new PimdEngine(ec);","title":"DeployPimd.s.sol deploys a fresh engine that the hook can never bind to, and lets every economic parameter be overridden from the environment"},{"citation":"resolved","description":"The rehearsal script hard-codes fee 0 in the PoolKey it swaps against. beforeInitialize refuses fee 0 (UnsupportedFeeTier), the manifest opens the pool at 12500, and PoolId is a hash of the full key, so every router.swap in this script targets a pool that does not exist. It also reads deployments.pimd.4663.json through vm.readFile (line 24) while foundry.toml deliberately grants no fs_permissions, so the script cannot run in this configuration at all. Low: it is rehearsal tooling, not launch bytecode, but it would report a false failure against a correctly opened pool.","line":34,"path":"script/RehearsePimd.s.sol","reproduction":"Input: forge script script/RehearsePimd.s.sol --rpc-url robinhood after a pool opened with fee 12500. Expected: three buys and a sell against the launched pool. Actual: vm.readFile reverts for lack of fs_permissions; with permissions granted, router.swap(key{fee: 0}) reverts PoolNotInitialized because toId(key with fee 0) != toId(key with fee 12500).","severity":"low","snippet":"            fee: 0,","title":"RehearsePimd.s.sol builds the PoolKey with fee 0, which is not the launched pool, and needs fs permissions foundry.toml forbids"},{"citation":"resolved","description":"callerTip is a 0.01 IMD constant capped at TIP_MAX_BPS (20%) of teamOwed (lines 459-460). Both caps are constants with no owner to adjust them. A caller who flushes after every trade always receives the smaller of 0.01 IMD and 20% of what the team is owed, so for any trade below 1.667 IMD of buys or 0.714 IMD of sells it is exactly 20% of the team slice, and the team's realised share of the tax becomes 20% rather than the 25% the README advertises. Holders are unaffected. The code comment at lines 450-453 shows this is the chosen trade-off; recorded so the economics owner confirms it.","line":458,"path":"src/pimd/PimdHook.sol","reproduction":"State: launched, past init block. Input: alice buys 1 IMD exact-in -> fee 0.024 IMD, teamOwed 0.006. bot calls flush(). Expected per README: team receives 0.006 IMD. Actual: tip = min(0.01, 0.006 * 2000 / 10000) = 0.0012 to bot, team receives 0.0048; repeated after every trade the team collects 80% of its slice.","severity":"info","snippet":"        uint256 tip = msg.sender == engine() ? 0 : callerTip;","title":"flush() pays an unprivileged caller up to 20% of the team's slice on every call, so a bot flushing after each trade lowers the team's share from 25% to 20% of tax"}],"hash":"6eaf23b9ef6e2c7ca3fc5a9d4cf66eaaefd204a98daeb871fa6e50bd5c4d68e1","nodeId":"a9c06db2-d17c-4121-a494-66b21a79c864","outcome":"completed","summary":"The review is complete. `.imd-findings.json` holds six findings, one proof test, and a 25-row coverage record covering all 20 listed entry points plus five invariants.\n\n**What was read and reached**\n\n- Read in full as imported: PimdToken, PimdHook, PimdEngine, L2Block, and DeployPimd.s.sol. The three rehearsal scripts and the test suite were skimmed. The 86 local tests pass.\n- Reached on chain: the baked-in engine at `0xFFDB…bACa4`. Its runtime bytecode is byte-identical to the compiled source outside immutable slots, and its getters match this launch's PoolManager, IMD, team wallet as binder, and the production parameters. It is unbound. The live PoolManager answers `exttload` on the engine's unlock slot, which equals `keccak256(\"Unlocked\") - 1`.\n- Not reached: the launch factory, which is resolved at launch and is not in the tree. Its direct `modifyLiquidity` call, single positive add, and absence of a swap in the initialize block are assumptions.\n\n**Findings**\n\n1. **Medium, PimdHook.sol:272.** The advertised 129000 ± 300 opening-tick gate does not exist. The constants are declared but never read. A proof test opens the pool at tick 143580 and fails on this code as intended.\n2. **Medium, PimdHook.sol:259.** When the factory's CREATE puts PIMD below IMD, no PoolKey can be accepted by both the PoolManager and the hook, so the launch is dead. Roughly 37% of addresses sort below IMD and nothing in the tree influences the token address.\n3. **Medium, PimdEngine.sol:286.** `bind` is post-deployment configuration by the team wallet. It validates the hook only through getters on the hook itself, so the binder can bind a token it controls and redirect all holder IMD, or exclude any holder for ever. A scratch test reproduced the redirect. Until bind is called, `fire` reverts while IMD keeps arriving. Recorded as a trust assumption since the engine cannot be redeployed.\n4. **Low, DeployPimd.s.sol:88.** The script always deploys a fresh engine the hook can never bind to, accepts every economic parameter from the environment, and tells the operator to edit the hook's constant.\n5. **Low, RehearsePimd.s.sol:34.** The rehearsal script targets a fee-0 key that is not the launched pool and needs file permissions the config forbids.\n6. **Info, PimdHook.sol:458.** A bot flushing after every trade takes 20% of the team's slice, lowering the team's realised share from 25% to 20% of tax. The code comments show this was a deliberate choice.\n\n**Verified sound**\n\nThe three points the brief flagged hold apart from the tick gate. `beforeInitialize` refuses the caller, a second pool, the wrong pairing, the wrong quote, and unsupported fees. `beforeAddLiquidity` admits the factory once. `beforeRemoveLiquidity` refuses every negative delta and admits zero. All five balance-reading engine entry points revert `PoolUnlocked` inside an unlock, confirmed with a scratch test in the phase where that guard decides. All four swap shapes charge exactly 2.4% and 5.6% of the gross IMD leg with a net-zero hook delta. The token is an unmodified solmate ERC-20 with no mint, burn, pause, or owner.\n\nNo source file was changed. Scratch tests live under `test/scratch/`, which is discarded before submission.","treeHash":null,"usage":{"cachedInputTokens":2931301,"inputTokens":610,"model":"claude-fable-5-1","outputTokens":54255,"runtime":"claude","turns":31,"wallClockMs":1162877}}],"verification":[{"checks":[{"durationMs":72536,"exitCode":0,"name":"build","output":"Compiling 90 files with Solc 0.8.26\nSolc 0.8.26 finished in 72.37s\nCompiler run successful with warnings:\nWarning (2018): Function state mutability can be restricted to pure\n   --> test/pimd/PimdFixes.t.sol:684:5:\n    |\n684 |     function _maxHolders() internal view override returns (uint256) {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nWarning (2018): Function state mutability can be restricted to pure\n   --> test/pimd/PimdFixes.t.sol:939:5:\n    |\n939 |     function _maxHolders() internal view override returns (uint256) {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nwarning[return-bomb]: external call with a gas limit may copy unbounded return data\n   ╭▸ src/libraries/L2Block.sol:17:39\n   │\n17 │         (bool ok, bytes memory ret) = ARBSYS.staticcall{gas: ARBSYS_GAS}(abi.encodeWithSignature(\"arbBlockNumber()\"));\n   │                                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/return-bomb\n\nwarning[missing-events-arithmetic]: `totalWeight` is changed without an event but is used in arithmetic\n    ╭▸ src/pimd/PimdEngine.sol:541:9\n    │\n541 │         totalWeight = tw;\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `cursor` is changed without an event but is used in arithmetic\n    ╭▸ src/pimd/PimdEngine.sol:542:9\n    │\n542 │         cursor = end;\n    │         ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `tipBudget` is changed without an event but is used in arithmetic\n    ╭▸ src/pimd/PimdEngine.sol:767:9\n    │\n767 │         tipBudget = budget - amt;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `pot` is changed without an event but is used in arithmetic\n    ╭▸ src/pimd/PimdEngine.sol:768:9\n    │\n768 │         pot -= amt;\n    │         ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/pimd/PimdEngine.sol:264:45\n    │\n264 │             if (c.keepers[i] == address(0)) revert BadConfig();\n    │                                             ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/pimd/PimdHook.sol:280:9\n    │\n280 │         emit Launched(key.toId(), tick);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:278:21\n    │\n278 │         initBlock = uint64(L2Block.number());\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:279:23\n    │\n279 │         launchStart = uint64(block.timestamp);\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/pimd/PimdHook.sol:308:28\n    │\n308 │         _tstore(_FEE_SLOT, fee);\n    │                            ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:300:46\n    │\n300 │                 params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:300:81\n    │\n300 │                 params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:309:63\n    │\n309 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(uint128(fee)), 0), 0);\n    │                                                               ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:309:70\n    │\n309 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(uint128(fee)), 0), 0);\n    │                                                                      ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/pimd/PimdHook.sol:362:44\n    │\n362 │         return (IHooks.afterSwap.selector, hookDeltaUnspecified);\n    │                                            ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/pimd/PimdHook.sol:351:24\n    │\n351 │                     && block.timestamp < uint256(launchStart) + launchCapSeconds\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/pimd/PimdHook.sol:352:24\n    │\n352 │                     && block.timestamp < uint256(launchStart) + LAUNCH_CAP_MAX_SECONDS\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/pimd/PimdHook.sol:530:9\n    │\n530 │         emit FeeTaken(isBuy, fee);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:330:44\n    │\n330 │             uint256 movedQuote = a0q < 0 ? uint256(uint128(-a0q)) : uint256(uint128(a0q));\n    │                                            ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:330:52\n    │\n330 │             uint256 movedQuote = a0q < 0 ? uint256(uint128(-a0q)) : uint256(uint128(a0q));\n    │                                                    ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:330:69\n    │\n330 │             uint256 movedQuote = a0q < 0 ? uint256(uint128(-a0q)) : uint256(uint128(a0q));\n    │                                                                     ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:330:77\n    │\n330 │             uint256 movedQuote = a0q < 0 ? uint256(uint128(-a0q)) : uint256(uint128(a0q));\n    │                                                                             ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:338:41\n    │\n338 │             uint256 quoteAmt = a0 < 0 ? uint256(uint128(-a0)) : uint256(uint128(a0));\n    │                                         ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:338:49\n    │\n338 │             uint256 quoteAmt = a0 < 0 ? uint256(uint128(-a0)) : uint256(uint128(a0));\n    │                                                 ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:338:65\n    │\n338 │             uint256 quoteAmt = a0 < 0 ? uint256(uint128(-a0)) : uint256(uint128(a0));\n    │                                                                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:338:73\n    │\n338 │             uint256 quoteAmt = a0 < 0 ? uint256(uint128(-a0)) : uint256(uint128(a0));\n    │                                                                         ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:342:36\n    │\n342 │             hookDeltaUnspecified = int128(uint128(fee));\n    │                                    ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:342:43\n    │\n342 │             hookDeltaUnspecified = int128(uint128(fee));\n    │                                           ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:354:33\n    │\n354 │                 uint256 spent = uint256(uint128(-delta.amount0())) + fee;\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdHook.sol:354:41\n    │\n354 │                 uint256 spent = uint256(uint128(-delta.amount0())) + fee;\n    │                                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_lock` is updated\n    ╭▸ src/pimd/PimdHook.sol:435:9\n    │\n435 │         poolManager.unlock(abi.encode(ACTION_HOLDERS, toHolders));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/pimd/PimdHook.sol:439:9\n    │\n439 │         emit Flushed(msg.sender, toHolders, 0, 0);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/pimd/PimdHook.sol:435:9\n    │\n435 │         poolManager.unlock(abi.encode(ACTION_HOLDERS, toHolders));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_lock` is updated\n    ╭▸ src/pimd/PimdHook.sol:465:9\n    │\n465 │         poolManager.unlock(abi.encode(ACTION_FLUSH, toHolders, toTeam - tip, msg.sender, tip));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/pimd/PimdHook.sol:473:9\n    │\n473 │         emit Flushed(msg.sender, toHolders, toTeam, tip);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/pimd/PimdHook.sol:465:9\n    │\n465 │         poolManager.unlock(abi.encode(ACTION_FLUSH, toHolders, toTeam - tip, msg.sender, tip));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/pimd/PimdHook.sol:520:16\n    │\n520 │             && block.timestamp < uint256(launchStart) + launchCapSeconds\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/pimd/PimdHook.sol:521:16\n    │\n521 │             && block.timestamp < uint256(launchStart) + LAUNCH_CAP_MAX_SECONDS;\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `locked` is updated\n    ╭▸ src/pimd/PimdEngine.sol:329:17\n    │\n329 │         require(imd.transferFrom(msg.sender, address(this), amount));\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/pimd/PimdEngine.sol:332:9\n    │\n332 │         emit Seeded(msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/pimd/PimdEngine.sol:350:27\n    │\n350 │             uint256 bal = IERC20Min(token).balanceOf(a);\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/pimd/PimdEngine.sol:807:17\n    │\n807 │             if (IERC20Min(token).balanceOf(a) < minBalance || _shapeChanged(a, _holder[a])) {\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/pimd/PimdEngine.sol:792:9\n    │\n792 │         emit Pruned(a);\n    │         ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/pimd/PimdEngine.sol:367:17\n    │\n367 │                 emit RegistrationDeferred(a, reclaimCursor);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/pimd/PimdEngine.sol:378:13\n    │\n378 │             emit Registered(a);\n    │             ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdEngine.sol:372:24\n    │\n372 │             h.index1 = uint32(holders.length);\n    │                        ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdEngine.sol:376:25\n    │\n376 │             h.lastBal = uint128(bal);\n    │                         ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdEngine.sol:377:29\n    │\n377 │             h.streakStart = uint64(block.timestamp); // the clock starts at registration, never earlier\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/pimd/PimdEngine.sol:403:17\n    │\n403 │             if (IERC20Min(token).balanceOf(a) >= minBalance && !_shapeChanged(a, _holder[a]) && !_isPool(a)) continue;\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-no-eth]: external call can be reentered before `phase` is updated\n    ╭▸ src/pimd/PimdEngine.sol:423:17\n    │\n423 │             try hook.flush() {}\n    │                 ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `phase` is updated\n    ╭▸ src/pimd/PimdEngine.sol:428:21\n    │\n428 │                 try hook.flushHolders() {} catch {}\n    │                     ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/pimd/PimdEngine.sol:415:13\n    │\n415 │         if (block.timestamp < lastFire + minInterval) revert TooSoon();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/pimd/PimdEngine.sol:719:9\n    │\n719 │         emit Income(income);\n    │         ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/pimd/PimdEngine.sol:443:9\n    │\n443 │         emit Fired(epoch + 1, drip, n);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `pot` is updated\n    ╭▸ src/pimd/PimdEngine.sol:751:13\n    │\n751 │             address(imd).call{gas: SEND_GAS}(abi.encodeCall(IERC20Min.transfer, (to, amount)));\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/pimd/PimdEngine.sol:538:32\n    │\n538 │             h.weight = uint128(w);\n    │                                ━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/pimd/PimdEngine.sol:523:27\n    │\n523 │             uint256 bal = IERC20Min(token).balanceOf(a);\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdEngine.sol:526:33\n    │\n526 │                 h.streakStart = uint64(nowTs); // sold or sent out: the clock restarts\n    │                                 ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdEngine.sol:529:33\n    │\n529 │                 h.streakStart = uint64((last * h.streakStart + (bal - last) * nowTs) / bal);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdEngine.sol:535:25\n    │\n535 │             h.lastBal = uint128(bal);\n    │                         ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdEngine.sol:538:24\n    │\n538 │             h.weight = uint128(w);\n    │                        ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `cursor` is updated\n    ╭▸ src/pimd/PimdEngine.sol:751:13\n    │\n751 │             address(imd).call{gas: SEND_GAS}(abi.encodeCall(IERC20Min.transfer, (to, amount)));\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/pimd/PimdEngine.sol:593:19\n    │\n593 │                 ++paidHolders;\n    │                   ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/pimd/PimdEngine.sol:592:17\n    │\n592 │                 paid += amt;\n    │                 ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/pimd/PimdEngine.sol:751:13\n    │\n751 │             address(imd).call{gas: SEND_GAS}(abi.encodeCall(IERC20Min.transfer, (to, amount)));\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/pimd/PimdEngine.sol:596:17\n    │\n596 │                 emit PayoutFailed(a, amt);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/pimd/PimdEngine.sol:612:13\n    │\n612 │             emit EpochPaid(epoch, epochPaidHolders == 0 ? 0 : total - leftover, epochPaidHolders);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdEngine.sol:591:31\n    │\n591 │                 h.received += uint128(amt);\n    │                               ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/pimd/PimdEngine.sol:646:13\n    │\n646 │         if (block.timestamp < lastFire + ABORT_DELAY) revert TooSoon();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[return-bomb]: external call with a gas limit may copy unbounded return data\n    ╭▸ src/pimd/PimdEngine.sol:751:13\n    │\n751 │             address(imd).call{gas: SEND_GAS}(abi.encodeCall(IERC20Min.transfer, (to, amount)));\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/return-bomb\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/pimd/PimdEngine.sol:789:32\n    │\n789 │         _holder[last].index1 = uint32(idx0 + 1);\n    │                                ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/pimd/PimdBase.t.sol:275:17\n    │\n275 │         vm.warp(block.timestamp + hook.launchCapSeconds() + 1);\n    │         ────────━━━━━━━━━━━━━━━─────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/pimd/Pimd.t.sol:195:17\n    │\n195 │         vm.warp(block.timestamp + 15 days); // alice reaches 3x\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/pimd/Pimd.t.sol:237:17\n    │\n237 │         vm.warp(block.timestamp + 2 days); // alice is past the first hour, and this epoch clears the backlog\n    │         ────────━━━━━━━━━━━━━━━────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/pimd/PimdAttacks.t.sol:352:17\n    │\n352 │         vm.warp(block.timestamp + 2 days);\n    │         ────────━━━━━━━━━━━━━━━────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/pimd/PimdFork.t.sol:236:17\n    │\n236 │         vm.warp(block.timestamp + 601);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":2499,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/pimd/PimdFixes.t.sol:PimdReclaimCursorTest\n[PASS] test_a_dead_slot_behind_eight_live_ones_is_still_reclaimed() (gas: 2397157)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.12s (658.99µs CPU time)\n\nRan 32 tests for test/pimd/PimdFixes.t.sol:PimdFixesTest\n[PASS] test_a_7702_delegated_wallet_keeps_earning_and_its_streak() (gas: 1967115)\n[PASS] test_a_bag_that_arrives_just_before_the_tally_carries_no_weight() (gas: 2309640)\n[PASS] test_a_deferred_registration_says_so() (gas: 1898392)\n[PASS] test_a_fire_that_opens_no_epoch_emits_nothing() (gas: 839978)\n[PASS] test_a_fresh_buy_earns_from_the_next_epoch() (gas: 2463778)\n[PASS] test_a_null_epoch_pays_no_per_holder_tip() (gas: 1989243)\n[PASS] test_a_null_epoch_pays_the_firer_nothing() (gas: 1675717)\n[PASS] test_a_pay_page_cannot_take_more_than_its_share_of_the_tips() (gas: 3468786)\n[PASS] test_a_refused_team_transfer_does_not_stop_the_drip() (gas: 1460076)\n[PASS] test_a_stuck_epoch_can_be_abandoned_and_the_imd_comes_back() (gas: 1662026)\n[PASS] test_a_walked_bag_cannot_lock_the_holder_set_for_ever() (gas: 1167001)\n[PASS] test_abort_is_refused_when_no_epoch_is_open() (gas: 97252)\n[PASS] test_an_engine_with_no_keeper_cannot_be_deployed() (gas: 13974)\n[PASS] test_an_unbounded_holder_set_cannot_be_configured() (gas: 13371)\n[PASS] test_bind_refuses_a_token_the_hook_does_not_tax() (gas: 57104)\n[PASS] test_everything_except_tally_stays_permissionless() (gas: 1543866)\n[PASS] test_fire_does_not_tip_the_engine_out_of_the_team_slice() (gas: 1401450)\n[PASS] test_firing_into_an_empty_holder_set_pays_no_tip() (gas: 880400)\n[PASS] test_only_a_keeper_can_tally() (gas: 1285050)\n[PASS] test_pay_is_funded_even_when_the_firer_could_take_the_lot() (gas: 2836198)\n[PASS] test_prune_cannot_brick_a_running_epoch() (gas: 2375989)\n[PASS] test_prune_is_refused_during_pay_too() (gas: 1231303)\n[PASS] test_prune_refuses_while_the_pool_is_unlocked() (gas: 1146391)\n[PASS] test_prune_still_works_between_epochs() (gas: 1162415)\n[PASS] test_real_code_still_voids_the_vetted_shape() (gas: 910282)\n[PASS] test_register_refuses_while_the_pool_is_unlocked() (gas: 1037823)\n[PASS] test_the_firer_is_paid_not_the_tallier() (gas: 1254128)\n[PASS] test_the_holder_bound_fits_the_chains_per_transaction_budget() (gas: 14237)\n[PASS] test_the_holder_set_is_bounded() (gas: 1905902)\n[PASS] test_the_keeper_tip_cannot_take_the_whole_team_slice() (gas: 747118)\n[PASS] test_the_launch_factory_and_precompiles_can_never_register() (gas: 746476)\n[PASS] test_total_to_team_matches_what_the_team_received() (gas: 760503)\nSuite result: ok. 32 passed; 0 failed; 0 skipped; finished in 1.14s (43.30ms CPU time)\n\nRan 1 test for test/pimd/Gas.t.sol:GasTest\n[PASS] test_epoch_gas_for_100_holders() (gas: 45150869)\nLogs:\n  fire             408551\n  tally(100)       3489228\n  pay(100)         3562883\n  epoch total      7460662\n  per holder       70521\n  hook.flush       130242\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.15s (26.04ms CPU time)\n\nRan 6 tests for test/pimd/PimdFork.t.sol:PimdForkTest\n[PASS] test_fork_buy_takes_tax_in_real_imd() (gas: 665037)\n[PASS] test_fork_engine_drips_real_imd_to_a_holder() (gas: 1542262)\nLogs:\n  IMD paid to one holder (wei) 5621280779456078301\n\n[PASS] test_fork_flush_moves_real_imd() (gas: 892000)\n[PASS] test_fork_imd_is_a_plain_erc20_with_no_transfer_fee() (gas: 286859)\n[PASS] test_fork_launch_against_real_state() (gas: 41093)\n[PASS] test_fork_sell_takes_tax_in_real_imd() (gas: 945217)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 1.96s (1.08s CPU time)\n\nRan 38 tests for test/pimd/Pimd.t.sol:PimdTest\n[PASS] test_a_partially_filled_buy_is_refused_rather_than_overtaxed() (gas: 781742)\n[PASS] test_a_second_liquidity_add_is_refused() (gas: 448429)\n[PASS] test_a_second_pool_on_this_hook_is_refused() (gas: 47553)\n[PASS] test_a_stranger_cannot_squat_the_seed() (gas: 192940606)\n[PASS] test_a_token_below_imd_can_never_open_the_pool() (gas: 6810117)\n[PASS] test_an_excluded_holder_is_never_registered_or_paid() (gas: 638866)\n[PASS] test_bind_is_one_shot() (gas: 38595)\n[PASS] test_buy_pays_the_buy_tax_in_imd() (gas: 557742)\n[PASS] test_drip_releases_about_four_percent_per_fifteen_minutes() (gas: 2330951)\n[PASS] test_even_the_factory_gets_only_one_add() (gas: 63959)\n[PASS] test_exact_out_buy_is_taxed_too() (gas: 853762)\n[PASS] test_exact_out_sell_is_taxed_too() (gas: 820044)\n[PASS] test_fee_collection_is_still_allowed() (gas: 690232)\n[PASS] test_first_hour_pays_nobody() (gas: 1151844)\n[PASS] test_flush_payouts_moves_real_imd() (gas: 771946)\n[PASS] test_holder_is_paid_in_imd_after_a_day() (gas: 1447209)\n[PASS] test_hook_refuses_raw_eth() (gas: 36923)\n[PASS] test_initialize_accepts_the_briefed_price() (gas: 192505374)\n[PASS] test_initialize_does_not_gate_the_opening_price() (gas: 192510162)\n[PASS] test_initialize_refuses_a_pool_that_is_not_our_token() (gas: 193230271)\n[PASS] test_initialize_refuses_a_quote_that_is_not_imd() (gas: 193230307)\n[PASS] test_initialize_refuses_an_unsupported_fee_tier() (gas: 192333054)\n[PASS] test_keeper_tip_comes_out_of_the_team_slice() (gas: 764376)\n[PASS] test_launch_cap_expires() (gas: 879760)\n[PASS] test_launch_cap_limits_one_buyer() (gas: 748633)\n[PASS] test_launch_puts_the_seed_in_one_range() (gas: 35421)\n[PASS] test_launch_sets_imd_as_currency0() (gas: 9827)\n[PASS] test_liquidity_can_never_be_removed_even_by_its_owner() (gas: 137625)\n[PASS] test_longer_holder_gets_a_bigger_share() (gas: 1931077)\n[PASS] test_only_the_launch_factory_may_open_the_pool() (gas: 192333909)\n[PASS] test_pool_starts_with_no_imd() (gas: 10156)\n[PASS] test_sell_pays_the_sell_tax_in_imd() (gas: 831485)\n[PASS] test_selling_restarts_the_streak() (gas: 1448330)\n[PASS] test_tax_splits_seventy_five_twenty_five() (gas: 561822)\n[PASS] test_the_imd_address_is_never_registered() (gas: 644162)\n[PASS] test_the_tax_is_only_holders_and_team() (gas: 545056)\n[PASS] test_the_token_address_is_never_registered_or_paid() (gas: 1866914)\n[PASS] test_third_party_liquidity_is_blocked() (gas: 1605784)\nSuite result: ok. 38 passed; 0 failed; 0 skipped; finished in 2.36s (7.99s CPU time)\n\nRan 14 tests for test/pimd/PimdAttacks.t.sol:PimdAttacksTest\n[PASS] test_a_holder_that_turns_pool_shaped_earns_nothing_at_once() (gas: 2098917)\n[PASS] test_a_holder_that_turns_pool_shaped_is_prunable() (gas: 1806634)\n[PASS] test_a_long_gap_still_releases_only_a_slice() (gas: 1470594)\n[PASS] test_a_pool_like_contract_is_not_paid() (gas: 1431205)\n[PASS] test_a_stranger_can_deploy_it_and_hold_nothing() (gas: 91050)\n[PASS] test_a_wallet_that_grows_code_is_not_stuck_at_zero() (gas: 2745684)\n[PASS] test_dust_holder_is_skipped_and_prunable() (gas: 742656)\n[PASS] test_fire_refuses_while_the_pool_is_unlocked() (gas: 1096957)\n[PASS] test_many_holders_weigh_whole_and_pay_across_pages() (gas: 54348333)\n[PASS] test_one_bag_cannot_be_weighed_twice_across_wallets() (gas: 1835876)\n[PASS] test_one_refused_holder_does_not_stop_the_others() (gas: 2399093)\n[PASS] test_pay_refuses_while_the_pool_is_unlocked() (gas: 1830241)\n[PASS] test_tally_refuses_while_the_pool_is_unlocked() (gas: 1655768)\n[PASS] test_team_is_excluded_from_drips() (gas: 26127)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 2.36s (58.73ms CPU time)\n\nRan 6 test suites in 2.37s (10.08s CPU time): 92 tests passed, 0 failed, 0 skipped (92 total tests)\n","passed":true},{"durationMs":87,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"PimdEngine.abortEpoch()\",\"PimdEngine.bind(address,address,address[])\",\"PimdEngine.fire()\",\"PimdEngine.pay(uint256)\",\"PimdEngine.prune(address[])\",\"PimdEngine.register(address[])\",\"PimdEngine.seed(uint256)\",\"PimdEngine.tally(uint256)\",\"PimdHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"PimdHook.beforeAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"PimdHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"PimdHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"PimdHook.flush()\",\"PimdHook.flushHolders()\",\"PimdHook.receive()\",\"PimdHook.unlockCallback(bytes)\",\"PimdToken.approve(address,uint256)\",\"PimdToken.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"PimdToken.transfer(address,uint256)\",\"PimdToken.transferFrom(address,address,uint256)\"],\"files\":{\".audit/HOT_SPOTS.md\":65,\".audit/REVIEW.md\":218,\".audit/context/01-access-control.md\":612,\".audit/context/02-swap-accounting.md\":568,\".audit/context/03-engine-lifecycle.md\":559,\".audit/context/04-arithmetic.md\":585,\".audit/context/05-reentrancy-external.md\":605,\".audit/context/06-tests-and-deploy.md\":829,\".audit/poc/ZZArith.t.sol\":188,\".audit/poc/ZZArith2.t.sol\":78,\".gitignore\":5,\"LICENSE\":21,\"README.md\":160,\"foundry.toml\":35,\"launch.json\":34,\"script/DeployPimd.s.sol\":118,\"script/PimdRehearsal.sol\":121,\"script/RehearsalSeeder.sol\":71,\"script/RehearsePimd.s.sol\":93,\"src/libraries/L2Block.sol\":21,\"src/pimd/PimdEngine.sol\":880,\"src/pimd/PimdHook.sol\":555,\"src/pimd/PimdToken.sol\":49,\"test/pimd/Gas.t.sol\":46,\"test/pimd/Pimd.t.sol\":619,\"test/pimd/PimdAttacks.t.sol\":420,\"test/pimd/PimdBase.t.sol\":409,\"test/pimd/PimdFixes.t.sol\":1003,\"test/pimd/PimdFork.t.sol\":265},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"00a3f5d21613322b46fbfcc27fd603c485085b98d78df5e84b6cc3321a8abc28","verifiedTreeHash":"81a48aef963a34da1a6821f0efcc16e7e90c49bf","verifierVersion":"0.1.0+fb8278c5"}]}