{"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":"6bb2d5c6-a7a3-40b8-b009-683816d38f07","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"06ae619e7afdcc51d2af01f5a60d3f1e37655bf885cba1e74d5d55a4622de5e1","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":"abdb0e15407124aabbbb32ce17a5d586eba0fb33b4149447193b4947c3c72ad6","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 and the token address and nothing else; everything else is a source constant. 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":"c9147f448fad42e63a00f9037f127e153d0234660036bd8d71007e72ff9a2a3c","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"6bb2d5c6-a7a3-40b8-b009-683816d38f07","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1405-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":"52257","feedbackHash":"8ea72c0f63a96e76651c4f56edb5e13c2a7d802c966c7120a6016e73566238ef","nodeKey":"audit_imported_code","submissionHash":"06ae619e7afdcc51d2af01f5a60d3f1e37655bf885cba1e74d5d55a4622de5e1","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51486","feedbackHash":"e901483517076b53bd8527ef804e5656bccf67c0b0015a65f2e86dbb7914c5ed","nodeKey":"manifest","submissionHash":"abdb0e15407124aabbbb32ce17a5d586eba0fb33b4149447193b4947c3c72ad6","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"0b6ca17dee10a1b4bf32238cf97cd22f48d97e3342f4a226516f2fac8b0f3f7c","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"22c9b760d0a15149","findings":[{"citation":"resolved","description":"The manifest's initialPrice is 2^96, i.e. sqrtPrice 1.0, which TickMath.getTickAtSqrtPrice decodes to tick 0 (1 PIMD = 1 IMD, a 1,000,000,000 IMD opening market cap). PimdHook.beforeInitialize (src/pimd/PimdHook.sol:251-254) computes the tick from the sqrtPriceX96 it is given and reverts WrongStartingPrice(got, 129000) unless 128700 <= tick <= 129300, which is the 2,500 IMD opening cap the chain policy requires and the manifest's own notes describe. The deployer passes pool.initialPrice literally (the manifest schema says it is the decimal sqrtPriceX96), so the factory's initialize call reverts inside the hook, no pool opens, and the single-shot launch transaction fails after the hook and token are deployed. Nothing in the hook can be changed to accept this value without changing the settled opening tick, so the fix is to the manifest: pool.initialPrice must be TickMath.getSqrtPriceAtTick(129000) = \"50111677533496076234078224273595\". Any value in [49365648873540214315780664062218, 50868980404855525156974128955070] (ticks 128700..129300) is accepted; 2^96 is not. Every other manifest field was checked against the source and matches: constructorArgs [$poolManager,$token] matches constructor(IPoolManager,address); the eight permissions match getHookPermissions; fee 12500 is one of the hook's accepted tiers; pairedCurrency is the hook's QUOTE_TOKEN in lowercase; notes are 3189 characters.","line":30,"path":"launch.json","reproduction":"State: fresh PimdHook, pool key {currency0: 0x5f7b...7127 (IMD), currency1: PIMD, fee: 12500, tickSpacing: 60, hooks: hook}. Call: launch factory calls PoolManager.initialize(key, 79228162514264337593543950336) (the manifest value). Expected: pool opens at the briefed price. Actual: PoolManager -> PimdHook.beforeInitialize computes tick = TickMath.getTickAtSqrtPrice(2^96) = 0, 0 < 129000 - 300, reverts WrongStartingPrice(0, 129000); PoolManager wraps it as Hooks.HookCallFailed / CustomRevert.WrappedError and initialize reverts; hook.launched() stays false. Verified locally with the repository's own harness: factory.open(key, 79228162514264337593543950336) reverts and launched() is false, while factory.open(key, TickMath.getSqrtPriceAtTick(129000)) = factory.open(key, 50111677533496076234078224273595) succeeds (test_initialize_accepts_a_price_inside_the_tolerance). No Foundry proof is attached because the defect is in launch.json, which a test cannot read (foundry.toml grants no fs_permissions and may not be changed).","severity":"high","snippet":"    \"initialPrice\": \"79228162514264337593543950336\"","title":"launch.json pool.initialPrice is tick 0; PimdHook.beforeInitialize refuses anything outside 129000 +/- 300, so the launch's initialize reverts"},{"citation":"resolved","description":"Every fee computation in PimdHook assumes IMD is currency0 and PIMD currency1, and beforeInitialize enforces it: currency1 must be the token and currency0 must be 0x5F7Bb59365ce557C26dbcAa4EE9d39A4b95B7127. Uniswap V4 sorts the pool key numerically, so the pool can only satisfy this if address(PimdToken) > 0x5F7Bb593...; for any token address below IMD the key is {currency0: PIMD, currency1: IMD}, c1 != token, and the hook reverts PoolMustPairToken. The share of the address space below 0x5F7B... is 0x5F7B/0x10000 = 37.3%. The previous deploy script mined a CREATE2 salt for exactly this (README: 'The deploy script mines a CREATE2 salt so the token's address lands above IMD's'); in the factory flow the token is deployed by the launch factory from $token and this tree contains nothing that chooses its salt. The factory's runtime bytecode at 0xA25B02A1e93903b790E6aaf7dA1b8B8d50294645 contains two CREATE2 opcodes, so it can deploy deterministically, but its source is not available here and no evidence shows it picks a salt that sorts the token above the paired currency. If it does not, the launch fails at initialize with the token already minted to the factory and the hook already deployed; if it does, this is a trust assumption on the factory that the adapter must state. The hook cannot fix it without supporting both orderings, which is a redesign the requester has ruled out, so it is reported for the adapter and the manifest reviewer.","line":246,"path":"src/pimd/PimdHook.sol","reproduction":"State: PimdToken deployed at any address numerically below 0x5F7Bb59365ce557C26dbcAa4EE9d39A4b95B7127, e.g. 0x1000000000000000000000000000000000000001; PimdHook constructed with (poolManager, thatToken). Call: factory calls PoolManager.initialize({currency0: thatToken, currency1: 0x5F7B...7127, fee: 12500, tickSpacing: 60, hooks: hook}, 50111677533496076234078224273595) which is the only sorted key V4 accepts for that pair. Expected: pool opens. Actual: beforeInitialize line 246 sees c1 = IMD != address(token) and reverts PoolMustPairToken; the unsorted key {currency0: IMD, currency1: thatToken} is refused earlier by PoolManager.initialize with CurrenciesOutOfOrderOrEqual. The repository's own base test has to mine 'PIMD sorts above IMD' (PimdBase.t.sol _deployTokenAbove) for the suite to open the pool at all.","severity":"medium","snippet":"        if (c1 != address(token)) revert PoolMustPairToken();","title":"The hook only opens if PIMD's deployed address sorts above IMD; nothing in this tree mines $token's address, so ~37% of possible token addresses make beforeInitialize revert PoolMustPairToken"},{"citation":"resolved","description":"The hold-streak ladder (0x under an hour up to 3x after two weeks) is advertised as restarting on any sale or transfer out. tally is the only place the streak is updated, it is keeper-only, and it only compares the balance at that instant with lastBal from the previous tally (src/pimd/PimdEngine.sol:513-520). A holder who sells their whole bag and buys it back (or receives it back) before the keeper's next tally shows bal >= last, so the clock is not restarted: equal balance keeps the full tier, a larger balance is blended toward now and keeps part of it. The same applies to transfers out and back, which pay no tax at all, so a holder can lend or LP their PIMD elsewhere between epochs and still be weighed at a matured tier. With the production cadence (minInterval 15 minutes, keeper tally some time after each fire) the window is every interval between two tallies. No funds are lost and the holder pays the sell and buy taxes if they trade, but the documented guarantee in the engine's own NatSpec and the README does not hold, and the min(bal, lastBal) defence is unaffected either way (it still weighs the surviving bag). Reporting rather than proposing a fix: tracking transfers would need token-side hooks, which the fixed-supply plain token deliberately omits, so this is a design limitation the adapter should state rather than patch.","line":515,"path":"src/pimd/PimdEngine.sol","reproduction":"State (reproduced with the repository's test harness, production tax rates): alice buys 100 IMD of PIMD, is registered, 15 days pass, one epoch runs so lastBal = her bag and holderInfo tier = 30000 (3x). Calls, all before the next keeper tally: alice sells 100% of her PIMD (balance 0), then buys 200 IMD of PIMD back (71,530,454e18 PIMD, more than she had). 15 minutes later fire + tally run. Expected per the documented rule: a full sale restarts the clock, tier 0 (under an hour). Actual: tally sees bal > last, blends streakStart, holderInfo reports tier 20000 (2x) and lastBal 71,530,454e18; had she bought back exactly her old amount, bal == last and the tier would have stayed 30000. The sale is invisible to the engine because no tally observed the zero balance.","severity":"low","snippet":"            if (bal < last) {\n                h.streakStart = uint64(nowTs); // sold or sent out: the clock restarts","title":"Engine cannot see a sale that is bought back before the next tally: 'selling restarts your streak' holds only if the balance is lower at the instant the keeper tallies"},{"citation":"resolved","description":"The rehearsal script builds the pool key with fee 0, but the pool the factory opens has fee 12500 (launch.json pool.fee, and the only fee tiers the hook accepts are 500, 3000, 10000, 12500), so the key hashes to a different PoolId and every swap in the script reverts PoolNotInitialized. It also reads deployments.pimd.4663.json with vm.readFile, which foundry.toml does not permit (no fs_permissions, by design). Not on the launch path and not a security issue: the script is a local fork tool and only the deploy script and the contracts ship. Noted so the adapter does not treat it as evidence the pool was exercised.","line":34,"path":"script/RehearsePimd.s.sol","reproduction":"Run: forge script script/RehearsePimd.s.sol:RehearsePimd --rpc-url <fork> with a deployments.pimd.4663.json present. Expected: three buys and one sell against the launched pool. Actual: vm.readFile reverts for lack of fs_permissions; if that were granted, router.swap with key.fee = 0 against a pool initialized with fee 12500 reverts PoolNotInitialized because PoolId = keccak256(abi.encode(key)) differs.","severity":"info","snippet":"            fee: 0,","title":"RehearsePimd.s.sol targets a pool key with fee 0 and needs vm.readFile, so it cannot drive the launched pool as written"},{"citation":"resolved","description":"The source taxes 2.4%/5.6% (PimdHook BUY_TAX_BPS 240, SELL_TAX_BPS 560) split 75/25 with no buy-and-burn, has no launcher or launch() function, is opened by the launch factory rather than by a mined salt in the deploy script, binds with a three-argument bind(token, hook, alsoExclude), and ships 91 tests (85 local + 6 fork). The README states the old numbers, a launch() call sequence that no longer exists, 'Supply only falls' (totalSupply is fixed; burns are transfers to 0xdEaD), and a parameter table with the old opening tick 147780 and 4% drip. Documentation only; recorded so a reviewer of the published tree does not take the README as the spec. The launch.json notes are the accurate description.","line":7,"path":"README.md","reproduction":"Compare README.md line 5-7 ('3% on buys, 7% on sells', '60% holders / 20% buy-and-burn / 20% team') with src/pimd/PimdHook.sol:63-65 (240, 560, 7500) and README 'Deploying' section ('hook.launch(token, tickLower, startTick, 60)', 'engine.bind(token, hook)') with the absence of any launch function in PimdHook and bind's three parameters at src/pimd/PimdEngine.sol:278.","severity":"info","snippet":"- split **60% holders / 20% buy-and-burn / 20% team**","title":"README.md describes the previous design (3%/7% tax, 60/20/20 split with buy-and-burn, a launcher role, salt mining, 32 tests) and contradicts the shipped source and manifest"},{"citation":"resolved","description":"Read in full, line by line, as they arrived at commit 9d7038f: src/pimd/PimdToken.sol (49 lines), src/pimd/PimdHook.sol (537), src/pimd/PimdEngine.sol (869), src/libraries/L2Block.sol (21), script/DeployPimd.s.sol (118), script/RehearsePimd.s.sol (93), launch.json, README.md, the test harness test/pimd/PimdBase.t.sol, and the vendored v4-core Hooks.sol (beforeSwap/afterSwap delta composition, beforeModifyLiquidity routing of liquidityDelta <= 0 to beforeRemoveLiquidity) and Lock.sol (IS_UNLOCKED_SLOT 0xc090fc46...ab23 is byte-identical to the engine's constant). Build and all 91 tests pass (forge 1.8.3, solc 0.8.26, via_ir), including the six fork tests against live Robinhood state. Verified on Robinhood Chain (4663) over RPC: PimdEngine at 0xFFDB5Dd341F692a29b5a0d3D1577DF683C1bACa4 has runtime bytecode of the same length as this tree's build and every differing byte lies inside a 32-byte zero window of the local build (immutable slots), so the deployed engine is this source; its immutables are poolManager 0x8366a39C..., imd 0x5F7Bb593..., team = binder = 0x0960E8Bd... (an EOA), drip 150 bps, minInterval 900 s, minBalance 1,000,000e18, fireTip 0.05e18, tipPerHolder 0.003e18, maxCatchup 21600 s, maxHolders 700, keepers 0x244FA8fd... and 0x25BfA297... both true, bound() false, phase Idle, pot 0; it matches the script's production branch. IMD (Identity.md, 18 decimals, totalSupply 49,106.33e18) is not an EIP-1967 proxy (implementation and admin slots are zero) but has an owner() of 0x047F606fD5b2BaA5f5C6c4aB8958E45CB6B054B7; the fork test confirms it transfers without fee or callback within the engine's 60k gas stipend. The PoolManager and the launch factory 0xA25B02A1e93903b790E6aaf7dA1b8B8d50294645 both have code; the factory's bytecode contains two CREATE2 opcodes and no DELEGATECALL or SELFDESTRUCT. Could not be reached: the launch factory's source (so whether it mines the token's salt above IMD, calls initialize and modifyLiquidity directly from its own address, and makes exactly one liquidity add are assumptions, not verified facts; finding 2 covers the first), IMD's source and the powers behind its owner() (treated as a trust assumption: a blacklist would be survived by the engine's gas-capped send and the hook's flushHolders path, a pause would stall both), and the airdrop distributor's address, which bind must exclude. Checked and holding, with the concrete inputs tried: beforeInitialize refuses a second pool (launched), a non-factory sender, fee 2500 / 0 / 100000, currency1 != PIMD, currency0 != IMD, and ticks 128699 and 129301 while accepting 128700..129300; beforeAddLiquidity admits the factory exactly once and refuses the factory's second add and any other sender's first; beforeRemoveLiquidity refuses liquidityDelta -1 from the position owner and admits 0 (fee collection); all four swap shapes charge exactly 240 and 560 bps of the gross IMD leg with the mint-to-claims and holdersOwed+teamOwed invariants conserved; register, prune, fire, tally and pay all revert PoolUnlocked from inside a PoolManager unlock; fire, tally, pay, abortEpoch and the IMD ledger pot + epochQuote <= balance stay consistent across normal, failed-send and aborted epochs; the token is a plain solmate ERC-20 with no owner, mint, burn, pause or upgrade path and totalSupply fixed at 1e27. No DELEGATECALL, CALLCODE, SELFDESTRUCT, proxy, initializer or post-deployment configuration exists in the three contracts.","line":56,"path":"src/pimd/PimdHook.sol","reproduction":"Not a defect; this entry records coverage so the report says which contracts were read and which could not be reached, as the acceptance criteria require. Commands run: forge test (91 passed), cast code / cast call against https://robinhood-rpc.publicnode.com for the engine, IMD, PoolManager, factory and team addresses, forge inspect PimdEngine deployedBytecode compared byte-for-byte with the on-chain code.","severity":"info","snippet":"contract PimdHook is IHooks, IUnlockCallback {","title":"Scope of this review: what was read, what was verified on chain, and what could not be reached"}],"hash":"06ae619e7afdcc51d2af01f5a60d3f1e37655bf885cba1e74d5d55a4622de5e1","nodeId":"113de270-0d81-45a3-a726-e4fc7687108a","outcome":"completed","summary":"The review is complete. The findings file is written and validated, the scratch tests are removed, and no tracked file was changed.\n\n**Outcome.** The three contracts, the library and both scripts were read in full as they arrived, and the deployed engine at the address baked into the hook was confirmed on chain to be this exact source with production parameters. The code's three headline gates hold. The one blocking defect is in the manifest, not the Solidity.\n\n**Findings written to .imd-findings.json**\n\n1. **High, launch.json line 30.** The manifest's initialPrice is 2^96, which is tick 0. The hook's beforeInitialize refuses any tick outside 129000 ± 300, so the factory's initialize call reverts and no pool opens. Confirmed with the repository harness. The correct value is the sqrt price at tick 129000:\n   ```\n   50111677533496076234078224273595\n   ```\n   No Foundry proof is attached because a test cannot read launch.json without filesystem permissions, which the config does not grant.\n\n2. **Medium, PimdHook.sol line 246.** The hook only opens if the token address sorts above IMD. Nothing in this tree mines the token's salt anymore, and the factory's source could not be reached. Roughly 37% of possible token addresses would make initialize revert PoolMustPairToken.\n\n3. **Low, PimdEngine.sol line 515.** A sale bought back before the keeper's next tally is invisible to the engine. Reproduced: after selling 100% and rebuying more, the holder kept a 2x tier instead of the documented restart to 0x.\n\n4. **Info.** The rehearsal script targets a fee-0 pool key and needs file reads the config forbids. The README still describes the previous design. A scope entry records what was read, what was verified on chain, and what could not be reached (factory source, IMD source and its owner's powers, the airdrop distributor address).\n\n**Verified holding, with concrete inputs tried.** beforeInitialize refuses a second pool, a wrong sender, unsupported fee tiers, the wrong currency on either side, and ticks just outside the window. beforeAddLiquidity admits the factory exactly once and refuses every later add. beforeRemoveLiquidity refuses every negative delta and allows zero. All four swap shapes tax exactly 2.4% and 5.6% of the gross IMD leg. Every engine entry that reads balances reverts PoolUnlocked from inside a PoolManager unlock, and the unlock-slot constant is byte-identical to v4-core. All 91 tests pass, including the six fork tests against live Robinhood state.\n\n**Coverage record.** All 20 listed entry points have a row, plus three invariants. Two rows reference findings (beforeInitialize, tally); the rest are marked as holding.","treeHash":null,"usage":{"cachedInputTokens":1549707,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":50085,"runtime":"claude","turns":26,"wallClockMs":730956}},{"artifacts":[],"attempt":1,"bundleHash":"a2e0f57c6bf076aa8fb32328e840da75f4143bd1cc8f743558070544897ca440","device":"0e78515c4d95885a","findings":[],"hash":"abdb0e15407124aabbbb32ce17a5d586eba0fb33b4149447193b4947c3c72ad6","nodeId":"71faa382-32a3-4103-a985-bad8c4c42a5c","outcome":"completed","summary":"launch.json is written and validated. The only file changed in the tree is launch.json.\n\n**What I found in the audit** (reported, nothing fixed):\n\n- **Hook gates hold as advertised.** beforeInitialize is the single gate: it refuses a second pool, a non-factory sender, a fee outside the four listed tiers, a wrong pairing or quote, and a tick more than 300 from 129000. That tick prices the supply at 2,500 IMD, which I confirmed numerically. beforeAddLiquidity admits the factory exactly once. beforeRemoveLiquidity refuses every negative delta and allows zero.\n- **Engine never reads balances while unlocked.** Every balance-reading entry point calls the PoolManager unlock check first, and tally is keeper-only and whole-set.\n- **Live engine verified.** The engine at the baked-in address is on chain, unbound, byte-identical to this tree's build outside immutable slots, and carries the script's production config.\n- **Seven findings for reviewers**, all in notes. The two that can revert the paid launch are that the hook requires the token address to sort above IMD, which the factory's deployment decides rather than the hook, and that the launch factory is a compile-time constant that must equal whatever the deployer resolves for the factory.\n\n**What the manifest says**\n\n- Hook PimdHook with constructor args `$poolManager`, `$token`. The hook has no owner or factory parameter, so no `$owner` or `$factory` entry.\n- Eight permissions, matching what getHookPermissions returns: beforeInitialize, afterInitialize, beforeAddLiquidity, beforeRemoveLiquidity, beforeSwap, afterSwap, beforeSwapReturnDelta, afterSwapReturnDelta.\n- Token PimdToken, Ponzinomics, PIMD, 18 decimals. Pool fields exactly as the reference gives them, with fee 12500, which the hook accepts.\n\n**Checks run**\n\n| Check | Result |\n|---|---|\n| forge build | ok |\n| forge test, local suites | 85 passed |\n| scratch test pinning permissions, token metadata, tick | 3 passed |\n| notes length | 3189 of 4000 |\n\nFork tests were not run. The manifest is not committed; no commit was requested.","treeHash":"0f58c8527170821b3013ce78eb7afd05f47d9a45","usage":{"cachedInputTokens":687103,"inputTokens":322,"model":"claude-fable-5-1","outputTokens":11978,"runtime":"claude","turns":19,"wallClockMs":326734}}],"verification":[{"checks":[{"durationMs":66611,"exitCode":0,"name":"build","output":"Compiling 87 files with Solc 0.8.26\nSolc 0.8.26 finished in 66.47s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/pimd/PimdEngine.sol:501:20:\n    |\n501 |     function tally(uint256 maxHolders) external nonReentrant {\n    |                    ^^^^^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n   --> src/pimd/PimdEngine.sol:111:5:\n    |\n111 |     uint256 public immutable maxHolders;\n    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/pimd/PimdEngine.sol:562:18:\n    |\n562 |     function pay(uint256 maxHolders) external nonReentrant {\n    |                  ^^^^^^^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n   --> src/pimd/PimdEngine.sol:111:5:\n    |\n111 |     uint256 public immutable maxHolders;\n    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\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:531:9\n    │\n531 │         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:532:9\n    │\n532 │         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:756:9\n    │\n756 │         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:757:9\n    │\n757 │         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:262:9\n    │\n262 │         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:260:21\n    │\n260 │         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:261:23\n    │\n261 │         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:290:28\n    │\n290 │         _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:282:46\n    │\n282 │                 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:282:81\n    │\n282 │                 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:291:63\n    │\n291 │         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:291:70\n    │\n291 │         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:344:44\n    │\n344 │         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:333:24\n    │\n333 │                     && 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:334:24\n    │\n334 │                     && 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:512:9\n    │\n512 │         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:312:44\n    │\n312 │             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:312:52\n    │\n312 │             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:312:69\n    │\n312 │             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:312:77\n    │\n312 │             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:320:41\n    │\n320 │             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:320:49\n    │\n320 │             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:320:65\n    │\n320 │             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:320:73\n    │\n320 │             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:324:36\n    │\n324 │             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:324:43\n    │\n324 │             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:336:33\n    │\n336 │                 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:336:41\n    │\n336 │                 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 `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:796:17\n    │\n796 │             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:781:9\n    │\n781 │         emit Pruned(a);\n    │         ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_lock` is updated\n    ╭▸ src/pimd/PimdHook.sol:417:9\n    │\n417 │         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/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/PimdHook.sol:421:9\n    │\n421 │         emit Flushed(msg.sender, toHolders, 0, 0);\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[unused-return]: return value of an external call is not used\n    ╭▸ src/pimd/PimdHook.sol:417:9\n    │\n417 │         poolManager.unlock(abi.encode(ACTION_HOLDERS, toHolders));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\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[reentrancy-no-eth]: external call can be reentered before `_lock` is updated\n    ╭▸ src/pimd/PimdHook.sol:447:9\n    │\n447 │         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:455:9\n    │\n455 │         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:447:9\n    │\n447 │         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[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:708:9\n    │\n708 │         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:740:13\n    │\n740 │             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:528:32\n    │\n528 │             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:513:27\n    │\n513 │             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:516:33\n    │\n516 │                 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:519:33\n    │\n519 │                 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:525:25\n    │\n525 │             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:528:24\n    │\n528 │             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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/pimd/PimdHook.sol:502:16\n    │\n502 │             && 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:503:16\n    │\n503 │             && 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 `cursor` is updated\n    ╭▸ src/pimd/PimdEngine.sol:740:13\n    │\n740 │             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:584:17\n    │\n584 │                 paid += amt;\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:585:19\n    │\n585 │                 ++paidHolders;\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:740:13\n    │\n740 │             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:588:17\n    │\n588 │                 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:604:13\n    │\n604 │             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:583:31\n    │\n583 │                 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:635:13\n    │\n635 │         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:740:13\n    │\n740 │             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:778:32\n    │\n778 │         _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:280:17\n    │\n280 │         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":1625,"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 855.54ms (697.27µ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 891.55ms (29.59ms 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 898.55ms (25.40ms 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.10s (922.58ms CPU time)\n\nRan 37 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: 448407)\n[PASS] test_a_second_pool_on_this_hook_is_refused() (gas: 47531)\n[PASS] test_a_stranger_cannot_squat_the_seed() (gas: 102403141)\n[PASS] test_an_excluded_holder_is_never_registered_or_paid() (gas: 638866)\n[PASS] test_bind_is_one_shot() (gas: 38573)\n[PASS] test_buy_pays_the_buy_tax_in_imd() (gas: 557742)\n[PASS] test_drip_releases_about_four_percent_per_fifteen_minutes() (gas: 2330929)\n[PASS] test_even_the_factory_gets_only_one_add() (gas: 63959)\n[PASS] test_exact_out_buy_is_taxed_too() (gas: 853740)\n[PASS] test_exact_out_sell_is_taxed_too() (gas: 820022)\n[PASS] test_fee_collection_is_still_allowed() (gas: 690210)\n[PASS] test_first_hour_pays_nobody() (gas: 1151800)\n[PASS] test_flush_payouts_moves_real_imd() (gas: 771924)\n[PASS] test_holder_is_paid_in_imd_after_a_day() (gas: 1447209)\n[PASS] test_hook_refuses_raw_eth() (gas: 36901)\n[PASS] test_initialize_accepts_a_price_inside_the_tolerance() (gas: 101967865)\n[PASS] test_initialize_refuses_a_mispriced_pool() (gas: 101797862)\n[PASS] test_initialize_refuses_a_pool_that_is_not_our_token() (gas: 102692742)\n[PASS] test_initialize_refuses_a_quote_that_is_not_imd() (gas: 102692800)\n[PASS] test_initialize_refuses_an_unsupported_fee_tier() (gas: 101795525)\n[PASS] test_keeper_tip_comes_out_of_the_team_slice() (gas: 764398)\n[PASS] test_launch_cap_expires() (gas: 879738)\n[PASS] test_launch_cap_limits_one_buyer() (gas: 748611)\n[PASS] test_launch_puts_the_seed_in_one_range() (gas: 35399)\n[PASS] test_launch_sets_imd_as_currency0() (gas: 9805)\n[PASS] test_liquidity_can_never_be_removed_even_by_its_owner() (gas: 137603)\n[PASS] test_longer_holder_gets_a_bigger_share() (gas: 1931055)\n[PASS] test_only_the_launch_factory_may_open_the_pool() (gas: 101796402)\n[PASS] test_pool_starts_with_no_imd() (gas: 10156)\n[PASS] test_sell_pays_the_sell_tax_in_imd() (gas: 831463)\n[PASS] test_selling_restarts_the_streak() (gas: 1448330)\n[PASS] test_tax_splits_seventy_five_twenty_five() (gas: 561800)\n[PASS] test_the_imd_address_is_never_registered() (gas: 644140)\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: 1605762)\nSuite result: ok. 37 passed; 0 failed; 0 skipped; finished in 1.53s (4.49s 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 1.53s (44.87ms CPU time)\n\nRan 6 test suites in 1.53s (6.81s CPU time): 91 tests passed, 0 failed, 0 skipped (91 total tests)\n","passed":true},{"durationMs":63,"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\":112,\"foundry.toml\":35,\"launch.json\":33,\"script/DeployPimd.s.sol\":118,\"script/RehearsePimd.s.sol\":93,\"src/libraries/L2Block.sol\":21,\"src/pimd/PimdEngine.sol\":869,\"src/pimd/PimdHook.sol\":537,\"src/pimd/PimdToken.sol\":49,\"test/pimd/Gas.t.sol\":46,\"test/pimd/Pimd.t.sol\":564,\"test/pimd/PimdAttacks.t.sol\":420,\"test/pimd/PimdBase.t.sol\":414,\"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":"abdb0e15407124aabbbb32ce17a5d586eba0fb33b4149447193b4947c3c72ad6","verifiedTreeHash":"0f58c8527170821b3013ce78eb7afd05f47d9a45","verifierVersion":"0.1.0+fb8278c5"}]}