{"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":"dc18d5f5-7213-4b3f-b281-5f137f06ddcc","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"2b16b9fbd2142e9fa3dc86d994fb0bbcd9eb2102a92c63344b1b92262f0afdb9","dependsOn":["audit_imported_code"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","tools":[]},"key":"adapt_contract_project","kind":"code","role":"implement","skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","state":"accepted"},{"acceptedSubmissionHash":"f526ee1198483ecc214df6f8ed32be30fdecff480d11c3985a91233f84c367be","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"29288c5945a1869d23201835a11a625c1e3eed8731259ff32899762f304a07fb","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"e79124f44640c0ad572b967b984d60c0f4b3c1d8ff853a78c47d0b12001a7fe8","dependsOn":[],"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":"0425e871e0c775e89261864cd52c2dc9f4e053c3bfadb1c8989e9e9255f8c502","dependsOn":["adapt_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"977d31a96d90c1c836ff796c4e8e258ed330c6ee49c77fb166e59694e991aba5","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"28582f9ea1475c3dc16a9ad4bf4eaadbac4785527f73eaa98860c4d9ba4e9c4d","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"40f46b43d0ce5d6b9cacfb45ca70527dc2e878ef97156c816567e2b0ead7c19a","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"d1c021517645d61307d9d0ee126c74ecc9f5f4c0470d205bce08c1dfec694f1e","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"4c76766b00591dfe982fe85d3de589fdb2f810e5137c301155d1c5e36a1d3b23","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"4c76766b00591dfe982fe85d3de589fdb2f810e5137c301155d1c5e36a1d3b23","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Use the pinned imported Foundry repository and preserve its tested CabalFundingHook, LaunchToken and separately runnable worker design. Review the code and adapt only as required for IMD's supported univ4_hook / LaunchFactory route. Produce the valid manifest, tests, audit results and deployment artifacts. Do not substitute a generic hook, omit the reserve or rewrite economic behavior silently. This request covers the token, pool and funding software; no website hosting or ongoing operation of a server is requested.\n\nToken: Cabal Coin (CABAL), Robinhood Chain 4663, paired with IMD. Exactly 1,000,000,000 tokens, 18 decimals, minted once to the deploying factory, with plain untaxed transfers, no privileged minting and no token admin switches. Preserve the read-only PVP_MINIMUM_HOLDING constant of 50,000 CABAL (50000000000000000000000 atomic units) and isPvpEligible(address), true exactly when balanceOf(address) meets that threshold. These views do not restrict transfers, require approvals or grant a game session; the game server checks them with a fresh wallet signature at every queue entry. Supply allocation: 88% official pool (poolBps 8800), 10% to the swarm under IMD policy, 2% remainder to 0x4251627b712762b6e81799698a05c6c9dff35c5d. Hook contract owner and fixed project treasury are also 0x4251627b712762b6e81799698a05c6c9dff35c5d. Keep IMD's LP custody and platform distribution rules. Current hook policy 30 opens the IMD pair at a 2,500 IMD valuation; recheck the current policy and report any change. This opening valuation is not a deposit of 2,500 IMD: the pool seed is single-sided CABAL. Do not invent a custom-token economics field to override the hook cap.\n\nImmutable custom tax: 4% on buys and 4% on sells of the official CABAL/IMD pool, collected entirely in Robinhood IMD. No tax on wallet transfers or unrelated pools. On exact-input buys charge floor(gross IMD input/25). On exact-input sells charge floor(gross pool IMD output/25). On exact-output buys add ceil(required pool IMD input/24); on exact-output sells add ceil(requested net IMD output/24). Preserve the code's atomic-unit rounding and full-fill checks. The separate platform pool fee is 12500 (1.25%); keep its current 80% requester / 20% network allocation. That fee is additional, outside our 70/30 split. Do not advertise a simple flat all-in 5.25% execution quote.\n\nUse the hook's ERC-6909 fee claims and embedded accounting. Redeem claims with harvest; allocate cumulative floor(totalHarvested*7/10) to the request fund and the remainder to the fixed project treasury. Pay treasury only to the immutable address above. Swaps must not call an oracle or depend on treasury payout, a running worker, gas funding or request service availability. Separate prefunding/donations go 100% to requests. A taxable base of 1,000 IMD yields 40 IMD custom fees: 28 IMD to requests and 12 IMD to treasury. No holder rewards, snapshots, dividends, automatic ETH conversion or bridging.\n\nUse src/LaunchToken.sol with no constructor arguments. Use src/CabalFundingHook.sol with the six constructor arguments in order: $poolManager, $factory, 0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127 (Robinhood IMD), 0xa43e6f75ee006411f79ac1c84120606c2330de82 (Robinhood Intake), $owner, 0x4251627b712762b6e81799698a05c6c9dff35c5d (treasury). Mine the required hook address mask 0x20cc. The supported launch route is univ4_hook / LaunchFactory, not evm_project / ProjectFactory. Verify the final initializer caller, permission flags, constructor bytes and bound pool. Owner has no tax/split/treasury/intake changes, arbitrary sweep, trading pause or upgrade authority; owner can set the request worker, spending caps and payment enablement only.\n\nPreserve worker-only oracle.request@oracle-1 payments through the immutable Robinhood Intake, no callback and no arbitrary payee/action. Enforce unique decision keys, expiry, per-request and UTC-day caps, exact approval/debit verification and allowance clearing. Deploy with paymentsEnabled false and no worker configured. Worker address and caps are separately configured by the owner after deployment and operational review. Current request price is 0.5 IMD; read current capabilities and halt if configured pricing no longer matches. A successful on-chain payment is not proof of off-chain admission or an oracle verdict.\n\nRetain the supplied Node worker, durable SQLite transaction journal, coordinator lease, signed-byte-before-broadcast persistence, duplicate prevention, receipt reconciliation, explicit reorg/ambiguity halt, bounded gas spending and low-fund stops. Keep the tested fixes for already-settled unsigned maintenance and atomic balance-read/reservation admission. Reserve up to eight paid decisions per BO5 before admitting a duel: 4 IMD at the current price, or 64 IMD for sixteen simultaneously reserved matches, plus an operating reserve. The project will host and integrate this worker; an IMD launch does not run it. Funding IMD and native Robinhood ETH for gas are separate owner actions. Tax volume is not a guarantee of sufficient funding.\n\nPreserve the offline Foundry build and pinned dependencies. Rerun the unit/fuzz tests, worker tests and local end-to-end accounting/recovery tests after any code change. Reproduce the local production-router fork review in both currency orderings using UniversalRouter 0x8876789976decbfcbbbe364623c63652db8c0904, V4Quoter 0x8dc178efb8111bb0973dd9d722ebeff267c98f94 and Permit2 0x000000000022d473030f116ddee9f6b43ac78ba3, after confirming current deployments. Match Explorer's extended exact-input struct, minHopPriceX36=0, command 0x10 and actions 0x060c0f. Assert quote/execution parity, slippage, approvals, deadlines, both trade directions and fee accounting. Quotes already include custom tax: do not deduct it again. Oversized orders that exceed available liquidity fail quoting; keep this visible rather than bypassing slippage or promising unlimited fills. The current UI offers exact input; exact output is separately tested at contract level.\n\nReturn reproducible source, manifest, addresses, ABIs, runtime hashes, code changes and remaining integration limits. Preserve default-disabled operation. The game coordinator, signer injection and verified oracle result application are implemented separately but remain disabled pending verified deployment, reserve funding and a paid live validation. Do not claim live operation before that validation. Publish no secrets, personal names, local machine paths, game domain or private gameplay rules. Include only the authorized public wallet roles and necessary protocol addresses. The submitted source and IMD work are public; do not fabricate private oracle delivery or host the game as part of this launch.","parentJobId":null,"planHash":"71f89e64d8ef0307748274c39b6b8669ed14e13c9351eb205299256772aec3d1","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"dc18d5f5-7213-4b3f-b281-5f137f06ddcc","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2196-use-pinned-imported-foundry"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"52193","feedbackHash":"7733c3c5fa330443009487b411dfae830c3b117c48c5e4f1567c53a9b27751af","nodeKey":"adapt_contract_project","submissionHash":"2b16b9fbd2142e9fa3dc86d994fb0bbcd9eb2102a92c63344b1b92262f0afdb9","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51189","feedbackHash":"085ad7664a036b621dbb02c042cf2ff3543376f692313d1d090161daae2ee837","nodeKey":"adapt_contract_project","submissionHash":"eacc9012880b9bf52d78203f7497fb4956b6f48f704e8ea44519cba3f0c80fad","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52254","feedbackHash":"d61830babd00096f7f9f6698e1db5a8ffd4e170fd8c31184fadb9466f7bf9379","nodeKey":"audit_economics","submissionHash":"f526ee1198483ecc214df6f8ed32be30fdecff480d11c3985a91233f84c367be","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50939","feedbackHash":"b2e1c00dbec91d6c455a4474f730dcba2ab6da683a6e0145f90f45379c51cc3e","nodeKey":"audit_flow","submissionHash":"29288c5945a1869d23201835a11a625c1e3eed8731259ff32899762f304a07fb","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51317","feedbackHash":"7905f7577757e31044d4532388cdb617f082b4794169062db3f71a4b965a7682","nodeKey":"audit_imported_code","submissionHash":"e79124f44640c0ad572b967b984d60c0f4b3c1d8ff853a78c47d0b12001a7fe8","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52701","feedbackHash":"930c2cdd2b4ca6a3680b352864a64080fdfc6ee556619ea4db7c65eff8eec8a8","nodeKey":"audit_judge","submissionHash":"0425e871e0c775e89261864cd52c2dc9f4e053c3bfadb1c8989e9e9255f8c502","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51515","feedbackHash":"0d2d08d1581d36d5ec9d6f47b8364a8013b4814f8e85420114c9f94daa3fdd1b","nodeKey":"audit_judge","submissionHash":"cf7bb0376e93aa2dab84f753304912f91dfd837dc260b9088af1f52d69e0e4c8","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52192","feedbackHash":"148ce2336274ad6403f677b9ea95b1166165c07f1744f2bad5fd0ea96a45ed11","nodeKey":"audit_math","submissionHash":"977d31a96d90c1c836ff796c4e8e258ed330c6ee49c77fb166e59694e991aba5","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52232","feedbackHash":"8bed89d80f94735aace8fde028fae730d02be088945fc061549c6dd3a4cba46f","nodeKey":"audit_permissions","submissionHash":"28582f9ea1475c3dc16a9ad4bf4eaadbac4785527f73eaa98860c4d9ba4e9c4d","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51108","feedbackHash":"b44e052b75509e45d3b4938fe5dca68a3b4546eaa4e3664eac39c1850231f77a","nodeKey":"manifest","submissionHash":"40f46b43d0ce5d6b9cacfb45ca70527dc2e878ef97156c816567e2b0ead7c19a","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51758","feedbackHash":"bceb5b56b540ae3535c7e41a418934400255378bf02347824b28251c009c79d4","nodeKey":"write_foundry_tests","submissionHash":"a98dad36fe291a6f655f4596c26a4570eb00ef73eea7835b721632a6bca2079d","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51291","feedbackHash":"efd11327fb17d8877bf3c6fa210697278fe4578c0f5cc6bbacba0ad8cb7e2a0b","nodeKey":"write_foundry_tests","submissionHash":"d1c021517645d61307d9d0ee126c74ecc9f5f4c0470d205bce08c1dfec694f1e","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"f08131f2cce191d32881e9eaf05ccad304d4eb9b41608e316a558ca13cc60aff","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"aac006f7fc669006","findings":[],"hash":"0425e871e0c775e89261864cd52c2dc9f4e053c3bfadb1c8989e9e9255f8c502","nodeId":"abdeafc3-bad2-4962-acba-9a318cb35406","outcome":"completed","summary":"Nothing from last round is still open, and I found nothing new to report. `.imd-findings.json` has an empty findings list and covers all 13 entry points, each marked \"holds\", plus two rows recording the confirmed fixes.\n\n**Last round's findings:**\n- **[medium] The worker ignored the contract's daily spending cap: fixed.** I re-ran my original scenario against the fixed worker code (4 IMD daily cap, 100 IMD fund, 0.5 IMD price):\n  - The first match is admitted. The second is refused with \"Daily spending cap reached\", and still refused after the first match's 8 decisions are paid.\n  - For a match whose reservation skipped admission, the engine now returns `day_cap` on every run. The payment is left queued, nothing is prepared, signed or sent, and the skipped day is logged only once.\n  - The worker's cap calculation counts days the same way the contract does (`block.timestamp / 1 days`). It also keeps treating the old day as current for five minutes after midnight, so it never under-counts spending.\n- **[low] A failed payment could be re-queued silently: fixed.** Queuing the same decision again after it failed now throws \"Decision payment failed; operator review required before any re-queue\".\n- **[info] Hook owner set through `$owner`: dropped.** The author is right. The manifest rules require that placeholder and refuse a typed-in address, and the brief itself lists `$owner` for that argument. Whether the owner comes out right depends on the launch request, not the code. Reading `owner()` after deployment, as `deployment/README.md` already says, is the right check.\n\n**This round's specialist reports:** they audited the code before the fix, so their three versions of the daily-cap issue and the re-queue issue are the same two findings above, now fixed. The other three are info notes describing behaviour the brief asks for:\n- an untaxed second pool can be opened next to the official one;\n- PvP eligibility is a live balance check, so one holding can qualify several wallets in turn;\n- rounding to the smallest unit means very tiny swaps pay no tax or slightly more than 4%.\n\nI dropped all three as not defects.\n\n**Checks run:** the contracts did not change this round; only the worker, tests, scripts, docs and reports did. I read every changed worker line and found nothing the fix broke. `forge test --offline` passed 63 of 63 and `node --test` passed 40 of 40. My replay script was a temporary file outside the repository and has been deleted. I did not change any repository file except writing `.imd-findings.json`.","treeHash":null,"usage":{"cachedInputTokens":914928,"inputTokens":24,"model":"claude-opus-5-5","outputTokens":8548,"runtime":"claude","turns":12,"wallClockMs":135800}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"82692ab5f7f7e54e","findings":[{"citation":"resolved","description":"The brief fixes the hook owner to 0x4251627b712762b6e81799698a05c6c9dff35c5d and the manifest writes the fifth constructor argument as \"$owner\", exactly as the brief lists it. Per the manifest rules, \"$owner\" resolves to the owner the launch request names, else to the payer. The owner is immutable in the hook (src/CabalFundingHook.sol:40, 98) and is the only address that can call configureWorker (src/CabalFundingHook.sol:107-110, 260), so whoever the placeholder resolves to permanently holds worker/cap/enable authority and nobody else can ever take it. The treasury argument is written as a literal and is not affected. deployment/launch-terms.json names the owner as 0x4251…5c5d and deployment/artifacts.json records the resolved sixth and fifth arguments as that address, but those files are not manifest fields the deployer reads. This is not a code defect and matches the brief; it is the one input the submitted source cannot enforce, so it must be confirmed in the launch request before deployment and verified by reading owner() afterwards, as deployment/README.md already instructs.","line":10,"path":"launch.json","reproduction":"State: a launch request for this manifest that does not name an owner, paid by address P != 0x4251…5c5d. Deployer resolves \"$owner\" = P and deploys CabalFundingHook(manager, factory, IMD, Intake, P, 0x4251…5c5d). Afterwards: hook.owner() == P; call configureWorker(w, 5e17, 1e18, true) from 0x4251…5c5d -> reverts Unauthorized(); the same call from P succeeds. No function can change owner. Expected per brief: owner() == 0x4251627b712762b6e81799698a05c6c9dff35c5d. Check before deployment: the launch request's owner field equals 0x4251…5c5d; check after: owner() view.","severity":"info","snippet":"      \"$owner\",","title":"Hook owner is bound through the $owner placeholder, so configureWorker authority depends on the launch request naming 0x4251…5c5d as owner"}],"hash":"28582f9ea1475c3dc16a9ad4bf4eaadbac4785527f73eaa98860c4d9ba4e9c4d","nodeId":"7ff93364-1fd7-49ab-b53c-6d55568aef4c","outcome":"completed","summary":"The review is complete and written to `.imd-findings.json` at the repository root. The working tree is otherwise unchanged.\n\n## Scope\n\nFiles read in full: `src/CabalFundingHook.sol`, `src/LaunchToken.sol`, `test/CabalFundingHook.t.sol`, `launch.json`, `deployment/*`, `ADAPTATION.md`, the deployment and project READMEs, the factory ABI fixture, and the fork and protocol reports. I checked the code against the Pashov Access Control, Trust Gap and Asymmetry guides plus the Senior Auditor's Mindset. Not reached in depth: the Node worker's internal admission and lease logic, which lives off chain and outside the assigned area. Its 36 tests pass.\n\n**Severity counts:** 0 Critical · 0 High · 0 Medium · 0 Low · 1 Info\n\n**What ran.** The offline Foundry build and all 22 Solidity tests pass, including the 1,536 fuzz cases. The Node worker, oracle-client and manifest tests pass. A scratch probe I wrote and removed confirmed the claims the suite did not cover directly: a stranger cannot bind the pool, the factory cannot rebind a second pool, a wrong fee or non-IMD pool is refused even from the factory, an outsider who unlocks the PoolManager cannot reach `unlockCallback` or burn or move the hook's ERC-6909 claims, and the owner's only power is configureWorker.\n\n**Artifacts.** The shipped hook runtime hex differs from a fresh compile only in the six patched immutable slots, and its keccak equals the recorded hash. Token bytes and both ABIs are byte-identical to the compiled output. Constructor order and the 0x20cc mask match the manifest.\n\n### [I-1] Owner authority depends on the `$owner` placeholder resolving to the named address\n**Location:** `launch.json:10`\nThe brief fixes the owner to 0x4251…5c5d and lists `$owner` as the fifth argument, which the manifest follows. The placeholder resolves to the owner the launch request names, else the payer. Because the owner is immutable and is the only caller of configureWorker, a request that omits the owner permanently hands worker and cap authority to the payer. This is a pre-deployment input to confirm, not a code defect.\n\n## Observations (non-blocking)\n- beforeInitialize does not pin the launch token or tick spacing. The factory is the sole initializer and binds in the same transaction, so this is the documented trust assumption, not a gap.\n- harvest, payTreasury, deposit and syncDonations are permissionless by design. A stranger calling them can only move value into the fixed ledgers or to the immutable treasury.\n- Worker and owner remain trusted to submit legitimate request bodies. Daily and per-request caps bound, but do not eliminate, waste by a compromised worker.\n\n## Coverage\nAll 13 listed entry points have rows, 12 `holds` and 1 `finding` on configureWorker pointing at the info item, plus four invariant rows covering the permission mask, artifact integrity, the absence of owner economic powers, and the default-disabled deployment state.","treeHash":null,"usage":{"cachedInputTokens":1457030,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":41103,"runtime":"claude","turns":31,"wallClockMs":567386}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"72b617d4b615473a","findings":[{"citation":"resolved","description":"DuelFundingBudget.admit reserves 8 x price per match against the hook's requestBalance plus the operating reserve. CabalFundingHook.payRequest additionally enforces `spentToday + amount > dailyLimit` (src/CabalFundingHook.sol:289), a per-UTC-day cap the owner configures. Neither admit() nor engine.runOnce reads dailyLimit/spentToday or the local day's confirmed spend before reserving, so the brief's guarantee that eight paid decisions are reserved before a duel is admitted holds only against the balance, not against the binding cap. Payments that exceed the day's remaining cap are confirmed-reverted, and the engine then marks them failed with no retry (worker/engine.mjs:36), which makes those decisions permanently unpayable (see finding 2). No funds are lost; the defect is a broken reservation guarantee under normal operation when more than dailyLimit/price decisions are reserved in one UTC day (for the brief's own sizing, sixteen simultaneous matches need 64 IMD of cap per day).","line":23,"path":"worker/budget.mjs","reproduction":"State: hook configured with configureWorker(worker, 0.5e18, 1e18, true) (dailyLimit 1 IMD), requestBalance 100 IMD, spentToday 0. Run (worker modules, in-memory store, stub adapter returning requestBalance=100e18, gas=1e18, terms.amount=0.5e18): for m in [m1,m2,m3,m4] await budget.admit(m). Actual: all four admissions succeed and store.reserved() == 16e18 (32 decisions) although the on-chain cap permits 2 payments that day; the third payRequest in the day reverts LimitExceeded (covered on the contract side by test/CabalFundingHook.t.sol testPaidRequestsDeduplicateAndEnforceCaps), the engine marks it 'failed' on reconcile, and the duel's remaining decisions cannot be funded that day. Expected: admit() refuses (or the operator is told) when reserved + 8*price exceeds the remaining daily cap, or the engine defers rather than lets the payment revert.","severity":"low","snippet":"    this.store.reserveMatch(matchId,price*8n,balances.requestBalance,BigInt(this.config.minimumImdReserveAtomic));","title":"Duel admission reserves against requestBalance only and ignores the on-chain UTC-day cap, so admitted matches can have their reserved decisions revert LimitExceeded"},{"citation":"resolved","description":"The decision key is deterministic per (chainId, contract, match, round, seat). When a signed payRequest is included but reverts on chain (Expired after an inclusion delay longer than the 1800 s deadline, or LimitExceeded from the UTC-day cap, finding 1), engine.runOnce patches the job to state 'failed' (worker/engine.mjs:36). store.next() excludes 'failed', so the job is never processed again. A later enqueue of the same decision with identical input hits the `if(old)` branch and returns the failed row without error, so DuelFundingBudget.queueDecision resolves successfully while nothing will ever be paid; the on-chain key is still unused (a reverted call writes nothing), so the payment is possible but unreachable without editing the journal. 'No automatic paid retry' is a stated design choice; silently reporting success for a dead decision is not, and it leaves the game coordinator with no signal that the decision is unfunded.","line":38,"path":"worker/store.mjs","reproduction":"node (in-memory FundingStore): s.reserveMatch('m1', 4e18, 100e18, 0); id = decisionKey(4663, contract, 'm1', 1, 0); s.enqueue({id, match:'m1', amount:'500000000000000000', body:'{\"question\":\"x\"}'}) -> state 'queued'. Simulate the engine's reconcile of a reverted inclusion: s.patch(id, {state:'failed', error:'Confirmed transaction reverted; no automatic paid retry'}) (exactly what worker/engine.mjs:36 does on observed.state==='reverted'). Then s.enqueue(same arguments). Actual: returns the row with state 'failed' and throws nothing; s.next() returns undefined; the decision can never be paid. Expected: enqueue throws (or re-queues a fresh attempt the operator explicitly approves) so queueDecision fails loudly for an unfundable decision.","severity":"low","snippet":"      if(old) { if(old.match_id!==match || old.amount!==price.toString() || old.payload!==JSON.stringify({body})) throw Error('Decision key reused with different input'); return old; }","title":"A decision whose payment transaction reverted is permanently unpayable, and store.enqueue/queueDecision returns the failed row as if it were queued"}],"hash":"29288c5945a1869d23201835a11a625c1e3eed8731259ff32899762f304a07fb","nodeId":"bcf182f2-2f10-40c8-ab40-89befb707008","outcome":"completed","summary":"Review complete. Findings and coverage are written to `.imd-findings.json` in the repository root, validated as JSON, with both snippets confirmed against the current tree.\n\n**Outcome.** No critical, high or medium defects in the Solidity control flow. The hook's swap, harvest, donation, treasury and payment paths all trace as the brief specifies, in both currency orderings and all four swap modes. Two low-severity worker control-flow findings are reported.\n\n**What I covered**\n- Traced `beforeSwap` and `afterSwap` through the vendored v4 `Hooks` and `PoolManager.swap`, confirming the specified-delta sign handling, the full-fill check against the raw pool delta, and that the ERC-6909 claim mint nets the hook delta to zero.\n- Verified the cumulative 70/30 identity in `harvest` is exact and cannot underflow, and that `unlockCallback` is reachable only from the hook's own `harvest`.\n- Checked `beforeInitialize` against the deployment flow: a predicted address without code fails `InvalidHookResponse`, and the fork review shows the factory as initializer.\n- Confirmed the mined mask 0x20cc equals the five declared permissions and matches `launch.json`.\n- Ran the existing 22 Foundry tests (all pass) plus five scratch tests under `test/scratch/` for partial fills in every mode, a 1-wei exact-output sell, and chunked harvest splits. All pass; one initial failure was my own fixture exceeding liquidity and reverting `PartialFill` as designed.\n- Read the worker's store, engine, budget and chain adapter for cross-transaction flow.\n\n**Findings (both low)**\n1. `admit` in the worker reserves eight decisions against `requestBalance` only and never reads the hook's UTC-day cap. Reproduced: four matches admitted with a 1 IMD daily cap, 32 decisions reserved, two payable that day.\n2. A decision whose payment reverted on chain is marked failed and never retried, while re-enqueueing the same key returns the failed row without error. Reproduced against the in-memory store.\n\n**Not done.** No high-severity proof tests were written because none was warranted. `forge-std` is not vendored, so any proof test would need the repo's own cheatcode interface pattern. The UTC-day double-window and the unpinned tickSpacing/CABAL in `beforeInitialize` were already dispositioned in ADAPTATION.md and are not repeated. No repository file was modified apart from `.imd-findings.json` and `test/scratch/`.","treeHash":null,"usage":{"cachedInputTokens":1890565,"inputTokens":514,"model":"claude-fable-5-1","outputTokens":37611,"runtime":"claude","turns":36,"wallClockMs":550559}},{"artifacts":[],"attempt":1,"bundleHash":"3ff6679576f6c671746664247ae1e774902e3dda9def58fd39e24814c3f179cb","device":"5d667e4b0751bcb5","findings":[],"hash":"2b16b9fbd2142e9fa3dc86d994fb0bbcd9eb2102a92c63344b1b92262f0afdb9","nodeId":"8aeedbac-37e8-4a61-9e8c-07db3b18a2d9","outcome":"completed","summary":"The responses file is excluded by the harness's own `.git/info/exclude` rule, so it is picked up from the working tree as intended. The revision is complete.\n\n## Outcome\n\nBoth review findings reproduced against the unchanged worker and are fixed. No Solidity changed, so the contract bytecode, constructor arguments, tax, split, reserve and admin surface are untouched.\n\n**Finding 9cb93f2… (medium, day cap) — fixed.** The reproduction showed two BO5 admissions under a 4 IMD daily cap, then the capped decision returning `unavailable` every tick with no signed bytes, no event, and a blocked `closeMatch`. Changes:\n\n- `worker/day-cap.mjs` (new) computes the hook's remaining UTC-day headroom from `dailyLimit`, `spentDay` and `spentToday` with the same day arithmetic as `payRequest`, counting the chain's recorded day as current until five minutes after midnight.\n- `worker/chain-adapter.mjs` reads those three values with the balance snapshot.\n- `worker/budget.mjs` and `worker/store.mjs` require the configured daily limit to match the chain and refuse a reservation, inside the same SQLite transaction, when open reservations plus eight decisions exceed today's headroom.\n- `worker/engine.mjs` defers a capped request before preflight or signing with a dated `day_cap` status, records one event per day, keeps the row queued, and lets harvests and treasury payouts run behind it.\n\n**Finding 22d4a7b… (low, failed re-queue) — fixed.** Re-queueing a decision whose row is `failed` now throws `Decision payment failed` instead of returning the dead row. No automatic paid retry was added.\n\n## Verification\n\n| Check | Result |\n|---|---|\n| `forge build --offline` | passes |\n| `forge test --offline` with the verifier's fuzz seed | 22 passed |\n| `node --test test/*.test.mjs` | 40 passed (4 new) |\n| `scripts/simulate.mjs` on local Anvil | passed, now proves worker headroom equals the compiled hook's state and a capped request uses no nonce |\n\nReports, `validation.json` and `source-integrity.json` were regenerated. README, deployment notes and ADAPTATION.md describe the new admission rule, the `day_cap` status, and the operator consequence that sixteen simultaneous BO5s need a daily cap of at least 64 IMD. `.imd-responses.json` answers both findings as fixed.\n\nOne limit remains as before: other simulation reverts, such as `DuplicateRequest` after restoring an old journal backup, still surface as `unavailable` and need operator reconciliation. That path spends nothing and is documented in ADAPTATION.md.","treeHash":"d0334cf90d41f141b5115f6204000a7f9c858806","usage":{"cachedInputTokens":2410907,"inputTokens":610,"model":"claude-fable-5-1","outputTokens":49144,"runtime":"claude","turns":67,"wallClockMs":667248}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"468e82a89b9bfe18","findings":[],"hash":"38b4f0275ff50ea5659d8ee6ca69d55114589f3bd25c363e05564409831eee7b","nodeId":"50251223-74f3-4527-b92e-0b387b0a65d5","outcome":"no_change","summary":"The response file is valid JSON. The git status command failed only because the sandbox blocks reading the global git config; it is not needed for the deliverable.\n\n**Outcome:** the manifest is unchanged and the single advisory finding is answered as disputed in `.imd-responses.json`.\n\n**What I verified**\n- The six constructor arguments in launch.json match the hook's constructor in declaration order, and the five permissions match what `getHookPermissions` returns.\n- The notes are 3815 characters, `kind` is the first field, and no hex string has odd length.\n- All 22 tests pass offline with the verifier's fuzz seed.\n\n**Why the finding is disputed**\n- The reviewer's own text says it is not a code defect and is consistent with the brief.\n- The manifest rules require the hook owner to be written as exactly `$owner` and refuse a hand-written address, so the manifest cannot be changed to satisfy it.\n- The launch facts fix this launch's owner to 0x4251…5c5d, so the placeholder resolves to the address the brief names. The notes already say so and the deployment README already instructs reading `owner()` after deployment.\n\nNo file other than `.imd-responses.json` was created or changed.","treeHash":null,"usage":{"cachedInputTokens":254380,"inputTokens":130,"model":"claude-fable-5-1","outputTokens":2592,"runtime":"claude","turns":5,"wallClockMs":69444}},{"artifacts":[],"attempt":1,"bundleHash":"89ea54320e68c32ddde3efd1ac0abc4d5bea43f96b9624880346b46d14c09cc9","device":"5231314e8cca884c","findings":[],"hash":"40f46b43d0ce5d6b9cacfb45ca70527dc2e878ef97156c816567e2b0ead7c19a","nodeId":"50251223-74f3-4527-b92e-0b387b0a65d5","outcome":"completed","summary":"Updated [launch.json](/root/.identitymd/work/dc18d5f5-7213-4b3f-b281-5f137f06ddcc/50251223-74f3-4527-b92e-0b387b0a65d5/launch.json) only. Verified constructors, permissions, pool settings and the notes limit.\n\nPassed:\n- Offline `forge build`\n- 22 Foundry tests with the specified fuzz seed\n- 36 Node tests\n- Accounting/recovery and production-router checks in both currency orderings\n\n[Policy 30](https://api.imd.fun/launch/policies) and [0.5 IMD request pricing](https://api.imd.fun/requests/capabilities) remain unchanged.","treeHash":"025c6782547b18cc912250318cc81cf9ee300e17","usage":{"cachedInputTokens":549504,"inputTokens":51138,"model":"gpt-6-astra","outputTokens":7407,"runtime":"codex","turns":5,"wallClockMs":176133}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"a7b8747ac077deb9","findings":[{"citation":"resolved","description":"Boundary x invariant seam. The admission invariant the brief asks for is 'reserve up to eight paid decisions per BO5 before admitting a duel', i.e. an admitted duel's decisions can be paid. admit() enforces that only against requestBalance (worker/budget.mjs:23, store.reserveMatch at worker/store.mjs:28: reserved()+value+minimumReserve <= observedBalance). The deployed contract enforces a second, independent bound: CabalFundingHook.payRequest reverts LimitExceeded when spentToday + amount > dailyLimit (src/CabalFundingHook.sol:289). The worker knows that bound (config.dailyLimitAtomic must equal the on-chain dailyLimit, worker/chain-adapter.mjs:51) but neither admit() nor queueDecision() counts the day's admitted budget against it. With the example simulation config (dailyLimitAtomic = 10 IMD = 20 decisions at 0.5 IMD) and 100 IMD on chain, admit() accepts 16 matches (64 IMD reserved, 128 decisions) although at most 20 can be paid in that UTC day. When the 21st payment is prepared, chain-adapter.prepare's estimateGas reverts with LimitExceeded, engine.runOnce catches it as a generic 'worker_error' and returns 'unavailable' (worker/engine.mjs:84-88) leaving the job in 'preparing'; the next tick resets it to 'queued' (worker/engine.mjs:25) and, because next() orders by state then created, picks the same job again. The reserved job therefore blocks every later request for every other admitted match until the UTC day rolls over, the match cannot be closed (store.closeMatch throws while a job is pending), and nothing halts or records the cap as the cause. No funds are lost and no duplicate payment is possible; the defect is that admission promises a reservation that the contract's own cap cannot honour, and the engine's failure path turns one cap hit into head-of-line starvation. Minimal fix that keeps the design: in admit(), also require (sum of reservations created in the current UTC day + spentToday on chain + price*8) <= dailyLimitAtomic, and in the engine classify a simulated LimitExceeded as a distinct 'day_cap' state that does not reset-and-retry the same job every tick (or defers the job behind other queued work until the day boundary).","line":23,"path":"worker/budget.mjs","reproduction":"Reproduced with test/scratch/daycap.test.mjs (node --test), which drives the real FundingStore, FundingEngine and DuelFundingBudget with an adapter whose prepare() throws exactly when the contract would revert LimitExceeded (spentToday + amount > dailyLimit). State: requestBalance 100e18, config.dailyLimitAtomic 10e18 (the value scripts/simulate.mjs uses), price 5e17, minimumImdReserveAtomic 0. Steps: admit('match-1'..'match-16') -> all succeed, store.reserved() == 64e18. Pay 20 decisions (match-1 and match-2 fully, match-3 rounds 1-2) -> each runOnce returns 'submitted' then 'confirmed', adapter.spentToday == 10e18. Enqueue match-3 round 3 seat 0 (funds reserved and present on chain). Expected: the decision is paid, since the match was admitted with its full 8-decision budget. Actual: runOnce() returns {state:'unavailable'}; store.next().state is 'preparing' with no raw; five further runOnce() calls all return 'unavailable' (reset to queued and re-prepared each tick); a decision for match-4 enqueued afterwards stays 'queued' and is never reached; store.closeMatch('match-3') throws 'Pending or ambiguous payments must be reconciled'; store.blocked() is false, so no halt or diagnostic names the day cap. The same sequence on the real chain adapter produces the identical loop because client.estimateGas reverts on the LimitExceeded() custom error.","severity":"low","snippet":"    this.store.reserveMatch(matchId,price*8n,balances.requestBalance,BigInt(this.config.minimumImdReserveAtomic));","title":"Duel admission reserves 8 decisions against the on-chain balance only; the contract's UTC-day cap is ignored, so admitted matches strand and the queue head loops for the rest of the day"},{"citation":"resolved","description":"Boundary x precision seam, recorded for coverage because the Math Precision guide's zero-rounding check names it; the behaviour is exactly the floor/ceil the brief specifies and is not a defect to fix. feeFromGross(gross) = gross/25 truncates to 0 for any exact-input buy of 1..24 wei IMD, so such a swap pays no custom tax; splitting a trade into 24-wei pieces to avoid the tax costs roughly 150k gas per piece for 24 wei (2.4e-17 IMD) of tax saved, so it cannot be used to evade a material amount. In the other direction feeFromNet(net) = ceil(net/24) adds 1 wei to every exact-output trade whose pool-side IMD amount is 1..23 wei, so a 1-wei-CABAL exact-output buy whose pool input is 3 wei pays 4 wei in total (25% rather than 4%), always in the protocol's favour. Both directions are bounded to 1 wei of rounding per swap, never leak value out of the hook, and leave the ledger invariants (totalFees == pendingFees + totalHarvested, cumulative floor(totalHarvested*7/10) split) intact. No change recommended; documenting the atomic-unit edge is sufficient.","line":141,"path":"src/CabalFundingHook.sol","reproduction":"test/scratch/DustProbe.t.sol (forge test --offline --match-path test/scratch/DustProbe.t.sol -vv), IMD as currency0, 1e25 liquidity in [-600,600] at price 1:1. Exact-input buy of 24 wei: swapper IMD delta -24, hook fee 0. Exact-input buy of 25 wei: delta -25, fee 1. Exact-output sell requesting 1 wei IMD: swapper receives 1 wei, pays 4 wei CABAL, fee 1 (pool output 2 wei, hook keeps 1). Exact-output buy of 1 wei CABAL: swapper IMD delta -4, fee 1 (pool input 3 wei, hook adds ceil(3/24)=1). Exact-input sell of 1 wei CABAL: IMD delta 0, fee 0. An exact-input buy of int128.max+1 reverts before any accrual.","severity":"info","snippet":"        return gross / 25;","title":"Atomic-unit tax rounding: exact-input swaps below 25 wei IMD are untaxed and exact-output dust pays far more than 4% (spec-consistent, economically irrelevant)"}],"hash":"977d31a96d90c1c836ff796c4e8e258ed330c6ee49c77fb166e59694e991aba5","nodeId":"a5fb6796-5ce0-410f-be4a-959ff4a116ab","outcome":"completed","summary":"The review is complete and recorded in `.imd-findings.json` at the repository root. Both snippet anchors match the tree, the JSON parses, and git reports no tracked files changed (scratch tests and the findings file are untracked, as intended).\n\n**What I checked.** I read the Senior Auditor's Mindset and the Math Precision, Boundary and Numerical Gap guides, then traced every CabalFundingHook and LaunchToken entry point for rounding direction, zero-rounding, overflow, downcasts, unsigned subtraction, and the seams between precision, boundaries and the ledger invariants. I rebuilt offline, reran the 22 Foundry tests (1,536 fuzz cases) and 36 Node tests, all passing, and added two scratch probes to pin exact numbers.\n\n**Result: the on-chain arithmetic holds.**\n- The four tax formulas match the brief exactly, every pre-charged side is full-fill checked against the raw AMM delta, and all amounts are bounded to int128 before use.\n- The cumulative split formula is exactly floor(7T/10) with no overflow and no chunk dependence. Slither's divide-before-multiply line is a false positive.\n- The ledger conservation invariants hold on every path, including donations and failed intake calls.\n- The permission mask, constructor order and pool fee in launch.json are consistent with the hook.\n\n**Findings (2):**\n1. **Low, worker/budget.mjs:23.** Duel admission reserves 8 decisions against the on-chain balance only and ignores the contract's UTC-day cap. Reproduced with a Node scratch test against the real store, engine and budget: 16 matches admit against a 10 IMD day cap, the 21st payment reverts LimitExceeded, and the engine resets and retries that one job every tick, starving every other admitted match for the rest of the day with no halt or diagnostic. No funds are lost. The suggested fix adds the day cap to the admission bound and gives the engine a distinct non-looping state for it.\n2. **Info, src/CabalFundingHook.sol:141.** Atomic-unit rounding edge: exact-input buys below 25 wei are untaxed, and 1-wei exact-output trades pay up to 4 wei. This is exactly the specified floor/ceil behaviour, bounded to 1 wei per swap in the protocol's favour, and needs no change.\n\n**Coverage.** All 13 listed entry points have rows (12 hold, worker admission carries finding 1), plus invariant and manifest rows. One row is marked unreached: I did not re-execute the Anvil fork of the production router within the time budget, so quote/execution parity rests on the supplied reports.","treeHash":null,"usage":{"cachedInputTokens":1342171,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":40416,"runtime":"claude","turns":29,"wallClockMs":557480}},{"artifacts":[],"attempt":1,"bundleHash":"8d8da37912ea506fd76b82db5bdce7712981a549e95fbfc37801781492cfc138","device":"b94d8a9fa9123fb1","findings":[],"hash":"a98dad36fe291a6f655f4596c26a4570eb00ef73eea7835b721632a6bca2079d","nodeId":"77cd0859-da12-4235-813c-cd9c42309dc6","outcome":"completed","summary":"Added tests and review evidence entirely under `test/`.\n\n- `forge build` and all 57 Foundry results pass.\n- 3,536 fuzz cases and 49,152 invariant calls pass.\n- All 36 Node tests, local recovery E2E, and router forks in both currency orderings pass.\n- Policy 30 and the 0.5 IMD request price remain unchanged.\n\nNo new implementation defects confirmed. Production files remain untouched; deployment and operation remain disabled.\n\nDetails: `test/REVIEW.md` and `test/review-results/`.","treeHash":"6455a1b2fe7f688aa97f061756bad2ea8909e39b","usage":{"cachedInputTokens":3615616,"inputTokens":171312,"model":"gpt-6-astra","outputTokens":38696,"runtime":"codex","turns":9,"wallClockMs":814933}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"ea89e16822824c6f","findings":[{"citation":"resolved","description":"Merged from three specialist reports (audit_economics medium, audit_flow finding 1, audit_math finding 1); same root cause, reproduced. The brief's admission rule is 'reserve up to eight paid decisions per BO5 before admitting a duel'. DuelFundingBudget.admit reserves 8*price only against the hook's requestBalance plus the operating reserve (worker/budget.mjs:23 -> worker/store.mjs:28). The deployed contract enforces a second, independent bound the worker already knows (config.dailyLimitAtomic must equal the on-chain dailyLimit, worker/chain-adapter.mjs:51): CabalFundingHook.payRequest reverts LimitExceeded when `spentToday + amount > dailyLimit` (src/CabalFundingHook.sol:289). Neither admit() nor queueDecision() nor FundingEngine.runOnce counts the day's reservations or confirmed spend against that cap. Once the cap is reached, chain-adapter.prepare's estimateGas reverts with LimitExceeded; engine.runOnce's generic catch records 'worker_error' and returns 'unavailable' (worker/engine.mjs:84-88), leaving the job in 'preparing' with no raw bytes. The next tick resets it to 'queued' (worker/engine.mjs:25) and, because store.next() orders by state then created, picks the same oldest job again. The capped decision therefore blocks every later request of every admitted match for up to 24 hours, closeMatch throws for those matches, store.blocked() stays false and no event names the day cap as the cause. The same loop occurs for any other simulation revert of a signed-later request (for example DuplicateRequest when the journal is restored from an older backup). No funds are lost, no duplicate payment is possible, no gas is spent; the defect is that admission promises a reservation the contract's own cap cannot honour, and the engine turns one cap hit into head-of-line starvation. Minimal fix preserving the design: in admit() also require (reservations created in the current UTC day + on-chain spentToday + 8*price) <= dailyLimitAtomic (expose spentDay/spentToday in the adapter ABI), and in the engine classify a simulated LimitExceeded as a dated 'day_cap' wait that defers the job instead of resetting and re-preparing it every tick.","line":23,"path":"worker/budget.mjs","reproduction":"test/scratch/review-daycap.test.mjs (node --test), run against the real FundingStore, FundingEngine and DuelFundingBudget with an adapter whose prepare() throws exactly when the contract would revert LimitExceeded (spentToday + amount > dailyLimit). State: on-chain requestBalance 100 IMD, dailyLimit 4 IMD (8 decisions/day), price 0.5 IMD, minimumImdReserveAtomic 0, config.dailyLimitAtomic 4e18. Steps: admit('match-A'); admit('match-B') -> both succeed, store.reserved() == 8e18 although only 4e18 can be spent today. Enqueue and pay the 8 decisions of match-A (each runOnce 'submitted' then 'confirmed'; adapter.spentToday == 4e18). Enqueue match-B round 1 seat 0 -> state 'queued'. Expected: the decision is paid (its match was admitted with a full eight-decision reservation) or admission of match-B is refused / reported as capped. Actual: four consecutive runOnce() return 'unavailable'; the row has raw == null (never signed); a match-B round 1 seat 1 decision enqueued afterwards is never reached (store.next() keeps returning the capped row); store.blocked() == false; store.closeMatch('match-B') throws 'Pending or ambiguous payments must be reconciled'; requestBalance is still 96 IMD. Contract side: test/CabalFundingHook.t.sol testPaidRequestsDeduplicateAndEnforceCaps already shows payRequest reverting LimitExceeded at the cap with funds remaining.","severity":"medium","snippet":"    this.store.reserveMatch(matchId,price*8n,balances.requestBalance,BigInt(this.config.minimumImdReserveAtomic));","title":"Duel admission and the funding engine ignore the hook's UTC-day cap: admitted matches get unpayable decisions and the capped job starves every later request until the next UTC day"},{"citation":"resolved","description":"From audit_flow finding 2, reproduced. The decision key is deterministic per (chainId, contract, match, round, seat). When a signed payRequest is included but reverts on chain (Expired after an inclusion delay past the 1800 s deadline, or LimitExceeded from the day cap in finding 1), engine.runOnce patches the job to state 'failed' with 'no automatic paid retry' (worker/engine.mjs:36). store.next() excludes 'failed', so the job is never processed again, yet a reverted call wrote nothing on chain, so the key is still unused and the payment is possible. A later store.enqueue / DuelFundingBudget.queueDecision with identical input hits the `if(old)` branch and returns the failed row without error, so the caller sees a successful queue result while nothing will ever be paid, and the match keeps a reservation it cannot use. 'No automatic paid retry' is the stated design; silently reporting success for a dead decision is not, and it leaves the game coordinator with no signal that the decision is unfunded. Minimal fix: when old.state === 'failed', throw (or return a result the caller must inspect) so queueDecision fails loudly, leaving any explicit re-queue to an operator action.","line":38,"path":"worker/store.mjs","reproduction":"test/scratch/review-daycap.test.mjs second test. State: FundingStore with match-A reserved (8 x 0.5 IMD); row = enqueue(decisionKey(4663, contract, 'match-A', 1, 0), amount 5e17, body '{\"v\":1}') -> state 'queued'. Apply exactly what engine.mjs:36 writes on observed.state === 'reverted': store.patch(row.id, {state:'failed', error:'Confirmed transaction reverted; no automatic paid retry'}). Then enqueue the same (id, match, amount, body). Expected: an error (or a distinguishable result) because the decision is unfundable. Actual: returns the existing row with state 'failed' and throws nothing; store.next() is undefined; engine.runOnce() returns {state:'idle'}; the decision can never be paid without editing the journal.","severity":"low","snippet":"      if(old) { if(old.match_id!==match || old.amount!==price.toString() || old.payload!==JSON.stringify({body})) throw Error('Decision key reused with different input'); return old; }","title":"A decision whose payment transaction reverted is permanently 'failed', and a later enqueue of the same decision returns the failed row as if it were queued"},{"citation":"resolved","description":"From audit_permissions, reproduced against the constructor. The brief fixes the hook owner to 0x4251627b712762b6e81799698a05c6c9dff35c5d and lists the fifth constructor argument as $owner, which the manifest writes exactly. Per the manifest rules, $owner resolves to the owner the launch request names, else the payer. The hook stores it immutably (src/CabalFundingHook.sol:40, 98) and it is the only address that can call configureWorker (src/CabalFundingHook.sol:107-110, 260); nothing can change it afterwards. The treasury is a literal and is unaffected. deployment/launch-terms.json and deployment/artifacts.json record the owner as 0x4251…5c5d, but those are not fields the deployer reads from the manifest. Not a code defect and consistent with the brief; it is the one input the submitted source cannot enforce, so the launch request's owner must be confirmed before deployment and owner() read afterwards, as deployment/README.md already instructs.","line":10,"path":"launch.json","reproduction":"State: a launch request for this manifest that names no owner, paid by P != 0x4251…5c5d. The deployer resolves $owner = P and runs CabalFundingHook(manager, factory, IMD, Intake, P, 0x4251…5c5d). Afterwards hook.owner() == P; configureWorker(w, 5e17, 1e18, true) from 0x4251…5c5d reverts Unauthorized(); the same call from P succeeds; no function changes owner. Expected per brief: owner() == 0x4251627b712762b6e81799698a05c6c9dff35c5d. Pre-deployment check: the launch request's owner field; post-deployment check: owner().","severity":"info","snippet":"      \"$owner\",","title":"Hook owner is bound through the $owner placeholder, so configureWorker authority depends on the launch request naming 0x4251…5c5d as owner"}],"hash":"cf7bb0376e93aa2dab84f753304912f91dfd837dc260b9088af1f52d69e0e4c8","nodeId":"abdeafc3-bad2-4962-acba-9a318cb35406","outcome":"completed","summary":"The review is complete and written to `.imd-findings.json`. No tracked file was changed.\n\n**Outcome.** The Solidity is sound. The two reproducible defects are in the Node worker, and the one kept info item is a deployment input the source cannot enforce.\n\n**What I verified myself**\n- Full offline Foundry suite passes (57 tests, including both currency-ordering invariant suites) and the 36 worker, oracle-client and manifest Node tests pass.\n- Every hook entry point traced: fee math matches the brief's floor/ceil rules in both orderings, pre-charged modes enforce full fills, the 70/30 split is cumulative and exact, every privileged path is limited to worker, caps and enablement, and the unlock callback can only carry the hook's own data. Slither's reentrancy lines are false positives behind `nonReentrant` and the manager-only callback.\n- Manifest and constructor: six arguments in declaration order, permission list equals the 0x20cc mask, 12500 is the only policy 30 fee tier so `beforeInitialize` cannot refuse the launch pool, and the factory fork review shows the real factory as initializer.\n\n**Findings kept (3)**\n1. **Medium, merged from three specialists.** Duel admission reserves eight decisions against `requestBalance` only and ignores the hook's UTC-day cap. Once the cap is hit, the engine's generic error path resets and re-prepares the same capped job every tick, starving every later decision of every admitted match until the next UTC day. Reproduced with a scratch Node test driving the real store, engine and budget.\n2. **Low.** A decision whose payment reverted is permanently `failed`, and re-enqueueing it returns the failed row without error, so the coordinator sees success for an unfundable decision.\n3. **Info.** The hook owner comes from the `$owner` placeholder, so configureWorker authority depends on the launch request naming the brief's owner address.\n\n**Dropped.** The hookless-pool, spot-balance eligibility and 1-wei rounding items match the brief's explicit design and are recorded in the coverage rows, not as defects. No specialist proof needed to be run since none attached a Solidity proof and no finding reached high severity.\n\n**Coverage.** All 13 entry points answered `holds` with reasons, plus two invariant rows. The scratch reproduction lives in `test/scratch/review-daycap.test.mjs`, which is outside the submitted tree.","treeHash":null,"usage":{"cachedInputTokens":1047790,"inputTokens":322,"model":"claude-fable-5-1","outputTokens":27754,"runtime":"claude","turns":25,"wallClockMs":413201}},{"artifacts":[],"attempt":1,"bundleHash":"36fe093a9e05bcf14c84a1aba832458e31ba7458819352929ae33d5ac142d019","device":"bd7adba3a8045853","findings":[],"hash":"d1c021517645d61307d9d0ee126c74ecc9f5f4c0470d205bce08c1dfec694f1e","nodeId":"77cd0859-da12-4235-813c-cd9c42309dc6","outcome":"completed","summary":"All tests pass after this revision. With the verifier's fuzz seed, `forge build` and `forge test` ran 63 tests in 8 suites with 0 failures. I found no new defect in the implementation, so I did not write `.imd-findings.json`. Every change is under `test/`; `src/`, the worker, the manifest, deployment files and configuration are untouched.\n\n**What changed and why**\nSince my last round, another contributor accepted a worker change. Before admitting a match, the worker now works out how much of the hook's daily spending limit is left. It reads `dailyLimit`, `spentDay` and `spentToday` from the contract, defers a request that limit would block, and refuses to re-queue a payment that already failed. No contract code changed. I added `test/CabalFundingHookDayCap.t.sol` to pin the contract behaviour that worker logic relies on:\n- **After UTC midnight with no payment yet:** the views still show the previous day, and the contract still allows a full new day's limit.\n- **Failed payment on a new day:** it leaves the old day recorded, and the limit is intact afterwards.\n- **Owner lowers the limit below today's spend:** nothing is left for the day and nothing resets; raising it again allows exactly the difference.\n- **Limit not a multiple of the price:** a limit one wei short of 4 IMD pays seven decisions, not eight.\n- **Sixteen matches:** a 64 IMD limit pays all 128 decisions (sixteen matches × eight) in one day; 63.5 IMD pays only 127.\n- **Fuzz test (512 runs):** a copy of the worker's formula never promises a payment the contract then rejects, with the worker's clock anywhere from one hour behind to five minutes ahead of the chain. Away from the five minutes after midnight it matches the contract exactly.\n\nMy first draft of two of these tests was wrong (one checked inside the worker's five-minute clock window, one ran out of deposited IMD). I fixed the tests, not the contract.\n\n**Reruns after the worker change**\n- **Worker tests:** 40/40 pass.\n- **Local end-to-end run:** passes. A 1,000 IMD taxable trade gives 40 IMD custom fees, split 28 to requests and 12 to treasury. Eight paid decisions cost 4 IMD, and nothing was paid twice. The worker's daily-limit figure matched the compiled hook, and a request over the limit was deferred without sending any transaction.\n- **Live policy check (Robinhood block 92141844):** policy 30 is unchanged, with the 12500 pool fee and the 2,500 IMD opening valuation. The request price is still 0.5 IMD and the live capabilities match.\n- **Production-router fork review (block 92141917):** passes in both currency orderings, and the runtime hashes match the pinned ones.\n\nThe refreshed results are in `test/review-results/`, and `test/REVIEW.md` is updated.\n\n**Remaining limits (in `test/REVIEW.md`)**\n- **Worker clock:** the daily-limit check allows the worker's clock to run at most five minutes ahead of the chain. A clock further ahead across midnight would admit decisions the contract still blocks. Those payments revert before any IMD moves, so it delays payments rather than losing funds. Keep the host clock synchronized.\n- **Daily limit:** the on-chain limit must be at least 64 IMD for sixteen simultaneous matches, and a larger request fund does not raise it.\n- **Before live operation:** deployment and IMD admission, owner configuration, reserve and gas funding, and a paid live validation are all still required. The game integration is still separate.\n\nNothing has been committed or pushed.","treeHash":"00d40edf7af09bf6f7ab0452b2dc4e88accac5e7","usage":{"cachedInputTokens":1507354,"inputTokens":44,"model":"claude-opus-5-5","outputTokens":16264,"runtime":"claude","turns":22,"wallClockMs":312304}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"0476c44a80aa9574","findings":[{"citation":"resolved","description":"queueDecision is the only validation gate before a decision is paid on-chain through payRequest. It allows a question of up to 8,000 characters. The oracle.request body schema (PaidOracleInput) limits question to 1-2,000 characters: the pinned oracle-consumer reference says 'question 1-2000 characters', imd.fun/docs says 'question Required. 1-2,000 characters', and the live free check at POST https://api.imd.fun/requests/check answered a 2,100-character question with {\"error\":\"invalid_request\",\"detail\":\"question: Too big: expected string to have <=2000 characters\"}. A refused body is status 1 and 'spends the price and never reaches the callback' (reference, 'When no answer comes'). The hook itself only bounds body.length <= 16384 bytes, so the contract cannot catch it either. The game server freezes 'server facts' into the question text, so a long question is an ordinary input, not an attack. Fix: change the bound to input.question.length>2000 (and count characters the way the door does), or better, reject in queueDecision whatever POST /requests/check refuses.","line":29,"path":"worker/budget.mjs","reproduction":"State: worker configured and paymentsEnabled, requestBalance >= 0.5 IMD, match reserved. Call budget.queueDecision({matchId:'m',round:1,seat:0,input:{v:1,chainId:4663,question:'x'.repeat(2500),answerType:'uint256',evidence:'panel',panelSize:5,quorum:4,toleranceBps:0,guards:{min:'0',max:'3'}}}). Expected: rejected locally, nothing paid. Actual: the job is enqueued, FundingEngine.runOnce prepares and broadcasts payRequest(key, body, 0.5 IMD, deadline); the hook debits 0.5 IMD to the Intake and the request is refused off-chain as invalid_input (question > 2000), with no refund. Each such decision loses 0.5 IMD; the store marks it 'paid' and the match reservation is consumed.","severity":"medium","snippet":"      || typeof input.question!=='string' || input.question.length<20 || input.question.length>8000) throw Error('Invalid frozen decision input');","title":"Worker accepts questions up to 8,000 characters although the paid oracle door refuses anything over 2,000, so the 0.5 IMD is spent and the decision is never answered"},{"citation":"resolved","description":"The body is built and queued without running the free wording check (POST /requests/check) that the pinned oracle-consumer reference requires before paying ('Check the question before you bake it ... A question the wording screen refuses is refused on every ask ... each spending the price'), and without allowAmbiguous/definitions that pin the reading. The worker's own fixture question from test/worker.test.mjs ('Choose an upgrade from these frozen server facts.') was sent to the live check on 2026-10-18 with panelSize 5 and evidence 'panel'; the service judged it and returned blockers [{code:'ambiguous_question'},{code:'not_answerable', detail:'No one can answer this from public sources today: it may ask about the future, a matter of opinion, or something that is not public.'}]. A duel upgrade choice is a matter of opinion by construction, so the same refusal applies to the real decisions unless the body pins the reading (definitions + allowAmbiguous:true) and the check returns no blockers. A refused body is status 1, the 0.5 IMD is forwarded to the IMD payee and nothing is refunded (reference 'When no answer comes'; docs 'Check the body with POST /requests/check, which is free, and send it only when blockers is empty'). The daily cap only bounds the rate of loss. Also note the live limits now report minPanelSize 5 (the reference said 2); the body's panelSize 5 sits exactly on the floor. Fix: in queueDecision (or in the engine before prepare), POST the exact body to /requests/check and refuse to enqueue/pay when blockers is non-empty; add definitions that pin the reading and set allowAmbiguous:true only when the definitions already pin it; treat a changed limits.minPanelSize like a changed price (halt).","line":31,"path":"worker/budget.mjs","reproduction":"Input: budget.queueDecision({matchId:'m',round:1,seat:0,input:{v:1,chainId:4663,question:'Choose an upgrade from these frozen server facts for seat zero, round one of this duel.',answerType:'uint256',evidence:'panel',panelSize:5,quorum:4,toleranceBps:0,guards:{min:'0',max:'3'}}}) with funding configured. Expected: a question the oracle will refuse is never paid. Actual: enqueued and paid (0.5 IMD leaves requestBalance via payRequest); the same question sent to POST https://api.imd.fun/requests/check returns judged:true with blockers ambiguous_question and not_answerable, i.e. the paid request ends in status 1 with the price spent. Verify with: curl -s -X POST https://api.imd.fun/requests/check -H 'content-type: application/json' -d '{\"action\":\"oracle.request\",\"input\":{\"question\":\"Choose an upgrade from these frozen server facts for seat zero, round one of this duel.\",\"panelSize\":5,\"evidence\":\"panel\"}}'","severity":"medium","snippet":"    const body=JSON.stringify({v:1,chainId:this.config.chainId,question:input.question,window:{hours:24},answerType:'uint256',evidence:'panel',panelSize:5,quorum:4,toleranceBps:0,validForSeconds:300,guards:{min:'0',max:'3'}});","title":"Worker pays for a question before any wording check; decision-style questions are refused by the oracle's screens after payment (price spent, no answer)"},{"citation":"resolved","description":"The contract verifies the exact debit (line 218) but never checks amount against Intake.priceOf(ORACLE_ACTION, imd). The live Intake rule (imd.fun/docs, 'Pay on chain') is: the amount is 'forwarded whole to the treasury in the same call. Less reverts PriceNotMet', so any overpayment is simply lost. The worker's assertReady refuses to run unless maxRequestAmount equals its configured price, so the overpayment needs an owner cap above 0.5 IMD plus a worker that sends more than the price; it is a hardening gap, not a bypass. The IOracleIntake interface in the hook omits priceOf (selector 0x51102885, present in the deployed Intake runtime at 0xa43e...de82; cast call returned 500000000000000000 on 2026-10-18). Fix: add priceOf to the interface and require amount == intake.priceOf(ORACLE_ACTION, address(imd)) inside payRequest (keeping the owner cap as an additional bound). This keeps the brief's immutable action/intake and exact-debit check.","line":216,"path":"src/CabalFundingHook.sol","reproduction":"test/scratch/PayRequestPriceBinding.t.sol (forge test --offline --match-path test/scratch/PayRequestPriceBinding.t.sol): an intake that mirrors the live rule (amount >= 0.5 IMD accepted and kept whole). Sequence: deposit(2 ether); configureWorker(worker, 1 ether, 10 ether, true); payRequest(key, \"{}\", 1 ether, deadline). Expected if bound to price: revert. Actual: succeeds, intake.received() == 1 ether and requestBalance drops by 1 IMD for one 0.5 IMD decision.","severity":"low","snippet":"        requestId = intake.request(ORACLE_ACTION, body, IOracleIntake.Callback(address(0), bytes4(0)), address(imd), amount);","title":"payRequest does not bind amount to the intake's current price; any amount up to maxRequestAmount is forwarded whole with no refund"},{"citation":"resolved","description":"The only outflow from requestBalance (prefunding, donations and 70% of every harvested tax) is payRequest to the immutable intake with the constant action id. The pinned oracle-consumer reference warns that the action version and price move ('A new action version (oracle-2) or a moved price must not need a redeploy') and that an action no longer sold reverts ActionNotSold. The brief explicitly asks for an immutable intake and no owner sweep, so this is the requested design and a trust/lifecycle assumption rather than a permission bypass; it is reported so the requester decides knowingly, and because the README's own residual list (item 6) says the same. Reservation funding of 64 IMD plus operating reserve would be the stranded amount at the planned scale. Possible mitigations that preserve the brief: cap prefunding to what the worker needs; or (scope decision) allow the owner to migrate only to a successor Intake that the IMD registry publishes, never to an arbitrary payee.","line":30,"path":"src/CabalFundingHook.sol","reproduction":"State: Intake owner removes or re-versions oracle.request@oracle-1 (or the Intake is superseded), hook holds requestBalance = R > 0. Any payRequest reverts (ActionNotSold or PriceNotMet). There is no function that moves requestBalance anywhere else: harvest, syncDonations and deposit only increase it; payTreasury only spends treasuryBalance; configureWorker cannot change intake or action. Expected: an operator path to recover or re-route R. Actual: R is locked forever.","severity":"low","snippet":"    bytes32 public constant ORACLE_ACTION = bytes32(\"oracle.request@oracle-1\");","title":"Request fund is permanently stranded if the Intake stops selling oracle.request@oracle-1 (immutable intake + constant action, no exit path)"},{"citation":"resolved","description":"prepare() bakes deadline = now + 300 s into the signed bytes. The engine deliberately never re-signs with a higher fee and rebroadcasts the identical bytes while the receipt is absent. If inclusion takes longer than 300 s (fee below market, RPC outage, sequencer backlog), the transaction reverts with Expired when it finally lands; reconcile returns 'reverted', the engine marks the job failed ('no automatic paid retry'), the duel decision is never purchased, and gas is spent. The match reservation then releases on closeMatch, so the game's funding promise for that decision silently breaks. This is a liveness defect, not a fund-loss one (IMD is not debited on revert). Fix: either size the deadline to the configured confirmation policy (for example 30-60 min) since the duplicate key and daily cap already prevent double payment, or let the engine re-prepare an expired, confirmed-reverted request under the same decision key (safe because paidRequests[key] prevents a double charge).","line":65,"path":"worker/chain-adapter.mjs","reproduction":"State: funding live, one queued decision. Make the broadcast sit unmined for > 300 s (set walletClient to a maxFeePerGas below the current base fee, or stall the RPC). Then let it mine. Expected: the decision is eventually paid once. Actual: receipt.status == 'reverted' (hook revert Expired at line 203), engine returns {state:'failed'}, reserved() no longer counts it after closeMatch, and no further attempt is made; the player's decision was never submitted although funds were available.","severity":"low","snippet":"      if(row.kind==='request') {functionName='payRequest'; args=[row.id,toHex(JSON.parse(row.payload).body),BigInt(row.amount),BigInt(Math.floor(Date.now()/1000)+300)];}","title":"Signed payRequest carries a 300-second deadline with no fee replacement; a transaction unmined for 5 minutes reverts Expired on inclusion, is marked failed and the decision is never paid"},{"citation":"resolved","description":"The hook has no knowledge of the CABAL token address (it is deployed in the same launch), so the bound pool is whichever pool the LaunchFactory initializes first with this hook, IMD on one side and fee 12500. Only the IMD deployer can call launch (NotDeployer), and the fork evidence shows the factory is the initializer sender, so no outsider can bind a different pool; the caller restriction and selector are correct and the launch rule (beforeInitialize restricted to the PoolManager, factory as sender) is satisfied. Recorded as a trust assumption for the adapter: verify after deployment that boundPool equals the PoolId of positionOf(launchNumber) and that pool.fee in launch.json is 12500 (policy 30 feeTiers [12500], confirmed live on 2026-10-18).","line":83,"path":"src/CabalFundingHook.sol","reproduction":"Not a failure path. Check: read hook.boundPool() and compare with keccak256(abi.encode(positionOf(launchNumber).key)); any other IMD/12500 pool initialized by the factory with this hook first would be accepted (manager.initialize(key with tickSpacing 120) from the factory binds, as the existing test testCannotCallCallbacksOrBindAnotherPool only exercises a non-factory caller).","severity":"info","snippet":"        if (sender != factory || initialized || address(key.hooks) != address(this) || key.fee != 12500","title":"beforeInitialize binds the first factory-initialized IMD pool with fee 12500 and does not pin the launch token or tickSpacing (trust in the factory, by design)"},{"citation":"resolved","description":"Checked against the launch's forbidden list: LaunchToken has no mint/burn/pause/blacklist/owner (plain OpenZeppelin ERC20, constructor-only mint of 1e27 to msg.sender, read-only PVP_MINIMUM_HOLDING = 50000e18 and isPvpEligible); CabalFundingHook has no DELEGATECALL/CALLCODE/SELFDESTRUCT, no proxy or initializer, every address is an immutable constructor input, and the only post-deployment setter is configureWorker, which the brief explicitly requires (worker and caps set by the owner after operational review, deployed with paymentsEnabled=false and worker unset). Trust assumptions to record for the adapter: the owner can raise maxRequestAmount/dailyLimit without bound, so a compromised owner key plus worker key can drain requestBalance to the IMD payee at the Intake (never to the attacker); configureWorker cannot clear the worker address (worker_ != 0 is required), only disable payments; both caps are UTC-day based, so up to 2x dailyLimit can be spent across midnight. None of these are bypasses of the stated design.","line":193,"path":"src/CabalFundingHook.sol","reproduction":"Owner-only paths: vm.prank(stranger); configureWorker(...) reverts Unauthorized (existing test testUnauthorizedDisabledExpiredAndEmptyFund). Cross-midnight spend: configureWorker(w, 0.5e18, 1e18, true); two payRequest at 23:59:59 UTC, then two more at 00:00:00 UTC succeed (spentDay resets), i.e. 2 IMD in two seconds against a 1 IMD/day cap. Expected by the brief: per-UTC-day cap (matches); noted so the operator sizes dailyLimit accordingly.","severity":"info","snippet":"    function configureWorker(address worker_, uint256 perRequest_, uint256 perDay_, bool enabled_) external onlyOwner {","title":"Post-deployment configuration surface is limited to configureWorker (worker, per-request cap, daily cap, enable); no tax, split, treasury, intake, pause, mint, sweep, proxy, delegatecall or selfdestru"},{"citation":"resolved","description":"Rechecked on 2026-10-18 against https://api.imd.fun/launch/policies: the current univ4_hook policy for chain 4663 is still version 30 (createdAt 2026-10-06), feeTiers [12500], initialMarketCaps[IMD 0x5f7b...7127] = 2500e18, pairedCurrencyAllowlist includes IMD, gasCeilingWei 1e16, contributorPoolBps 1000, contributorLockSeconds 3600. Its liquidityBps is 8000 and treasuryBps 1000, while the brief specifies poolBps 8800 / 10% swarm / 2% remainder; the deployed factory's SWARM_BPS() is 1000 and the fork test launched successfully with poolBps 8800, so the factory accepts the brief's split, but the deployer copies the policy's split into the deployment task (reference, 'Deployment inputs have separate owners'). This is a service/configuration gap to confirm at admission, not a code defect. Other live checks: Intake.priceOf(oracle.request@oracle-1, IMD) = 0.5 IMD; LaunchFees.poolFee = 12500 and REQUESTER_BPS = 8000; runtime keccak256 of factory, PoolManager, IMD, Intake, UniversalRouter, V4Quoter and Permit2 match the values pinned in reports/ and test/fixtures/ provenance.","line":91,"path":"README.md","reproduction":"curl -s https://api.imd.fun/launch/policies | select kind univ4_hook, chainId 4663: version 30, liquidityBps 8000, treasuryBps 1000, contributorPoolBps 1000. Expected per brief: pool 8800 bps, remainder 200 bps to 0x4251627b712762b6e81799698a05c6c9dff35c5d. If the deployment task is built from the policy split instead of the brief, the pool receives 80% and 10% goes to the policy's treasury role, contradicting the brief.","severity":"info","snippet":"3. Final IMD manifest/admission and revalidation against the actual launched pool. Factory deployment, initializer caller, constructor support, CREATE2 mask `0x20cc`, platform fee collection and both currency orderings are covered on a local fork. The production router and quoter are covered by the subsequent review; the actual browser wallet flow is not. The manifest must select `univ4_hook` / LaunchFactory, not `evm_project` / ProjectFactory's initialization-only guard. No provider approval or launch workflow was requested. The final source must be pinned to a public repository and commit before an imported launch request can use it.","title":"Launch policy 30 is unchanged (fee tier 12500, IMD opening cap 2,500 IMD) but its default supply split (80/10/10) differs from the brief's 88/10/2; confirm the frozen allocation uses poolBps 8800"},{"citation":"resolved","description":"Read in full: src/CabalFundingHook.sol, src/LaunchToken.sol, test/CabalFundingHook.t.sol, test/LaunchToken.t.sol, test/FactoryReviewMath.sol, test/LocalCreate2.sol, worker/{budget,engine,store,chain-adapter,service,cli}.mjs, test/worker.test.mjs, scripts/review-factory.mjs, the first part of scripts/review-router-cases.mjs, README.md, reports/*, test/fixtures/*.provenance.json and the LaunchFactory ABI fixture; vendored lib/v4-core Hooks.sol flag and delta handling. Every external/public state-changing function of both contracts was traced (beforeInitialize, beforeSwap, afterSwap, harvest, unlockCallback, deposit, syncDonations, payTreasury, configureWorker, payRequest; ERC20 transfer/approve of the token) against the Pashov checklist: fee math (floor gross/25, ceil net/24), BeforeSwapDelta/afterSwap delta signs, full-fill check, ERC-6909 claim backing, cumulative 70/30 split, reentrancy, access control, forbidden-feature list, hook mask 0x20cc = beforeInitialize|beforeSwap|afterSwap|beforeSwapReturnDelta|afterSwapReturnDelta, constructor argument order. No critical or high defect was found in the Solidity; no proof file is attached. Re-run: forge test --offline (19 passed incl. 1,536 fuzz cases), worker tests in a scratch copy (25 passed), and scripts/review-factory.mjs --production-router in a scratch copy of the tree against a fresh Robinhood fork at block 92107540 (hash 0xef5794e59074e9565d8716d1fafed524cc00b3560f4b8e93ee13ba1006e57301): passed in both currency orderings, eight quotes equal execution, 100 IMD exact-input buy -> 4 IMD tax, 1000 IMD base -> 28/12 split reproduced by the unit test, slippage/deadline/approval reverts atomic, partial sell refused by V4Quoter, real Intake code debits exactly 0.5 IMD with the zero callback, runtime hashes of factory/PoolManager/IMD/Intake/LaunchFees/UniversalRouter/V4Quoter/Permit2 equal the pinned values; 0 public transactions. Live reads on 2026-10-18/19: policy 30 unchanged (fee 12500, IMD cap 2,500 IMD), Intake.priceOf = 0.5 IMD, LaunchFees poolFee 12500 / REQUESTER_BPS 8000, SWARM_BPS 1000. Not reached: the verified Solidity source of the deployed LaunchFactory, Intake, UniversalRouter and V4Quoter (only runtime hashes, ABI fixtures and live calls were checked, so their internal rules are taken from imd.fun/docs and the pinned references); scripts/simulate.mjs and the remainder of review-router-cases.mjs were executed but not line-audited; no browser-wallet flow; no launch.json exists yet to review; the real game question texts are private, so the wording-screen finding uses the repository's own fixture question.","line":25,"path":"src/CabalFundingHook.sol","reproduction":"Commands: forge test --offline; in a copy outside the tree: npm ci --ignore-scripts && node --test test/*.test.mjs && ANVIL_BIN=$(which anvil) node scripts/review-factory.mjs --production-router. All passed as stated; no repository file was modified by this review (only .imd-findings.json and test/scratch/).","severity":"info","snippet":"contract CabalFundingHook is ReentrancyGuard {","title":"Coverage: what was read, re-run and reproduced, and what could not be reached"}],"hash":"e79124f44640c0ad572b967b984d60c0f4b3c1d8ff853a78c47d0b12001a7fe8","nodeId":"3d1b9699-8ad4-45d8-b738-3a8eb2823b82","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds nine findings. The working tree is otherwise untouched (the findings file and `test/scratch/` are excluded by the harness's git exclude list, which is why `git status` is clean).\n\n**Outcome.** No critical or high defect in the Solidity. The hook's fee math, delta signs, full-fill check, ERC-6909 claim backing, cumulative 70/30 split, access control and forbidden-feature list all hold up, and the hook mask 0x20cc and six-argument constructor match the brief. The substantive defects are in the worker's pre-payment validation.\n\n**Findings written:**\n- **Medium, worker/budget.mjs:29.** The worker accepts questions up to 8,000 characters, but the paid oracle door refuses anything over 2,000. Confirmed live: the free check returns `question: Too big: expected string to have <=2000 characters`. Such a decision is paid on-chain and refused off-chain with the 0.5 IMD spent.\n- **Medium, worker/budget.mjs:31.** The worker never runs the free wording check and sets no `allowAmbiguous`/definitions. The repository's own fixture question sent to the live check came back with `ambiguous_question` and `not_answerable` blockers. Opinion-type duel decisions are refused after payment unless the reading is pinned and pre-checked.\n- **Low, src/CabalFundingHook.sol:216.** `payRequest` never binds `amount` to `Intake.priceOf`; the live intake forwards overpayment whole. Reproduced with `test/scratch/PayRequestPriceBinding.t.sol` (1 IMD accepted for a 0.5 IMD action under a misconfigured cap).\n- **Low, src/CabalFundingHook.sol:30.** Immutable intake plus constant action means the request fund is stranded if `oracle.request@oracle-1` is retired. This is the brief's chosen design, recorded as a trust assumption.\n- **Low, worker/chain-adapter.mjs:65.** A 300-second deadline baked into signed bytes with no fee replacement turns any slow inclusion into an `Expired` revert and a permanently unpaid decision.\n- **Info.** Factory-trust note on `beforeInitialize`, the post-deployment `configureWorker` surface, a policy-vs-brief split mismatch to confirm at admission, and a coverage statement.\n\n**Verification done.** Foundry suite passes offline (19 tests, 1,536 fuzz cases). Worker tests pass (25) in a scratch copy. The production-router fork review was reproduced from a scratch copy against a fresh Robinhood fork at block 92107540 in both currency orderings with zero public transactions. Live reads confirm policy 30 is unchanged (fee tier 12500, 2,500 IMD opening cap), the oracle price is still 0.5 IMD, and all pinned runtime hashes match the chain.\n\n**Not reached.** The verified source of the deployed LaunchFactory, Intake, UniversalRouter and V4Quoter (checked only via runtime hashes, ABI fixtures and live calls), the browser wallet flow, and the real game question texts, which are private.","treeHash":null,"usage":{"cachedInputTokens":2584719,"inputTokens":578,"model":"claude-fable-5-1","outputTokens":55589,"runtime":"claude","turns":43,"wallClockMs":882847}},{"artifacts":[],"attempt":1,"bundleHash":"7c48fbcacd757f6adadde5a4642709a6bd314cfbdca9678b283330bde550cc00","device":"ee8bab8367ea6e91","findings":[],"hash":"eacc9012880b9bf52d78203f7497fb4956b6f48f704e8ea44519cba3f0c80fad","nodeId":"8aeedbac-37e8-4a61-9e8c-07db3b18a2d9","outcome":"completed","summary":"Implemented the adaptation, manifest and deployment artifacts. Changes and audit dispositions are documented in `ADAPTATION.md`; ABIs, constructor bytes, mined hook prediction and runtime hashes are under `deployment/`.\n\nPassed:\n- `forge build`\n- 22 Solidity tests, including 1,536 fuzz cases\n- 36 worker/artifact tests\n- Accounting/recovery simulation and both fork currency orderings\n\nEconomics and disabled defaults are preserved. Protected configuration and dependencies are unchanged.\n\nNo public deployment or paid validation occurred. Final token addressing awaits IMD’s launch number; activation still requires verified deployment, reserve/gas funding and paid live validation.","treeHash":"6d7a676d136cd49b31742694437367d3ae1b1888","usage":{"cachedInputTokens":9500160,"inputTokens":234432,"model":"gpt-6-astra","outputTokens":51934,"runtime":"codex","turns":8,"wallClockMs":1100711}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"584bd638e7e022f0","findings":[{"citation":"resolved","description":"The brief's admission rule is 'reserve up to eight paid decisions per BO5 before admitting a duel', and the contract separately enforces an owner-set UTC-day cap in payRequest (src/CabalFundingHook.sol:284-289, spentToday + amount > dailyLimit reverts LimitExceeded). DuelFundingBudget.admit only checks reserved()+8*price+minimumReserve <= on-chain requestBalance; it never reads spentToday/spentDay/dailyLimit (the adapter ABI in worker/chain-adapter.mjs does not even expose them). The reservation therefore guarantees nothing when the sum of open reservations exceeds the day's remaining cap. When the cap is hit, FundingEngine.runOnce calls adapter.prepare, whose estimateGas reverts with LimitExceeded; the generic catch at worker/engine.mjs:87-88 records 'worker_error' and returns 'unavailable', leaving the job in 'preparing'. The next run resets it to 'queued' (engine.mjs:25) and retries, and because store.next() orders by created time this oldest job is picked on every run, so every later decision of every match is blocked until the next UTC day. Flow gap: execution (reservation) x periphery (on-chain cap) x first principles (reserve-before-admit). No funds are lost and no gas is spent, but an admitted duel's decisions cannot be paid for up to 24 hours and the queue is stuck. Minimal fix preserving the design: read dailyLimit, spentDay and spentToday at admission and reserve against min(requestBalance - minimumReserve, remainingToday - reservedToday); or at least classify a LimitExceeded simulation as a dated 'day_cap' wait instead of a generic error so later jobs are not blocked.","line":23,"path":"worker/budget.mjs","reproduction":"State: dailyLimit on chain = 4 IMD (8 decisions/day, exactly the per-BO5 reservation), requestBalance = 50 IMD, minimumImdReserveAtomic = 0. Calls: budget.admit('match-A'); budget.admit('match-B') -> both succeed, store.reserved() = 8 IMD although the cap allows 4 IMD today. Queue and pay the 8 decisions of match-A (spentToday = 4 IMD). queueDecision for match-B round 1 seat 0 succeeds. engine.runOnce() x3 returns ['unavailable','unavailable','unavailable'], job state stays 'preparing'/'queued', requestBalance still 46 IMD. Expected: match-B is refused at admission (or its first decision is reported as capped until the next UTC day) because the day's cap is exhausted. Actual: duel admitted on a reservation that cannot be honoured today; every subsequent request job is blocked behind it. Reproduced with a LocalAdapter identical to test/worker.test.mjs plus a prepare() that throws 'execution reverted: LimitExceeded()' when spentToday+amount > dailyLimit (mirrors estimateGas on payRequest); the on-chain side is shown by test/scratch/EconomicsProbe.t.sol testNinthPaymentOfDayRevertsUnderFourImdCap (deposit 100, configureWorker(w,0.5e18,4e18,true), 8 payRequest succeed, the 9th reverts LimitExceeded with 96 IMD still in requestBalance).","severity":"medium","snippet":"    this.store.reserveMatch(matchId,price*8n,balances.requestBalance,BigInt(this.config.minimumImdReserveAtomic));","title":"Duel admission reserves against requestBalance only and ignores the on-chain UTC-day cap, so admitted duels can have unpayable decisions and the engine spins on them"},{"citation":"resolved","description":"This matches the brief ('No tax on wallet transfers or unrelated pools') and is reported as an economic trust assumption, not a code defect. LaunchToken has plain transfers and PoolManager.initialize is permissionless, so anyone can initialize a second CABAL/IMD pool with hooks = address(0) (or any fee tier) and seed it from CABAL bought once on the official pool. Traders and aggregators route to the 4%-cheaper pool; the official pool's volume, and with it the 70/30 request/treasury revenue, trends to zero while the official LP position (88% of supply) keeps bearing the 12500 platform fee. Only the factory-held liquidity keeps the official pool attractive; the brief's own note that 'tax volume is not a guarantee of sufficient funding' should be read with this concrete path in mind when sizing the prefunded reserve.","line":131,"path":"src/CabalFundingHook.sol","reproduction":"test/scratch/EconomicsProbe.t.sol testParallelHooklessPoolIsUntaxed: initialize PoolKey{currency0/1 = sorted(IMD,CABAL), fee 3000, tickSpacing 60, hooks 0x0} on the same PoolManager, add liquidity, swap exact-input 1000 IMD -> CABAL on it: hook.totalFees() stays 0 and the trader is debited exactly 1000 IMD. The same 1000 IMD exact-input buy on the bound pool accrues 40 IMD. Expected per design: no tax; impact: revenue depends on where liquidity sits.","severity":"info","snippet":"        if (!initialized || PoolId.unwrap(key.toId()) != PoolId.unwrap(boundPool)) revert WrongPool();","title":"Tax is bound to one pool id: any hookless CABAL/IMD (or CABAL/other) pool trades untaxed, so the funding model depends on liquidity staying in the official pool"},{"citation":"resolved","description":"By design the view does not lock or snapshot balances, and the brief states the game server re-checks it with a fresh signature at every queue entry. The economic consequence is that the threshold is a per-signature rental, not a per-player commitment: a single 50,000 CABAL holding (or a same-block flash purchase on the pool) can be moved wallet to wallet between queue entries, or a lender can rent it, so the 50,000 CABAL 'cost' of eligibility is shared across many entrants. Reported for the game-server designer; no contract change is implied by the brief.","line":18,"path":"src/LaunchToken.sol","reproduction":"test/scratch/EconomicsProbe.t.sol testEligibilityIsSpotBalance: transfer 50,000e18 CABAL to A -> isPvpEligible(A) == true; A transfers all to B -> isPvpEligible(A) == false, isPvpEligible(B) == true, in the same block. Expected per design (view only); the economic effect is that a queue-entry check cannot distinguish a committed holder from a borrowed balance.","severity":"info","snippet":"        return balanceOf(account) >= PVP_MINIMUM_HOLDING;","title":"isPvpEligible is a spot balance: one 50,000 CABAL stake can qualify any number of wallets sequentially"}],"hash":"f526ee1198483ecc214df6f8ed32be30fdecff480d11c3985a91233f84c367be","nodeId":"bed892c5-8387-460a-a092-ba950a192855","outcome":"completed","summary":"The findings file is written and valid, and both the project suite and my scratch probe pass. Review complete.\n\n**Outcome.** The on-chain economics of `CabalFundingHook` and `LaunchToken` hold under the Economic Security, Invariant and Flow Gap checks. I found no fund-loss or misallocation defect in the contracts. One medium finding sits in the worker's admission logic, plus two info-level economic trust assumptions. Written to `.imd-findings.json` with 19 coverage rows (all 13 entry points answered, 18 of 19 rows resolved).\n\n**Findings**\n- **Medium, worker/budget.mjs:23.** Duel admission reserves eight decisions against the on-chain request balance only and never reads the UTC-day cap. With a 4 IMD daily cap, two duels are admitted, the first consumes the day's cap, and the second duel's first decision then spins as `unavailable` on every engine run while the queue stays blocked behind it until the next UTC day. Reproduced with a local adapter mirroring the suite's harness plus the contract's own `LimitExceeded` revert. Suggested fix keeps the design: reserve against the day's remaining cap as well, or classify a cap revert as a dated wait instead of a generic error.\n- **Info, src/CabalFundingHook.sol:131.** The tax binds to one pool id. A hookless CABAL/IMD pool trades untaxed, so request and treasury revenue depends on liquidity staying in the official pool. Matches the brief's stated scope; reported as an economic assumption for reserve sizing.\n- **Info, src/LaunchToken.sol:18.** PvP eligibility is a spot balance, so one 50,000 CABAL stake can qualify many wallets in sequence between queue entries. Matches the brief; reported for the game-server designer.\n\n**Leads rejected after tracing.** All slither and aderyn lines on `harvest` and `payRequest` (reentrancy, divide-before-multiply, uninitialized local): the reentrancy guard, exact balance-delta checks and the algebraic identity for the cumulative 70/30 split close each path. Dust swaps under 25 wei pay no tax, which is economically irrelevant. Front-running `harvest(amount)` only causes a revert, not loss.\n\n**Verification run.** The 22 project tests pass offline. My probe at `test/scratch/EconomicsProbe.t.sol` passes six cases covering the parallel-pool scope, partial-fill reverts in both pre-charged modes, dust rounding, spot eligibility and the ninth-payment day-cap revert. forge-std is not vendored, so the probe uses the repo's Vm-interface pattern; no critical or high finding exists, so no proof file is attached.\n\n**Not reached.** I did not re-read policy 30's current 2,500 IMD valuation or re-verify live router, quoter and Intake deployments over the network. Those rows rely on the committed fork-review snapshot and are marked unreached.","treeHash":null,"usage":{"cachedInputTokens":1388151,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":43565,"runtime":"claude","turns":31,"wallClockMs":548591}}],"verification":[{"checks":[{"durationMs":1294,"exitCode":0,"name":"build","output":"Compiling 68 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.24s\nCompiler run successful!\n","passed":true},{"durationMs":575,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/LaunchToken.t.sol:LaunchTokenTest\n[PASS] testFactoryReceivesExactSupplyAndTransfersStayPlain() (gas: 112350)\n[PASS] testPvpBoundaryAndSellingBelowRequirement() (gas: 220400)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 428.76µs (554.40µs CPU time)\n\nRan 20 tests for test/CabalFundingHook.t.sol:FundingHookTest\n[PASS] testAllSwapModesAndOrderings() (gas: 46940698)\n[PASS] testCannotCallCallbacksOrBindAnotherPool() (gas: 103283)\n[PASS] testEndToEndSwapTreasuryAndOraclePayment() (gas: 1974118)\n[PASS] testFirstBuyIntoSingleSidedPool() (gas: 33529938)\n[PASS] testFuzzActualSwapFeeMath(uint64,uint8) (runs: 512, μ: 597794, ~: 599434)\n[PASS] testFuzzMixedOperationsPreserveAllLedgers(uint256) (runs: 512, μ: 11851371, ~: 11862317)\n[PASS] testFuzzSplitAndChunkIndependence(uint96,uint8) (runs: 512, μ: 1446930, ~: 1209333)\n[PASS] testIntakeCannotReenterFundingAccounting() (gas: 581340)\n[PASS] testIntakeUnderchargeRollsEverythingBack() (gas: 591348)\n[PASS] testLongerDeadlineAllowsDelayedInclusionButStillExpires() (gas: 559030)\n[PASS] testPaidRequestsDeduplicateAndEnforceCaps() (gas: 967860)\n[PASS] testPartialFillRevertsWithoutKeepingTax() (gas: 231451)\n[PASS] testPaymentMustEqualCurrentIntakePriceEvenWithHigherOwnerCap() (gas: 726244)\n[PASS] testPlainCabalTransfersUntaxed() (gas: 69223)\n[PASS] testPrefundingAndDirectDonationsAreNotTaxed() (gas: 372653)\n[PASS] testRetiredActionCannotSpendReserveAndDoesNotBlockTrading() (gas: 913079)\n[PASS] testTreasuryFailureAndPaymentOutageDoNotBlockSwaps() (gas: 1116476)\n[PASS] testUnauthorizedDisabledExpiredAndEmptyFund() (gas: 385202)\n[PASS] testUnauthorizedInitializationIsRejected() (gas: 44130)\n[PASS] testUninitializedHookCannotBeClaimedByAnOutsider() (gas: 4638658)\nSuite result: ok. 20 passed; 0 failed; 0 skipped; finished in 510.40ms (797.80ms CPU time)\n\nRan 2 test suites in 511.12ms (510.83ms CPU time): 22 tests passed, 0 failed, 0 skipped (22 total tests)\n","passed":true},{"durationMs":39,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CabalFundingHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"CabalFundingHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"CabalFundingHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"CabalFundingHook.configureWorker(address,uint256,uint256,bool)\",\"CabalFundingHook.deposit(uint256)\",\"CabalFundingHook.harvest(uint256)\",\"CabalFundingHook.payRequest(bytes32,bytes,uint256,uint256)\",\"CabalFundingHook.payTreasury(uint256)\",\"CabalFundingHook.syncDonations()\",\"CabalFundingHook.unlockCallback(bytes)\",\"LaunchToken.approve(address,uint256)\",\"LaunchToken.transfer(address,uint256)\",\"LaunchToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"ADAPTATION.md\":57,\"README.md\":101,\"config.example.json\":21,\"deployment/CabalFundingHook.init.hex\":1,\"deployment/CabalFundingHook.runtime.hex\":1,\"deployment/LaunchToken.creation.hex\":1,\"deployment/LaunchToken.runtime.hex\":1,\"deployment/README.md\":32,\"deployment/abi/CabalFundingHook.json\":1129,\"deployment/abi/LaunchToken.json\":347,\"deployment/artifacts.json\":58,\"deployment/launch-terms.json\":10,\"foundry.toml\":14,\"launch.json\":34,\"package-lock.json\":238,\"package.json\":9,\"reports/build.txt\":322,\"reports/contract-review.txt\":85,\"reports/factory-fork-review.json\":130,\"reports/frontend-review.json\":23,\"reports/local-e2e.json\":25,\"reports/oracle-preflight-review.json\":50,\"reports/production-router-review.json\":270,\"reports/production-router-review.txt\":33,\"reports/protocol-review.json\":65,\"reports/solidity-tests.json\":1,\"reports/validation.json\":159,\"reports/worker-tests.tap\":250,\"scripts/check-oracle.mjs\":22,\"scripts/check-protocol.mjs\":43,\"scripts/launch-artifacts.mjs\":78,\"scripts/package-source.mjs\":44,\"scripts/review-factory.mjs\":175,\"scripts/review-router-cases.mjs\":103,\"scripts/simulate.mjs\":110,\"scripts/vendor-solidity.mjs\":32,\"source-integrity.json\":1914,\"src/CabalFundingHook.sol\":306,\"src/LaunchToken.sol\":20,\"test/CabalFundingHook.t.sol\":483,\"test/FactoryReviewMath.sol\":24,\"test/LaunchToken.t.sol\":35,\"test/LocalCreate2.sol\":9,\"test/fixtures/IMDLaunchFactory.abi.json\":896,\"test/fixtures/IMDLaunchFactory.provenance.json\":13,\"test/fixtures/IMDTrading.provenance.json\":45,\"test/fixtures/IMDUniversalRouter.abi.json\":848,\"test/fixtures/IMDV4Quoter.abi.json\":650,\"test/fixtures/protocol-runtimes.json\":10,\"test/launch-manifest.test.mjs\":28,\"test/oracle-client.test.mjs\":42,\"test/worker.test.mjs\":269,\"vendor-integrity.json\":1221,\"vendor/README.md\":13,\"vendor/bundle-inputs.json\":11366,\"vendor/licenses/@adraffy/ens-normalize/LICENSE\":21,\"vendor/licenses/@noble/ciphers/LICENSE\":22,\"vendor/licenses/@noble/curves/LICENSE\":21,\"vendor/licenses/@noble/hashes/LICENSE\":21,\"vendor/licenses/@scure/base/LICENSE\":21,\"vendor/licenses/@scure/bip32/LICENSE\":21,\"vendor/licenses/@scure/bip39/LICENSE\":21,\"vendor/licenses/abitype/LICENSE\":21,\"vendor/licenses/eventemitter3/LICENSE\":21,\"vendor/licenses/isows/LICENSE\":21,\"vendor/licenses/ox/LICENSE\":21,\"vendor/licenses/viem/LICENSE\":21,\"vendor/licenses/ws/LICENSE\":20,\"vendor/viem-entry.mjs\":2,\"vendor/viem.mjs\":20275,\"vendor/worker-dependencies.json\":87,\"worker/budget.mjs\":48,\"worker/chain-adapter.mjs\":114,\"worker/cli.mjs\":5,\"worker/day-cap.mjs\":18,\"worker/engine.mjs\":100,\"worker/oracle-client.mjs\":59,\"worker/service.mjs\":16,\"worker/store.mjs\":112},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"2b16b9fbd2142e9fa3dc86d994fb0bbcd9eb2102a92c63344b1b92262f0afdb9","verifiedTreeHash":"d0334cf90d41f141b5115f6204000a7f9c858806","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":1417,"exitCode":0,"name":"build","output":"Compiling 68 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.36s\nCompiler run successful!\n","passed":true},{"durationMs":563,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/LaunchToken.t.sol:LaunchTokenTest\n[PASS] testFactoryReceivesExactSupplyAndTransfersStayPlain() (gas: 112350)\n[PASS] testPvpBoundaryAndSellingBelowRequirement() (gas: 220400)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 492.32µs (349.59µs CPU time)\n\nRan 20 tests for test/CabalFundingHook.t.sol:FundingHookTest\n[PASS] testAllSwapModesAndOrderings() (gas: 46940698)\n[PASS] testCannotCallCallbacksOrBindAnotherPool() (gas: 103283)\n[PASS] testEndToEndSwapTreasuryAndOraclePayment() (gas: 1974118)\n[PASS] testFirstBuyIntoSingleSidedPool() (gas: 33529938)\n[PASS] testFuzzActualSwapFeeMath(uint64,uint8) (runs: 512, μ: 597794, ~: 599434)\n[PASS] testFuzzMixedOperationsPreserveAllLedgers(uint256) (runs: 512, μ: 11851371, ~: 11862317)\n[PASS] testFuzzSplitAndChunkIndependence(uint96,uint8) (runs: 512, μ: 1446930, ~: 1209333)\n[PASS] testIntakeCannotReenterFundingAccounting() (gas: 581340)\n[PASS] testIntakeUnderchargeRollsEverythingBack() (gas: 591348)\n[PASS] testLongerDeadlineAllowsDelayedInclusionButStillExpires() (gas: 559030)\n[PASS] testPaidRequestsDeduplicateAndEnforceCaps() (gas: 967860)\n[PASS] testPartialFillRevertsWithoutKeepingTax() (gas: 231451)\n[PASS] testPaymentMustEqualCurrentIntakePriceEvenWithHigherOwnerCap() (gas: 726244)\n[PASS] testPlainCabalTransfersUntaxed() (gas: 69223)\n[PASS] testPrefundingAndDirectDonationsAreNotTaxed() (gas: 372653)\n[PASS] testRetiredActionCannotSpendReserveAndDoesNotBlockTrading() (gas: 913079)\n[PASS] testTreasuryFailureAndPaymentOutageDoNotBlockSwaps() (gas: 1116476)\n[PASS] testUnauthorizedDisabledExpiredAndEmptyFund() (gas: 385202)\n[PASS] testUnauthorizedInitializationIsRejected() (gas: 44130)\n[PASS] testUninitializedHookCannotBeClaimedByAnOutsider() (gas: 4638658)\nSuite result: ok. 20 passed; 0 failed; 0 skipped; finished in 489.97ms (758.34ms CPU time)\n\nRan 2 test suites in 490.82ms (490.46ms CPU time): 22 tests passed, 0 failed, 0 skipped (22 total tests)\n","passed":true},{"durationMs":50,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CabalFundingHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"CabalFundingHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"CabalFundingHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"CabalFundingHook.configureWorker(address,uint256,uint256,bool)\",\"CabalFundingHook.deposit(uint256)\",\"CabalFundingHook.harvest(uint256)\",\"CabalFundingHook.payRequest(bytes32,bytes,uint256,uint256)\",\"CabalFundingHook.payTreasury(uint256)\",\"CabalFundingHook.syncDonations()\",\"CabalFundingHook.unlockCallback(bytes)\",\"LaunchToken.approve(address,uint256)\",\"LaunchToken.transfer(address,uint256)\",\"LaunchToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"ADAPTATION.md\":46,\"README.md\":101,\"config.example.json\":21,\"deployment/CabalFundingHook.init.hex\":1,\"deployment/CabalFundingHook.runtime.hex\":1,\"deployment/LaunchToken.creation.hex\":1,\"deployment/LaunchToken.runtime.hex\":1,\"deployment/README.md\":32,\"deployment/abi/CabalFundingHook.json\":1129,\"deployment/abi/LaunchToken.json\":347,\"deployment/artifacts.json\":58,\"deployment/launch-terms.json\":10,\"foundry.toml\":14,\"launch.json\":34,\"package-lock.json\":238,\"package.json\":9,\"reports/build.txt\":322,\"reports/contract-review.txt\":85,\"reports/factory-fork-review.json\":130,\"reports/frontend-review.json\":23,\"reports/local-e2e.json\":23,\"reports/oracle-preflight-review.json\":50,\"reports/production-router-review.json\":270,\"reports/production-router-review.txt\":33,\"reports/protocol-review.json\":65,\"reports/solidity-tests.json\":1,\"reports/validation.json\":158,\"reports/worker-tests.tap\":226,\"scripts/check-oracle.mjs\":22,\"scripts/check-protocol.mjs\":43,\"scripts/launch-artifacts.mjs\":78,\"scripts/package-source.mjs\":44,\"scripts/review-factory.mjs\":175,\"scripts/review-router-cases.mjs\":103,\"scripts/simulate.mjs\":92,\"scripts/vendor-solidity.mjs\":32,\"source-integrity.json\":1909,\"src/CabalFundingHook.sol\":306,\"src/LaunchToken.sol\":20,\"test/CabalFundingHook.t.sol\":483,\"test/FactoryReviewMath.sol\":24,\"test/LaunchToken.t.sol\":35,\"test/LocalCreate2.sol\":9,\"test/fixtures/IMDLaunchFactory.abi.json\":896,\"test/fixtures/IMDLaunchFactory.provenance.json\":13,\"test/fixtures/IMDTrading.provenance.json\":45,\"test/fixtures/IMDUniversalRouter.abi.json\":848,\"test/fixtures/IMDV4Quoter.abi.json\":650,\"test/fixtures/protocol-runtimes.json\":10,\"test/launch-manifest.test.mjs\":28,\"test/oracle-client.test.mjs\":42,\"test/worker.test.mjs\":212,\"vendor-integrity.json\":1221,\"vendor/README.md\":13,\"vendor/bundle-inputs.json\":11366,\"vendor/licenses/@adraffy/ens-normalize/LICENSE\":21,\"vendor/licenses/@noble/ciphers/LICENSE\":22,\"vendor/licenses/@noble/curves/LICENSE\":21,\"vendor/licenses/@noble/hashes/LICENSE\":21,\"vendor/licenses/@scure/base/LICENSE\":21,\"vendor/licenses/@scure/bip32/LICENSE\":21,\"vendor/licenses/@scure/bip39/LICENSE\":21,\"vendor/licenses/abitype/LICENSE\":21,\"vendor/licenses/eventemitter3/LICENSE\":21,\"vendor/licenses/isows/LICENSE\":21,\"vendor/licenses/ox/LICENSE\":21,\"vendor/licenses/viem/LICENSE\":21,\"vendor/licenses/ws/LICENSE\":20,\"vendor/viem-entry.mjs\":2,\"vendor/viem.mjs\":20275,\"vendor/worker-dependencies.json\":87,\"worker/budget.mjs\":45,\"worker/chain-adapter.mjs\":110,\"worker/cli.mjs\":5,\"worker/engine.mjs\":91,\"worker/oracle-client.mjs\":59,\"worker/service.mjs\":16,\"worker/store.mjs\":96},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"40f46b43d0ce5d6b9cacfb45ca70527dc2e878ef97156c816567e2b0ead7c19a","verifiedTreeHash":"025c6782547b18cc912250318cc81cf9ee300e17","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":2631,"exitCode":0,"name":"build","output":"Compiling 73 files with Solc 0.8.26\nSolc 0.8.26 finished in 2.57s\nCompiler run successful!\n","passed":true},{"durationMs":3908,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/LaunchToken.t.sol:LaunchTokenTest\n[PASS] testFactoryReceivesExactSupplyAndTransfersStayPlain() (gas: 112350)\n[PASS] testPvpBoundaryAndSellingBelowRequirement() (gas: 220400)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 391.61µs (532.03µs CPU time)\n\nRan 9 tests for test/CabalFundingHookArithmetic.t.sol:CabalFundingHookArithmeticTest\n[PASS] testAfterSwapRejectsWrongIMDDirectionAndIncompletePrechargedFills() (gas: 314514)\n[PASS] testAtomicUnitsChargeOnlyIMDInAllFourSwapModes() (gas: 4420097)\n[PASS] testFirstBuyWorksWithZeroIMDSeedInEitherCurrencyOrdering() (gas: 46435508)\n[PASS] testFuzzFeeBoundsAcrossFullUint256Domain(uint256) (runs: 1000, μ: 11993, ~: 11998)\n[PASS] testFuzzGrossToNetToGrossHasAtMostOneAtomicUnitRounding(uint256) (runs: 1000, μ: 11472, ~: 11475)\n[PASS] testPinnedZeroOneDivisorAndMaximumFeeEdges() (gas: 104231)\n[PASS] testPrechargedFeesRejectUnrepresentableSignedAmounts() (gas: 152157)\n[PASS] testTenOneWeiHarvestsMatchSingleCumulativeAllocation() (gas: 3353673)\n[PASS] testUnrelatedPoolAndWalletTransfersAccrueNoCustomFee() (gas: 632099)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 520.67ms (671.25ms CPU time)\n\nRan 20 tests for test/CabalFundingHookFailures.t.sol:CabalFundingHookFailuresTest\n[PASS] testApprovalAndAllowanceClearFailuresRollBackTheWholePayment() (gas: 1702069)\n[PASS] testBothSwapCallbacksRejectUnboundAndDifferentPools() (gas: 51435675)\n[PASS] testConstructorDoesNotCallExternalDependencies() (gas: 26250687)\n[PASS] testConstructorRejectsAnUnminedPermissionMask() (gas: 24221)\n[PASS] testConstructorRejectsEveryMissingRole() (gas: 11225)\n[PASS] testDeploymentHasOnlyRequiredPermissionsAndDisabledPayments() (gas: 110806)\n[PASS] testEveryCallbackRejectsNonManagerIncludingOwner() (gas: 122423)\n[PASS] testEveryIntakeFailureRollsBackApprovalKeyBalancesAndDailyCap() (gas: 2758685)\n[PASS] testFeeOnTransferDepositAndHarvestAreRejectedAtomically() (gas: 1149656)\n[PASS] testFreshHookRejectsBadBindingThenAcceptsFactoryExactlyOnce() (gas: 51627167)\n[PASS] testIntakeReentryCannotHarvestDonatePayTreasuryOrPayAgain() (gas: 2934514)\n[PASS] testOwnerHasNoEconomicOrSweepSwitches() (gas: 469763)\n[PASS] testReconciliationEventsBindTheActualPoolRecipientBodyAndAmount() (gas: 1127633)\n[PASS] testRequestInputBoundsAreAtomicAndMaximumBodyIsAccepted() (gas: 1044241)\n[PASS] testSwapsAndHarvestRemainLiveWithUnavailableIntakeAndBlockedTreasury() (gas: 1423460)\n[PASS] testUTCAndDeadlineBoundariesAndReplaySurviveDayChange() (gas: 1020988)\n[PASS] testUnsyncedDonationCannotBeSpentUntilExplicitlyBooked() (gas: 802413)\n[PASS] testWorkerConfigurationRejectsInvalidParametersAndNonOwner() (gas: 112485)\n[PASS] testWorkerRotationRevokesOldWorkerAndDoesNotResetSpentBudget() (gas: 878463)\n[PASS] testZeroAndExcessSettlementCannotTouchRequestPrefunding() (gas: 998425)\nSuite result: ok. 20 passed; 0 failed; 0 skipped; finished in 520.77ms (127.10ms CPU time)\n\nRan 20 tests for test/CabalFundingHook.t.sol:FundingHookTest\n[PASS] testAllSwapModesAndOrderings() (gas: 46940698)\n[PASS] testCannotCallCallbacksOrBindAnotherPool() (gas: 103283)\n[PASS] testEndToEndSwapTreasuryAndOraclePayment() (gas: 1974118)\n[PASS] testFirstBuyIntoSingleSidedPool() (gas: 33529938)\n[PASS] testFuzzActualSwapFeeMath(uint64,uint8) (runs: 512, μ: 597826, ~: 599434)\n[PASS] testFuzzMixedOperationsPreserveAllLedgers(uint256) (runs: 512, μ: 11826766, ~: 11845801)\n[PASS] testFuzzSplitAndChunkIndependence(uint96,uint8) (runs: 512, μ: 1502819, ~: 1307654)\n[PASS] testIntakeCannotReenterFundingAccounting() (gas: 581340)\n[PASS] testIntakeUnderchargeRollsEverythingBack() (gas: 591348)\n[PASS] testLongerDeadlineAllowsDelayedInclusionButStillExpires() (gas: 559030)\n[PASS] testPaidRequestsDeduplicateAndEnforceCaps() (gas: 967860)\n[PASS] testPartialFillRevertsWithoutKeepingTax() (gas: 231451)\n[PASS] testPaymentMustEqualCurrentIntakePriceEvenWithHigherOwnerCap() (gas: 726244)\n[PASS] testPlainCabalTransfersUntaxed() (gas: 69223)\n[PASS] testPrefundingAndDirectDonationsAreNotTaxed() (gas: 372653)\n[PASS] testRetiredActionCannotSpendReserveAndDoesNotBlockTrading() (gas: 913079)\n[PASS] testTreasuryFailureAndPaymentOutageDoNotBlockSwaps() (gas: 1116476)\n[PASS] testUnauthorizedDisabledExpiredAndEmptyFund() (gas: 385202)\n[PASS] testUnauthorizedInitializationIsRejected() (gas: 44130)\n[PASS] testUninitializedHookCannotBeClaimedByAnOutsider() (gas: 4638658)\nSuite result: ok. 20 passed; 0 failed; 0 skipped; finished in 520.86ms (794.44ms CPU time)\n\nRan 2 tests for test/LaunchTokenInvariant.t.sol:LaunchTokenStatefulInvariantTest\n[PASS] invariantSupplyTransfersAllowancesAndEligibilityStayConsistent() (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------------+-----------------------+-------+---------+----------╮\n| Contract           | Selector              | Calls | Reverts | Discards |\n+=========================================================================+\n| LaunchTokenHandler | approve               | 2765  | 0       | 0        |\n|--------------------+-----------------------+-------+---------+----------|\n| LaunchTokenHandler | crossPvpBoundary      | 2792  | 0       | 0        |\n|--------------------+-----------------------+-------+---------+----------|\n| LaunchTokenHandler | overdraw              | 2658  | 0       | 0        |\n|--------------------+-----------------------+-------+---------+----------|\n| LaunchTokenHandler | revokeAndAttemptSpend | 2735  | 0       | 0        |\n|--------------------+-----------------------+-------+---------+----------|\n| LaunchTokenHandler | transfer              | 2721  | 0       | 0        |\n|--------------------+-----------------------+-------+---------+----------|\n| LaunchTokenHandler | transferFrom          | 2713  | 0       | 0        |\n╰--------------------+-----------------------+-------+---------+----------╯\n\n[PASS] testAllTokenHandlerBranchesIncludingFullBalanceAndThresholds() (gas: 1327678)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.39s (3.39s CPU time)\n\nRan 2 tests for test/CabalFundingHookInvariant.t.sol:CabalFundingImdFirstInvariantTest\n[PASS]\nCabalFundingImdFirstInvariantTest invariants:\n[PASS] invariantClaimsCashAndEveryLedgerMatchIndependentFlows\n[PASS] invariantFixedTokenSupplyAndPvpViewsTrackRealHoldings\n[PASS] invariantPaidDecisionsAndDailySpendingNeverResetOrReplay\n CabalFundingImdFirstInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------------+---------------------+-------+---------+----------╮\n| Contract            | Selector            | Calls | Reverts | Discards |\n+========================================================================+\n| CabalFundingHandler | advanceTime         | 1270  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | configure           | 1322  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | deposit             | 1245  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | donate              | 1241  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | failedIntakePayment | 1232  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | harvest             | 1250  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | pay                 | 1258  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | replay              | 1272  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | roundTrip           | 1291  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | swap                | 1265  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | sync                | 1227  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | treasury            | 1276  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | unauthorized        | 1235  | 0       | 0        |\n╰---------------------+---------------------+-------+---------+----------╯\n\n[PASS] testHandlerExercisesSuccessAndFailureTransitions() (gas: 2971855)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.84s (3.83s CPU time)\n\nRan 2 tests for test/CabalFundingHookInvariant.t.sol:CabalFundingImdSecondInvariantTest\n[PASS]\nCabalFundingImdSecondInvariantTest invariants:\n[PASS] invariantClaimsCashAndEveryLedgerMatchIndependentFlows\n[PASS] invariantFixedTokenSupplyAndPvpViewsTrackRealHoldings\n[PASS] invariantPaidDecisionsAndDailySpendingNeverResetOrReplay\n CabalFundingImdSecondInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------------+---------------------+-------+---------+----------╮\n| Contract            | Selector            | Calls | Reverts | Discards |\n+========================================================================+\n| CabalFundingHandler | advanceTime         | 1270  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | configure           | 1322  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | deposit             | 1245  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | donate              | 1241  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | failedIntakePayment | 1232  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | harvest             | 1250  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | pay                 | 1258  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | replay              | 1272  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | roundTrip           | 1291  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | swap                | 1265  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | sync                | 1227  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | treasury            | 1276  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | unauthorized        | 1235  | 0       | 0        |\n╰---------------------+---------------------+-------+---------+----------╯\n\n[PASS] testHandlerExercisesSuccessAndFailureTransitions() (gas: 2970138)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.84s (3.82s CPU time)\n\nRan 7 test suites in 3.84s (12.63s CPU time): 57 tests passed, 0 failed, 0 skipped (57 total tests)\n","passed":true},{"durationMs":42,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CabalFundingHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"CabalFundingHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"CabalFundingHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"CabalFundingHook.configureWorker(address,uint256,uint256,bool)\",\"CabalFundingHook.deposit(uint256)\",\"CabalFundingHook.harvest(uint256)\",\"CabalFundingHook.payRequest(bytes32,bytes,uint256,uint256)\",\"CabalFundingHook.payTreasury(uint256)\",\"CabalFundingHook.syncDonations()\",\"CabalFundingHook.unlockCallback(bytes)\",\"LaunchToken.approve(address,uint256)\",\"LaunchToken.transfer(address,uint256)\",\"LaunchToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"ADAPTATION.md\":46,\"README.md\":101,\"config.example.json\":21,\"deployment/CabalFundingHook.init.hex\":1,\"deployment/CabalFundingHook.runtime.hex\":1,\"deployment/LaunchToken.creation.hex\":1,\"deployment/LaunchToken.runtime.hex\":1,\"deployment/README.md\":32,\"deployment/abi/CabalFundingHook.json\":1129,\"deployment/abi/LaunchToken.json\":347,\"deployment/artifacts.json\":58,\"deployment/launch-terms.json\":10,\"foundry.toml\":14,\"launch.json\":34,\"package-lock.json\":238,\"package.json\":9,\"reports/build.txt\":322,\"reports/contract-review.txt\":85,\"reports/factory-fork-review.json\":130,\"reports/frontend-review.json\":23,\"reports/local-e2e.json\":23,\"reports/oracle-preflight-review.json\":50,\"reports/production-router-review.json\":270,\"reports/production-router-review.txt\":33,\"reports/protocol-review.json\":65,\"reports/solidity-tests.json\":1,\"reports/validation.json\":158,\"reports/worker-tests.tap\":226,\"scripts/check-oracle.mjs\":22,\"scripts/check-protocol.mjs\":43,\"scripts/launch-artifacts.mjs\":78,\"scripts/package-source.mjs\":44,\"scripts/review-factory.mjs\":175,\"scripts/review-router-cases.mjs\":103,\"scripts/simulate.mjs\":92,\"scripts/vendor-solidity.mjs\":32,\"source-integrity.json\":1909,\"src/CabalFundingHook.sol\":306,\"src/LaunchToken.sol\":20,\"test/CabalFundingHook.t.sol\":483,\"test/CabalFundingHookArithmetic.t.sol\":167,\"test/CabalFundingHookFailures.t.sol\":442,\"test/CabalFundingHookInvariant.t.sol\":428,\"test/FactoryReviewMath.sol\":24,\"test/LaunchToken.t.sol\":35,\"test/LaunchTokenInvariant.t.sol\":183,\"test/LocalCreate2.sol\":9,\"test/REVIEW.md\":135,\"test/fixtures/IMDLaunchFactory.abi.json\":896,\"test/fixtures/IMDLaunchFactory.provenance.json\":13,\"test/fixtures/IMDTrading.provenance.json\":45,\"test/fixtures/IMDUniversalRouter.abi.json\":848,\"test/fixtures/IMDV4Quoter.abi.json\":650,\"test/fixtures/protocol-runtimes.json\":10,\"test/helpers/CabalReviewFixture.sol\":274,\"test/launch-manifest.test.mjs\":28,\"test/oracle-client.test.mjs\":42,\"test/replay-reviews.mjs\":50,\"test/review-results/local-e2e.json\":23,\"test/review-results/production-router-review.json\":270,\"test/review-results/protocol-review.json\":65,\"test/review-results/validation.json\":518,\"test/worker.test.mjs\":212,\"vendor-integrity.json\":1221,\"vendor/README.md\":13,\"vendor/bundle-inputs.json\":11366,\"vendor/licenses/@adraffy/ens-normalize/LICENSE\":21,\"vendor/licenses/@noble/ciphers/LICENSE\":22,\"vendor/licenses/@noble/curves/LICENSE\":21,\"vendor/licenses/@noble/hashes/LICENSE\":21,\"vendor/licenses/@scure/base/LICENSE\":21,\"vendor/licenses/@scure/bip32/LICENSE\":21,\"vendor/licenses/@scure/bip39/LICENSE\":21,\"vendor/licenses/abitype/LICENSE\":21,\"vendor/licenses/eventemitter3/LICENSE\":21,\"vendor/licenses/isows/LICENSE\":21,\"vendor/licenses/ox/LICENSE\":21,\"vendor/licenses/viem/LICENSE\":21,\"vendor/licenses/ws/LICENSE\":20,\"vendor/viem-entry.mjs\":2,\"vendor/viem.mjs\":20275,\"vendor/worker-dependencies.json\":87,\"worker/budget.mjs\":45,\"worker/chain-adapter.mjs\":110,\"worker/cli.mjs\":5,\"worker/engine.mjs\":91,\"worker/oracle-client.mjs\":59,\"worker/service.mjs\":16,\"worker/store.mjs\":96},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"a98dad36fe291a6f655f4596c26a4570eb00ef73eea7835b721632a6bca2079d","verifiedTreeHash":"6455a1b2fe7f688aa97f061756bad2ea8909e39b","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":6293,"exitCode":0,"name":"build","output":"Compiling 74 files with Solc 0.8.26\nSolc 0.8.26 finished in 6.14s\nCompiler run successful!\n","passed":true},{"durationMs":7591,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/LaunchToken.t.sol:LaunchTokenTest\n[PASS] testFactoryReceivesExactSupplyAndTransfersStayPlain() (gas: 112350)\n[PASS] testPvpBoundaryAndSellingBelowRequirement() (gas: 220400)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 619.80µs (820.54µs CPU time)\n\nRan 9 tests for test/CabalFundingHookArithmetic.t.sol:CabalFundingHookArithmeticTest\n[PASS] testAfterSwapRejectsWrongIMDDirectionAndIncompletePrechargedFills() (gas: 314514)\n[PASS] testAtomicUnitsChargeOnlyIMDInAllFourSwapModes() (gas: 4420097)\n[PASS] testFirstBuyWorksWithZeroIMDSeedInEitherCurrencyOrdering() (gas: 46435508)\n[PASS] testFuzzFeeBoundsAcrossFullUint256Domain(uint256) (runs: 1000, μ: 11993, ~: 11998)\n[PASS] testFuzzGrossToNetToGrossHasAtMostOneAtomicUnitRounding(uint256) (runs: 1000, μ: 11472, ~: 11475)\n[PASS] testPinnedZeroOneDivisorAndMaximumFeeEdges() (gas: 104231)\n[PASS] testPrechargedFeesRejectUnrepresentableSignedAmounts() (gas: 152157)\n[PASS] testTenOneWeiHarvestsMatchSingleCumulativeAllocation() (gas: 3353673)\n[PASS] testUnrelatedPoolAndWalletTransfersAccrueNoCustomFee() (gas: 632099)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 926.29ms (91.30ms CPU time)\n\nRan 20 tests for test/CabalFundingHookFailures.t.sol:CabalFundingHookFailuresTest\n[PASS] testApprovalAndAllowanceClearFailuresRollBackTheWholePayment() (gas: 1702069)\n[PASS] testBothSwapCallbacksRejectUnboundAndDifferentPools() (gas: 51435675)\n[PASS] testConstructorDoesNotCallExternalDependencies() (gas: 26250687)\n[PASS] testConstructorRejectsAnUnminedPermissionMask() (gas: 24221)\n[PASS] testConstructorRejectsEveryMissingRole() (gas: 11225)\n[PASS] testDeploymentHasOnlyRequiredPermissionsAndDisabledPayments() (gas: 110806)\n[PASS] testEveryCallbackRejectsNonManagerIncludingOwner() (gas: 122423)\n[PASS] testEveryIntakeFailureRollsBackApprovalKeyBalancesAndDailyCap() (gas: 2758685)\n[PASS] testFeeOnTransferDepositAndHarvestAreRejectedAtomically() (gas: 1149656)\n[PASS] testFreshHookRejectsBadBindingThenAcceptsFactoryExactlyOnce() (gas: 51627167)\n[PASS] testIntakeReentryCannotHarvestDonatePayTreasuryOrPayAgain() (gas: 2934514)\n[PASS] testOwnerHasNoEconomicOrSweepSwitches() (gas: 469763)\n[PASS] testReconciliationEventsBindTheActualPoolRecipientBodyAndAmount() (gas: 1127633)\n[PASS] testRequestInputBoundsAreAtomicAndMaximumBodyIsAccepted() (gas: 1044241)\n[PASS] testSwapsAndHarvestRemainLiveWithUnavailableIntakeAndBlockedTreasury() (gas: 1423460)\n[PASS] testUTCAndDeadlineBoundariesAndReplaySurviveDayChange() (gas: 1020988)\n[PASS] testUnsyncedDonationCannotBeSpentUntilExplicitlyBooked() (gas: 802413)\n[PASS] testWorkerConfigurationRejectsInvalidParametersAndNonOwner() (gas: 112485)\n[PASS] testWorkerRotationRevokesOldWorkerAndDoesNotResetSpentBudget() (gas: 878463)\n[PASS] testZeroAndExcessSettlementCannotTouchRequestPrefunding() (gas: 998425)\nSuite result: ok. 20 passed; 0 failed; 0 skipped; finished in 979.76ms (242.58ms CPU time)\n\nRan 6 tests for test/CabalFundingHookDayCap.t.sol:CabalFundingHookDayCapTest\n[PASS] testCapThatIsNotAMultipleOfThePriceRoundsDown() (gas: 1738195)\n[PASS] testFuzzWorkerHeadroomNeverExceedsWhatTheContractPays(uint256,uint256,uint256,uint256,uint256,uint256) (runs: 512, μ: 2227030, ~: 2061230)\n[PASS] testLoweringTheCapBelowTodaysSpendLeavesZeroHeadroomWithoutReset() (gas: 2059139)\n[PASS] testRevertedPaymentOnANewDayLeavesTheStaleDayRecorded() (gas: 1528203)\n[PASS] testSixteenReservedBo5sNeedA64ImdDailyCap() (gas: 41322387)\n[PASS] testStaleDayViewsStillAllowAFullNewDayCap() (gas: 3279112)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 996.91ms (226.12ms CPU time)\n\nRan 20 tests for test/CabalFundingHook.t.sol:FundingHookTest\n[PASS] testAllSwapModesAndOrderings() (gas: 46940698)\n[PASS] testCannotCallCallbacksOrBindAnotherPool() (gas: 103283)\n[PASS] testEndToEndSwapTreasuryAndOraclePayment() (gas: 1974118)\n[PASS] testFirstBuyIntoSingleSidedPool() (gas: 33529938)\n[PASS] testFuzzActualSwapFeeMath(uint64,uint8) (runs: 512, μ: 597778, ~: 599434)\n[PASS] testFuzzMixedOperationsPreserveAllLedgers(uint256) (runs: 512, μ: 11825735, ~: 11844807)\n[PASS] testFuzzSplitAndChunkIndependence(uint96,uint8) (runs: 512, μ: 1497057, ~: 1209387)\n[PASS] testIntakeCannotReenterFundingAccounting() (gas: 581340)\n[PASS] testIntakeUnderchargeRollsEverythingBack() (gas: 591348)\n[PASS] testLongerDeadlineAllowsDelayedInclusionButStillExpires() (gas: 559030)\n[PASS] testPaidRequestsDeduplicateAndEnforceCaps() (gas: 967860)\n[PASS] testPartialFillRevertsWithoutKeepingTax() (gas: 231451)\n[PASS] testPaymentMustEqualCurrentIntakePriceEvenWithHigherOwnerCap() (gas: 726244)\n[PASS] testPlainCabalTransfersUntaxed() (gas: 69223)\n[PASS] testPrefundingAndDirectDonationsAreNotTaxed() (gas: 372653)\n[PASS] testRetiredActionCannotSpendReserveAndDoesNotBlockTrading() (gas: 913079)\n[PASS] testTreasuryFailureAndPaymentOutageDoNotBlockSwaps() (gas: 1116476)\n[PASS] testUnauthorizedDisabledExpiredAndEmptyFund() (gas: 385202)\n[PASS] testUnauthorizedInitializationIsRejected() (gas: 44130)\n[PASS] testUninitializedHookCannotBeClaimedByAnOutsider() (gas: 4638658)\nSuite result: ok. 20 passed; 0 failed; 0 skipped; finished in 997.06ms (2.29s CPU time)\n\nRan 2 tests for test/LaunchTokenInvariant.t.sol:LaunchTokenStatefulInvariantTest\n[PASS] invariantSupplyTransfersAllowancesAndEligibilityStayConsistent() (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------------+-----------------------+-------+---------+----------╮\n| Contract           | Selector              | Calls | Reverts | Discards |\n+=========================================================================+\n| LaunchTokenHandler | approve               | 2765  | 0       | 0        |\n|--------------------+-----------------------+-------+---------+----------|\n| LaunchTokenHandler | crossPvpBoundary      | 2792  | 0       | 0        |\n|--------------------+-----------------------+-------+---------+----------|\n| LaunchTokenHandler | overdraw              | 2658  | 0       | 0        |\n|--------------------+-----------------------+-------+---------+----------|\n| LaunchTokenHandler | revokeAndAttemptSpend | 2735  | 0       | 0        |\n|--------------------+-----------------------+-------+---------+----------|\n| LaunchTokenHandler | transfer              | 2721  | 0       | 0        |\n|--------------------+-----------------------+-------+---------+----------|\n| LaunchTokenHandler | transferFrom          | 2713  | 0       | 0        |\n╰--------------------+-----------------------+-------+---------+----------╯\n\n[PASS] testAllTokenHandlerBranchesIncludingFullBalanceAndThresholds() (gas: 1327678)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 6.59s (6.59s CPU time)\n\nRan 2 tests for test/CabalFundingHookInvariant.t.sol:CabalFundingImdFirstInvariantTest\n[PASS]\nCabalFundingImdFirstInvariantTest invariants:\n[PASS] invariantClaimsCashAndEveryLedgerMatchIndependentFlows\n[PASS] invariantFixedTokenSupplyAndPvpViewsTrackRealHoldings\n[PASS] invariantPaidDecisionsAndDailySpendingNeverResetOrReplay\n CabalFundingImdFirstInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------------+---------------------+-------+---------+----------╮\n| Contract            | Selector            | Calls | Reverts | Discards |\n+========================================================================+\n| CabalFundingHandler | advanceTime         | 1270  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | configure           | 1322  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | deposit             | 1245  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | donate              | 1241  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | failedIntakePayment | 1232  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | harvest             | 1250  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | pay                 | 1258  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | replay              | 1272  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | roundTrip           | 1291  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | swap                | 1265  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | sync                | 1227  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | treasury            | 1276  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | unauthorized        | 1235  | 0       | 0        |\n╰---------------------+---------------------+-------+---------+----------╯\n\n[PASS] testHandlerExercisesSuccessAndFailureTransitions() (gas: 2971855)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 7.03s (7.02s CPU time)\n\nRan 2 tests for test/CabalFundingHookInvariant.t.sol:CabalFundingImdSecondInvariantTest\n[PASS]\nCabalFundingImdSecondInvariantTest invariants:\n[PASS] invariantClaimsCashAndEveryLedgerMatchIndependentFlows\n[PASS] invariantFixedTokenSupplyAndPvpViewsTrackRealHoldings\n[PASS] invariantPaidDecisionsAndDailySpendingNeverResetOrReplay\n CabalFundingImdSecondInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------------+---------------------+-------+---------+----------╮\n| Contract            | Selector            | Calls | Reverts | Discards |\n+========================================================================+\n| CabalFundingHandler | advanceTime         | 1270  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | configure           | 1322  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | deposit             | 1245  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | donate              | 1241  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | failedIntakePayment | 1232  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | harvest             | 1250  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | pay                 | 1258  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | replay              | 1272  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | roundTrip           | 1291  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | swap                | 1265  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | sync                | 1227  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | treasury            | 1276  | 0       | 0        |\n|---------------------+---------------------+-------+---------+----------|\n| CabalFundingHandler | unauthorized        | 1235  | 0       | 0        |\n╰---------------------+---------------------+-------+---------+----------╯\n\n[PASS] testHandlerExercisesSuccessAndFailureTransitions() (gas: 2970138)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 7.42s (7.35s CPU time)\n\nRan 8 test suites in 7.42s (24.94s CPU time): 63 tests passed, 0 failed, 0 skipped (63 total tests)\n","passed":true},{"durationMs":90,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CabalFundingHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"CabalFundingHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"CabalFundingHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"CabalFundingHook.configureWorker(address,uint256,uint256,bool)\",\"CabalFundingHook.deposit(uint256)\",\"CabalFundingHook.harvest(uint256)\",\"CabalFundingHook.payRequest(bytes32,bytes,uint256,uint256)\",\"CabalFundingHook.payTreasury(uint256)\",\"CabalFundingHook.syncDonations()\",\"CabalFundingHook.unlockCallback(bytes)\",\"LaunchToken.approve(address,uint256)\",\"LaunchToken.transfer(address,uint256)\",\"LaunchToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"ADAPTATION.md\":57,\"README.md\":101,\"config.example.json\":21,\"deployment/CabalFundingHook.init.hex\":1,\"deployment/CabalFundingHook.runtime.hex\":1,\"deployment/LaunchToken.creation.hex\":1,\"deployment/LaunchToken.runtime.hex\":1,\"deployment/README.md\":32,\"deployment/abi/CabalFundingHook.json\":1129,\"deployment/abi/LaunchToken.json\":347,\"deployment/artifacts.json\":58,\"deployment/launch-terms.json\":10,\"foundry.toml\":14,\"launch.json\":34,\"package-lock.json\":238,\"package.json\":9,\"reports/build.txt\":322,\"reports/contract-review.txt\":85,\"reports/factory-fork-review.json\":130,\"reports/frontend-review.json\":23,\"reports/local-e2e.json\":25,\"reports/oracle-preflight-review.json\":50,\"reports/production-router-review.json\":270,\"reports/production-router-review.txt\":33,\"reports/protocol-review.json\":65,\"reports/solidity-tests.json\":1,\"reports/validation.json\":159,\"reports/worker-tests.tap\":250,\"scripts/check-oracle.mjs\":22,\"scripts/check-protocol.mjs\":43,\"scripts/launch-artifacts.mjs\":78,\"scripts/package-source.mjs\":44,\"scripts/review-factory.mjs\":175,\"scripts/review-router-cases.mjs\":103,\"scripts/simulate.mjs\":110,\"scripts/vendor-solidity.mjs\":32,\"source-integrity.json\":1914,\"src/CabalFundingHook.sol\":306,\"src/LaunchToken.sol\":20,\"test/CabalFundingHook.t.sol\":483,\"test/CabalFundingHookArithmetic.t.sol\":167,\"test/CabalFundingHookDayCap.t.sol\":147,\"test/CabalFundingHookFailures.t.sol\":442,\"test/CabalFundingHookInvariant.t.sol\":428,\"test/FactoryReviewMath.sol\":24,\"test/LaunchToken.t.sol\":35,\"test/LaunchTokenInvariant.t.sol\":183,\"test/LocalCreate2.sol\":9,\"test/REVIEW.md\":149,\"test/fixtures/IMDLaunchFactory.abi.json\":896,\"test/fixtures/IMDLaunchFactory.provenance.json\":13,\"test/fixtures/IMDTrading.provenance.json\":45,\"test/fixtures/IMDUniversalRouter.abi.json\":848,\"test/fixtures/IMDV4Quoter.abi.json\":650,\"test/fixtures/protocol-runtimes.json\":10,\"test/helpers/CabalReviewFixture.sol\":274,\"test/launch-manifest.test.mjs\":28,\"test/oracle-client.test.mjs\":42,\"test/replay-reviews.mjs\":50,\"test/review-results/local-e2e.json\":25,\"test/review-results/production-router-review.json\":270,\"test/review-results/protocol-review.json\":65,\"test/review-results/validation.json\":550,\"test/worker.test.mjs\":269,\"vendor-integrity.json\":1221,\"vendor/README.md\":13,\"vendor/bundle-inputs.json\":11366,\"vendor/licenses/@adraffy/ens-normalize/LICENSE\":21,\"vendor/licenses/@noble/ciphers/LICENSE\":22,\"vendor/licenses/@noble/curves/LICENSE\":21,\"vendor/licenses/@noble/hashes/LICENSE\":21,\"vendor/licenses/@scure/base/LICENSE\":21,\"vendor/licenses/@scure/bip32/LICENSE\":21,\"vendor/licenses/@scure/bip39/LICENSE\":21,\"vendor/licenses/abitype/LICENSE\":21,\"vendor/licenses/eventemitter3/LICENSE\":21,\"vendor/licenses/isows/LICENSE\":21,\"vendor/licenses/ox/LICENSE\":21,\"vendor/licenses/viem/LICENSE\":21,\"vendor/licenses/ws/LICENSE\":20,\"vendor/viem-entry.mjs\":2,\"vendor/viem.mjs\":20275,\"vendor/worker-dependencies.json\":87,\"worker/budget.mjs\":48,\"worker/chain-adapter.mjs\":114,\"worker/cli.mjs\":5,\"worker/day-cap.mjs\":18,\"worker/engine.mjs\":100,\"worker/oracle-client.mjs\":59,\"worker/service.mjs\":16,\"worker/store.mjs\":112},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"d1c021517645d61307d9d0ee126c74ecc9f5f4c0470d205bce08c1dfec694f1e","verifiedTreeHash":"00d40edf7af09bf6f7ab0452b2dc4e88accac5e7","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":1301,"exitCode":0,"name":"build","output":"Compiling 68 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.23s\nCompiler run successful!\n","passed":true},{"durationMs":576,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/LaunchToken.t.sol:LaunchTokenTest\n[PASS] testFactoryReceivesExactSupplyAndTransfersStayPlain() (gas: 112350)\n[PASS] testPvpBoundaryAndSellingBelowRequirement() (gas: 220400)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 620.33µs (498.73µs CPU time)\n\nRan 20 tests for test/CabalFundingHook.t.sol:FundingHookTest\n[PASS] testAllSwapModesAndOrderings() (gas: 46940698)\n[PASS] testCannotCallCallbacksOrBindAnotherPool() (gas: 103283)\n[PASS] testEndToEndSwapTreasuryAndOraclePayment() (gas: 1974118)\n[PASS] testFirstBuyIntoSingleSidedPool() (gas: 33529938)\n[PASS] testFuzzActualSwapFeeMath(uint64,uint8) (runs: 512, μ: 597794, ~: 599434)\n[PASS] testFuzzMixedOperationsPreserveAllLedgers(uint256) (runs: 512, μ: 11851371, ~: 11862317)\n[PASS] testFuzzSplitAndChunkIndependence(uint96,uint8) (runs: 512, μ: 1446930, ~: 1209333)\n[PASS] testIntakeCannotReenterFundingAccounting() (gas: 581340)\n[PASS] testIntakeUnderchargeRollsEverythingBack() (gas: 591348)\n[PASS] testLongerDeadlineAllowsDelayedInclusionButStillExpires() (gas: 559030)\n[PASS] testPaidRequestsDeduplicateAndEnforceCaps() (gas: 967860)\n[PASS] testPartialFillRevertsWithoutKeepingTax() (gas: 231451)\n[PASS] testPaymentMustEqualCurrentIntakePriceEvenWithHigherOwnerCap() (gas: 726244)\n[PASS] testPlainCabalTransfersUntaxed() (gas: 69223)\n[PASS] testPrefundingAndDirectDonationsAreNotTaxed() (gas: 372653)\n[PASS] testRetiredActionCannotSpendReserveAndDoesNotBlockTrading() (gas: 913079)\n[PASS] testTreasuryFailureAndPaymentOutageDoNotBlockSwaps() (gas: 1116476)\n[PASS] testUnauthorizedDisabledExpiredAndEmptyFund() (gas: 385202)\n[PASS] testUnauthorizedInitializationIsRejected() (gas: 44130)\n[PASS] testUninitializedHookCannotBeClaimedByAnOutsider() (gas: 4638658)\nSuite result: ok. 20 passed; 0 failed; 0 skipped; finished in 512.82ms (832.43ms CPU time)\n\nRan 2 test suites in 513.68ms (513.44ms CPU time): 22 tests passed, 0 failed, 0 skipped (22 total tests)\n","passed":true},{"durationMs":64,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CabalFundingHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"CabalFundingHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"CabalFundingHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"CabalFundingHook.configureWorker(address,uint256,uint256,bool)\",\"CabalFundingHook.deposit(uint256)\",\"CabalFundingHook.harvest(uint256)\",\"CabalFundingHook.payRequest(bytes32,bytes,uint256,uint256)\",\"CabalFundingHook.payTreasury(uint256)\",\"CabalFundingHook.syncDonations()\",\"CabalFundingHook.unlockCallback(bytes)\",\"LaunchToken.approve(address,uint256)\",\"LaunchToken.transfer(address,uint256)\",\"LaunchToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"ADAPTATION.md\":46,\"README.md\":101,\"config.example.json\":21,\"deployment/CabalFundingHook.init.hex\":1,\"deployment/CabalFundingHook.runtime.hex\":1,\"deployment/LaunchToken.creation.hex\":1,\"deployment/LaunchToken.runtime.hex\":1,\"deployment/README.md\":32,\"deployment/abi/CabalFundingHook.json\":1129,\"deployment/abi/LaunchToken.json\":347,\"deployment/artifacts.json\":58,\"deployment/launch-terms.json\":10,\"foundry.toml\":14,\"launch.json\":34,\"package-lock.json\":238,\"package.json\":9,\"reports/build.txt\":322,\"reports/contract-review.txt\":85,\"reports/factory-fork-review.json\":130,\"reports/frontend-review.json\":23,\"reports/local-e2e.json\":23,\"reports/oracle-preflight-review.json\":50,\"reports/production-router-review.json\":270,\"reports/production-router-review.txt\":33,\"reports/protocol-review.json\":65,\"reports/solidity-tests.json\":1,\"reports/validation.json\":158,\"reports/worker-tests.tap\":226,\"scripts/check-oracle.mjs\":22,\"scripts/check-protocol.mjs\":43,\"scripts/launch-artifacts.mjs\":78,\"scripts/package-source.mjs\":44,\"scripts/review-factory.mjs\":175,\"scripts/review-router-cases.mjs\":103,\"scripts/simulate.mjs\":92,\"scripts/vendor-solidity.mjs\":32,\"source-integrity.json\":1909,\"src/CabalFundingHook.sol\":306,\"src/LaunchToken.sol\":20,\"test/CabalFundingHook.t.sol\":483,\"test/FactoryReviewMath.sol\":24,\"test/LaunchToken.t.sol\":35,\"test/LocalCreate2.sol\":9,\"test/fixtures/IMDLaunchFactory.abi.json\":896,\"test/fixtures/IMDLaunchFactory.provenance.json\":13,\"test/fixtures/IMDTrading.provenance.json\":45,\"test/fixtures/IMDUniversalRouter.abi.json\":848,\"test/fixtures/IMDV4Quoter.abi.json\":650,\"test/fixtures/protocol-runtimes.json\":10,\"test/launch-manifest.test.mjs\":28,\"test/oracle-client.test.mjs\":42,\"test/worker.test.mjs\":212,\"vendor-integrity.json\":1221,\"vendor/README.md\":13,\"vendor/bundle-inputs.json\":11366,\"vendor/licenses/@adraffy/ens-normalize/LICENSE\":21,\"vendor/licenses/@noble/ciphers/LICENSE\":22,\"vendor/licenses/@noble/curves/LICENSE\":21,\"vendor/licenses/@noble/hashes/LICENSE\":21,\"vendor/licenses/@scure/base/LICENSE\":21,\"vendor/licenses/@scure/bip32/LICENSE\":21,\"vendor/licenses/@scure/bip39/LICENSE\":21,\"vendor/licenses/abitype/LICENSE\":21,\"vendor/licenses/eventemitter3/LICENSE\":21,\"vendor/licenses/isows/LICENSE\":21,\"vendor/licenses/ox/LICENSE\":21,\"vendor/licenses/viem/LICENSE\":21,\"vendor/licenses/ws/LICENSE\":20,\"vendor/viem-entry.mjs\":2,\"vendor/viem.mjs\":20275,\"vendor/worker-dependencies.json\":87,\"worker/budget.mjs\":45,\"worker/chain-adapter.mjs\":110,\"worker/cli.mjs\":5,\"worker/engine.mjs\":91,\"worker/oracle-client.mjs\":59,\"worker/service.mjs\":16,\"worker/store.mjs\":96},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1253,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/CabalFundingHook.sol:269: Reentrancy in CabalFundingHook.payRequest(bytes32,bytes,uint256,uint256) (src/CabalFundingHook.sol#269-305):\n[high/medium] reentrancy-balance at src/CabalFundingHook.sol:204: Reentrancy in CabalFundingHook.harvest(uint256) (src/CabalFundingHook.sol#204-220):\n[medium/medium] divide-before-multiply at src/CabalFundingHook.sol:204: CabalFundingHook.harvest(uint256) (src/CabalFundingHook.sol#204-220) performs a multiplication on the result of a division:\n[medium/medium] reentrancy-no-eth at src/CabalFundingHook.sol:269: Reentrancy in CabalFundingHook.payRequest(bytes32,bytes,uint256,uint256) (src/CabalFundingHook.sol#269-305):\n[medium/medium] reentrancy-no-eth at src/CabalFundingHook.sol:204: Reentrancy in CabalFundingHook.harvest(uint256) (src/CabalFundingHook.sol#204-220):\n[medium/medium] uninitialized-local at src/CabalFundingHook.sol:166: CabalFundingHook.beforeSwap(address,PoolKey,SwapParams,bytes).fee (src/CabalFundingHook.sol#166) is a local variable never initialized\n[medium/medium] unused-return at src/CabalFundingHook.sol:204: CabalFundingHook.harvest(uint256) (src/CabalFundingHook.sol#204-220) ignores return value by manager.unlock(abi.encode(amount)) (src/CabalFundingHook.sol#207)\n[low/medium] reentrancy-benign at src/CabalFundingHook.sol:149: Reentrancy in CabalFundingHook._accrue(uint256) (src/CabalFundingHook.sol#149-156):\n[low/medium] reentrancy-benign at src/CabalFundingHook.sol:204: Reentrancy in CabalFundingHook.harvest(uint256) (src/CabalFundingHook.sol#204-220):\n[low/medium] reentrancy-events at src/CabalFundingHook.sol:149: Reentrancy in CabalFundingHook._accrue(uint256) (src/CabalFundingHook.sol#149-156):\n[low/medium] timestamp at src/CabalFundingHook.sol:269: CabalFundingHook.payRequest(bytes32,bytes,uint256,uint256) (src/CabalFundingHook.sol#269-305) uses timestamp for comparisons","passed":true},{"durationMs":366,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/CabalFundingHook.sol:206: Reentrancy: State change after external call (10 places)\n[low] centralization-risk at src/CabalFundingHook.sol:260: Centralization Risk\n[low] large-numeric-literal at src/LaunchToken.sol:9: Large Numeric Literal (2 places)\n[low] literal-instead-of-constant at src/CabalFundingHook.sol:146: Literal Instead of Constant (7 places)\n[low] modifier-used-only-once at src/CabalFundingHook.sol:107: Modifier Invoked Only Once\n[low] state-change-without-event at src/CabalFundingHook.sol:120: State Change Without Event\n[low] unchecked-return at src/CabalFundingHook.sol:170: Unchecked Return (3 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"eacc9012880b9bf52d78203f7497fb4956b6f48f704e8ea44519cba3f0c80fad","verifiedTreeHash":"6d7a676d136cd49b31742694437367d3ae1b1888","verifierVersion":"0.1.0+a6ee6548"}]}