{"assessments":[],"deployments":[{"attestationHash":"4d1bf3ad99393aaa496fa10911a4fde202a14ee151b8688fb7f76bb4c842b1e6","chainId":11155111,"contracts":[],"id":"bf870e61-7867-483a-b1d3-894e72e97312","manifestHash":"69f3d7abe36c988f0e56ade7831c511acb1b6d484baa3df4e67582173f2d408b","status":"parked"}],"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":"31731677-968e-46e0-82b2-1e6a64463c3b","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"cb66dce37eabde249cb46aff9f27b8f3b9b0389d99e3627c351e3d505defbbd3","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"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":"16025f1c4bc065354fcae08bd9105cd0c67d6522a4e5b7aeb7f09f9ff8f78285","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"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":"6216001836059269cb140661d5b9a9d8c89ae34784daeb7ad791f2a0099131dc","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":"5b49911bcb71bd9d86573a4c36c90d84679d3a907b13d0cfa1aa959d148846cb","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"31c3fe4d4221576d79bd48ed0c879481379d8d18245a295f27aea2f8a92aff90","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"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":"27ccece7cce5036ad1a74c9896f99f52aeb4818df87392f1378f576890160e0e","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"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":"d81c1ac6e5bd1956bb25bd9e4c2b8381258e632425a69987d1482fe2d457c849","dependsOn":["audit_imported_code"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"587567a5ef71546b5973ceaf6ef1dbbd590f49503b5ac9a6973ed86b89584857","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"587567a5ef71546b5973ceaf6ef1dbbd590f49503b5ac9a6973ed86b89584857","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"c42b46c9d76aec21a0e6480c1f83e59c402e89da12af50391622e1bac182fe14","dependsOn":["audit_imported_code","build_contract_project","write_foundry_tests"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"274fea5fd6b5238338796ff7706efe06c1caebe205faf1ed014805ee31e3ec4d","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Token name: SOVRN.ONE (the name itself contains one full stop; it is exactly the 9 characters S O V R N . O N E). Token symbol: SVO. Total supply 1,000,000,000 with 18 decimals. Chain id 11155111 (Sepolia rehearsal; the mainnet version differs only in this chain id and REFUEL_SAFE), paired with native ETH, opening cap 10 ETH (currency0 native ETH, currency1 SVO, sqrtPriceX96 792281625142643375935439503360000 = 100,000,000 SVO per ETH, as in the base).\n\nBASE. The workspace is a Uniswap v4 hook launch whose hook constructor deploys its child contracts. Keep layout, lib/, foundry.toml UNCHANGED (bytecode_hash \"none\"; README quotes it), errors, guards and still-applicable tests. Rename OG* to Sovrn*. Delete OGDistributor, OGAuction, SPEPE interfaces and constants, TEAM, and every NFT/reward/auction path. README credits the base, lists changes and who can call what; no yield, returns, profit or investment wording; no audit claim.\n\nPURPOSE. SVO trading fees (ETH only) fund the inference of a voice AI. All hook fees go to one immutable vault that only accounts for them: 70% is earmarked for inference, 30% for buying SVO to burn. The vault never swaps; a Safe withdraws and does the buyback by hand. Holders get no payouts.\n\nCONTRACTS (exact).\n1. SovrnToken: constructor() no args; name \"SOVRN.ONE\", symbol \"SVO\", 18 decimals; fixed supply minted to msg.sender; name() returns exactly \"SOVRN.ONE\" and symbol() exactly \"SVO\" (launch.json token.name and token.symbol match); plain ERC-20 (no tax, owner, mint, pause, blacklist). Keep the base's DEAD constant (0x000000000000000000000000000000000000dEaD) and totalBurned.\n2. SovrnHook: constructor(IPoolManager manager, SovrnToken token, address factory); launch.json FLAT [\"$poolManager\",\"$token\",\"$factory\"]. Behaviour UNCHANGED from the base hook (3.5% ETH fee on buys and sells, buy fee decaying 50% to 3.5% over 3,600 s, same exact-in/out handling, quote mechanism, permissions, flags 8396, one pool) EXCEPT the destination: the ENTIRE hook fee goes to the vault (ETH directly when the manager holds enough, else ERC-6909 claims; redeemFees() permissionless, pays the vault; one claim counter). Remove teamCredit/payTeam. The constructor deploys the vault: new LifeForceVault(manager, token, address(this)). Getters: vault(), poolKey(), launchFeeNow(), decayMinutesLeft().\n3. LifeForceVault: constructor(IPoolManager manager, SovrnToken token, address hook) (manager unused beyond a code check). Constants REFUEL_SAFE = 0xb1eC9d1C36974d05eb9889eBf8A150b05791E559, INFERENCE_BPS 7000, BUYBACK_BPS 3000. No owner, setters, swap logic or upgrade.\n - receive() payable: floor(value*3000/10000) to buybackReserve, the rest to inferenceReserve; event LifeForceFunded(address indexed from, uint256 amount, uint256 inference, uint256 buyback).\n - withdrawInference(uint256 amount) and withdrawBuyback(uint256 amount): only REFUEL_SAFE; amount <= the matching reserve; reduce it, pay REFUEL_SAFE, revert if the payment fails; events InferenceWithdrawn(uint256), BuybackWithdrawn(uint256). These are the ONLY ways ETH leaves the vault.\n - burn(): anyone; sends the vault's ENTIRE SVO balance to DEAD and nothing else; reverts at zero; event Burned(uint256). The vault can send SVO nowhere else (anyone may transfer SVO to it).\n - Views: inferenceReserve, buybackReserve, sovrnHeld. ETH balance always equals inferenceReserve + buybackReserve. Reentrancy-guard both withdrawals.\n\nMANIFEST. launch.json kind univ4_hook, token SovrnToken, hook SovrnHook with the flat args; the vault is NOT in it. Keep the base pool block (fee 12500, tickSpacing 60). script/PrepareLaunch.s.sol still mines CREATE2 salts for the new initcode and flags; no env vars, no broadcast.\n\nTESTS (Foundry; these numbers are the contract of record). Cover: name() == \"SOVRN.ONE\" and symbol() == \"SVO\"; fee identical to the base in all four exact modes; decay at 0/1800/3600 s; 100% of fees reach the vault (direct and via claims + redeemFees); 70/30 split rounding; each withdraw only by REFUEL_SAFE and never above its reserve, reentrancy-safe, and reverting when the Safe rejects ETH; burn sends the full balance to DEAD, reverts at zero, cannot send elsewhere; exact permissions; second init and wrong pool key rejected; ETH accounting invariant; no privileged roles besides REFUEL_SAFE. Use a real v4 PoolManager if possible, else a faithful mock and say which.\n\nDELIVER README with every number and privilege, the prepare script, the launch attestation.","parentJobId":null,"planHash":"7c33a9816ab1d3663e74c971dc46e8f1dac8d399492de24afd29f5739492362a","previousHash":"dcf39b19bdbf46eb9b0ffac9700425c69c9a44962f781bf5152dbbc374dd41cb","projectId":"31731677-968e-46e0-82b2-1e6a64463c3b","publication":{"commit":"19c642a782b0c8d1e18b87b8ceaf5ed8ad6cbf75","deliveredAt":"2026-10-08T18:55:21.239Z","repoUrl":"https://github.com/identity-md-launches/launch-1069-sovrn-one-the-name-itself-contains-one-f"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[],"site":null,"snapshotHash":"42173c653fdd395c44a16357ae7a5574449a0237193a8dfc618ce2df4e6716ea","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"9b06782c7559b54c","findings":[{"citation":"resolved","description":"Periphery / deliverable. The README (section 'Preparation and operation', step 4, and 'Validation and scope') tells operators to regenerate the launch attestation with `python3 script/attest.py` and to verify it with `python3 script/attest.py --check`. The committed launch.json was correctly reduced to the schema's five top-level keys (kind, hook, token, pool, notes; the schema has additionalProperties:false, so a chainId key would be refused). The script still hard-asserts a sixth top-level key `chainId`, so build_record() raises AssertionError before producing a record in both modes. Consequently the committed launch-attestation.json cannot be regenerated or checked with the committed tooling, and it no longer describes the tree: its deliverySha256 for launch.json (recorded d5e91f208512...) differs from the file now in the tree (a0739ccf96a0...), test/AdversarialFees.t.sol is also stale, and six delivered test files (test/FeeDifferential.t.sol, test/README.md, test/SettlementFailures.t.sol, test/TokenFailurePaths.t.sol, test/VaultCallbackBoundaries.t.sol, test/VaultModelInvariants.t.sol) are not recorded at all. The contract creation bytecodes recorded in the attestation do match the current `forge build` output, so the Solidity side is unaffected; the defect is that the attestation deliverable and its verification script contradict the manifest deliverable. The manifest's own notes field acknowledges this as an open follow-up, which confirms it was knowingly left in the tree. Fix: drop `chainId` from the asserted key set (keep the recorded `chainId: 11155111` in the output record as a script constant), regenerate launch-attestation.json, and re-run `--check`.","line":27,"path":"script/attest.py","reproduction":"State: the committed tree at HEAD, forge installed. Run `python3 script/attest.py --check` (or without --check). Expected: 'Launch manifest and attestation match the current build.' (or a regenerated file). Actual: `AssertionError` at script/attest.py line 27 in build_record(), exit code 1, no record produced. Independently: sha256 of launch.json is a0739ccf96a0... while launch-attestation.json deliverySha256['launch.json'] is d5e91f208512...; test/AdversarialFees.t.sol likewise differs; six test files present in the tree have no deliverySha256 entry.","severity":"medium","snippet":"    assert set(manifest) == {\"kind\", \"chainId\", \"hook\", \"token\", \"pool\", \"notes\"}\n    assert manifest[\"kind\"] == \"univ4_hook\" and manifest[\"chainId\"] == 11155111","title":"script/attest.py asserts a chainId manifest key that launch.json no longer has, so the README's attestation regenerate/check path fails and launch-attestation.json is stale for the delivered tree"},{"citation":"resolved","description":"Execution-trace / periphery note answering the slither 'locked-ether' and aderyn 'contract-locks-ether' leads. Every fee path sends ETH to the vault (afterSwap line 200 `poolManager.take(key.currency0, address(vault), fee)`, unlockCallback line 231 `poolManager.take(Currency.wrap(address(0)), address(vault), amount)`); the hook never names itself as a take recipient. The receive() is therefore a leftover from the base's team-payment path and only serves to accept ETH that a third party deliberately takes to the hook address inside their own unlock. The hook has no function that moves its own ETH balance (redeemFees only burns ERC-6909 claims and takes to the vault), so such ETH is unrecoverable. No fee ETH and no other party's ETH is at risk: only the sender who chose the hook as recipient loses it, so this is self-harm and not a vulnerability. Reported so the lead is closed with evidence rather than left open; the minimal fix is to delete receive() (a native take to the hook then reverts with NativeTransferFailed instead of locking), or to document in the README that the hook cannot hold or forward ETH.","line":236,"path":"src/SovrnHook.sol","reproduction":"State: initialized pool (any). A contract calls poolManager.unlock, inside its callback does sync(native); settle{value: 1 ether}(); take(Currency.wrap(address(0)), address(hook), 1 ether). Expected (for a hook that is not meant to hold ETH): the transfer is refused. Actual: hook.receive() accepts it because msg.sender is the manager; address(hook).balance == 1 ether afterwards; hook.redeemFees() leaves it unchanged (claimFees is 0) and the vault balance stays 0. Verified with test/scratch/StuckInHook.t.sol test_managerTakeToHookIsAcceptedAndUnrecoverable on the real vendored PoolManager.","severity":"info","snippet":"    receive() external payable {\n        if (msg.sender != address(poolManager)) revert Unauthorized();\n    }","title":"SovrnHook.receive() accepts ETH from the PoolManager although no code path ever routes ETH to the hook, so any ETH taken to the hook is permanently locked"}],"hash":"16025f1c4bc065354fcae08bd9105cd0c67d6522a4e5b7aeb7f09f9ff8f78285","nodeId":"1d139cf4-020f-4453-adac-81fc3b58a274","outcome":"completed","summary":"The review is complete and `.imd-findings.json` is written with two findings and a coverage row for every listed entry point. Nothing outside the allowed paths was changed. Git status is clean because the findings file and the scratch test are both ignored.\n\n**What I found**\n\n- **Medium, periphery deliverable.** The attestation script asserts a top-level `chainId` key in the manifest. The manifest was correctly trimmed to the five keys the schema allows, so both the regenerate and the `--check` paths the README advertises abort with an AssertionError before producing a record. The committed attestation is also stale. Its recorded hash for the manifest and for one test file differ from the tree, and six delivered test files are not recorded at all. Contract creation bytecodes still match the current build, so the Solidity side is unaffected.\n- **Info, hook.** The hook's `receive()` accepts ETH from the PoolManager, but no code path ever sends ETH to the hook and no function moves ETH out of it. ETH a third party deliberately takes to the hook is locked. This is self-harm only. I verified it with a scratch test on the real vendored PoolManager, and it closes the slither and aderyn locked-ether leads.\n\n**What held in my area**\n\nExecution trace and first-principles checks on all three contracts passed. The quote mechanism relies on the manager skipping callbacks when the hook is the swap sender, which I confirmed in the vendored library. The post-hook swap amount equals the quoted amount in both ETH-specified modes, including the exact-output sell rounding and partial fills, so the quote-mismatch guard cannot be tripped by a third party. Fee deltas net the hook to zero on both the direct-take and claim paths. The busy and redeeming flags cannot be left set because every path that sets them is atomic. The vault's reserve views, withdrawals, burn and reentrancy guard all behave as the brief requires. The manifest is schema-valid with notes under the length cap.\n\n**Leads I judged not to be defects**\n\nSlither's divide-before-multiply on the vault split is a false positive; the formula is exactly floor of the product. Third-party ERC-6909 claims minted to the hook are not redeemed, but the README documents this as intended.\n\n**Static analysis:** slither and aderyn output was given to me as leads. I did not run them myself. The full existing suite passes with 63 tests, and `forge fmt --check` is clean.","treeHash":null,"usage":{"cachedInputTokens":1634836,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":40140,"runtime":"claude","turns":41,"wallClockMs":690267}},{"artifacts":[],"attempt":1,"bundleHash":"0f8c21d05917dea14bc745af7aeadcac5a2a76e92be318703f925d9984c77915","device":"e7f8c6542ba6fced","findings":[],"hash":"274fea5fd6b5238338796ff7706efe06c1caebe205faf1ed014805ee31e3ec4d","nodeId":"4a6b659b-5d3d-466f-addb-a3ce2ba78e1c","outcome":"completed","summary":"Added three targeted regressions for claim rounding, zero-fee trades, and settlement rollback. Updated `test/README.md`; all changes stay under `test/`.\n\nValidation: `forge build` passed; `forge test` passed **71 tests**, with zero failures or skips. Both invariant campaigns passed using the real local v4 PoolManager. No new contract defect was reproduced.","treeHash":"25176c566effd8003f7be54b2204689d0817d04a","usage":{"cachedInputTokens":1526144,"inputTokens":108028,"model":"gpt-6-astra","outputTokens":6604,"runtime":"codex","turns":7,"wallClockMs":239490}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"6b16b4979d227241","findings":[{"citation":"resolved","description":"Asymmetry: the hook has two inflow surfaces with no matching outflow. (1) `receive()` admits ETH whenever `msg.sender` is the PoolManager, yet no hook path ever moves native balance out: `afterSwap` takes fees straight to the vault and `unlockCallback` takes redeemed claims to the vault, so the hook's own ETH balance is never read or spent. The guard only stops direct sends; any account can route ETH through the manager with its own unlock (`settle{value}` then `take(native, hook, amount)`), which the guard accepts. (2) Likewise anyone can `mint(hook, 0, amount)` ERC-6909 native claims to the hook inside their own unlock; `redeemFees()` burns exactly `claimFees`, so claims the hook holds beyond its counter are never redeemed. Both are permanently stranded: the hook has no owner, sweep, or selfdestruct, and the attested bytecode is final. No third party loses funds (only the pusher), so this is low, but the `receive()` function is dead weight inherited from the base's team-payment flow and widens the lock-ether surface for no purpose. Minimal fix that preserves the brief: delete `receive()` (no hook path needs the hook to hold ETH; the manager-only guard already shows it is unintended), and optionally make `redeemFees()` redeem `poolManager.balanceOf(address(this), 0)` instead of the counter so stray claims still reach the vault. Both changes alter the creation bytecode, so salts and launch-attestation.json must be regenerated.","line":236,"path":"src/SovrnHook.sol","reproduction":"Deploy PoolManager, SovrnToken, SovrnHook at a 0x..20cc address (factory = test), initialize the pool at 792281625142643375935439503360000. (a) `address(hook).call{value: 1 ether}(\"\")` from an EOA -> reverts Unauthorized (expected). (b) A contract calls `manager.unlock(...)`; in its callback: `manager.sync(Currency.wrap(address(0))); manager.settle{value: 1 ether}(); manager.take(Currency.wrap(address(0)), address(hook), 1 ether);` -> succeeds; `address(hook).balance == 1 ether`, `hook.claimFees() == 0`; `hook.redeemFees()` is a no-op; vault balance stays 0; no ABI function can ever move that 1 ether. (c) Same callback with `manager.mint(address(hook), 0, 1 ether)` instead of take -> `manager.balanceOf(hook, 0) == 1 ether`, `hook.claimFees() == 0`, `redeemFees()` leaves the claim in place forever. Expected per README: 'All hook fees go to one immutable vault' and nothing accumulates in the hook; actual: ETH/claims can accumulate in the hook with no exit. Reproduced locally in test/scratch/HookInflow.t.sol (both cases pass as written, demonstrating the stranding).","severity":"low","snippet":"    receive() external payable {\n        if (msg.sender != address(poolManager)) revert Unauthorized();\n    }","title":"Hook accepts manager-routed ETH and third-party native claims but has no outflow for either (inflow/outflow asymmetry)"},{"citation":"resolved","description":"README.md line 79 tells operators to 'regenerate with `python3 script/attest.py`, or verify with `python3 script/attest.py --check`'. Both commands abort at this assertion because launch.json (correctly, per the LaunchManifest schema which has `additionalProperties: false` and no `chainId`) no longer carries a top-level `chainId`. Consequently launch-attestation.json cannot be regenerated or checked, and its `deliverySha256` map is already stale for two files: launch.json (recorded d5e91f208512..., actual a0739ccf96a0...) and test/AdversarialFees.t.sol (recorded 2f64e1b32011..., actual cca590b89cf7...). The three creation bytecodes and every `sourceSha256` entry still match the tree, so the attestation's contract linkage is intact; what is broken is the README's verification procedure and the manifest binding, which the brief lists as a deliverable ('the launch attestation'). The manifest notes disclose this gap but the README does not. Fix: have attest.py read the chain id from its own constant (it already hardcodes 11155111 on line 29) and assert `set(manifest) == {\"kind\",\"hook\",\"token\",\"pool\",\"notes\"}`, then regenerate launch-attestation.json so the delivery hashes match.","line":27,"path":"script/attest.py","reproduction":"From the repository root run `python3 -I script/attest.py --check`. Expected (per README line 79): prints 'Launch manifest and attestation match the current build.' Actual: `AssertionError` raised at script/attest.py line 27 in build_record (`assert set(manifest) == {...\"chainId\"...}`), because `set(json.load(open('launch.json')))` is {kind, hook, token, pool, notes}. Independently: `sha256sum launch.json` gives a0739ccf96a0... while launch-attestation.json deliverySha256['launch.json'] is d5e91f208512....","severity":"low","snippet":"    assert set(manifest) == {\"kind\", \"chainId\", \"hook\", \"token\", \"pool\", \"notes\"}","title":"README's attestation regenerate/verify command fails: attest.py asserts a `chainId` manifest key the schema forbids, and the recorded delivery hashes are stale"},{"citation":"resolved","description":"Access x economics seam, recorded as a trust assumption rather than a defect because the brief specifies exactly this design ('a Safe withdraws and does the buyback by hand'). Both reserves are withdrawable by the same single address to the same destination, so the INFERENCE_BPS/BUYBACK_BPS split constrains only the labels on two counters, not where ETH ends up. The Safe can empty the vault in two calls, may spend buyback ETH on anything, and no contract path ever purchases or burns SVO; `burn()` only acts on SVO somebody chose to send. Holders therefore rely entirely on the Safe's conduct for the advertised 30% buyback. README documents this ('spending after withdrawal is not enforced by the contracts'). No code change is required by the brief; if the requester wants the earmark enforced, that is a scope decision (for example a separate buyback destination or an on-chain purchase path), not a fix.","line":59,"path":"src/LifeForceVault.sol","reproduction":"Fund the vault with 10 ether via `receive()` -> inferenceReserve()==7 ether, buybackReserve()==3 ether. `vm.prank(0xb1eC9d1C36974d05eb9889eBf8A150b05791E559); vault.withdrawBuyback(3 ether); vault.withdrawInference(7 ether);` -> vault balance 0, the Safe holds all 10 ether, `token.balanceOf(DEAD)` unchanged at 0, `totalBurned()==0`. Nothing reverted and nothing was bought back, which matches the code's actual guarantee (destination and bound only).","severity":"info","snippet":"    function withdrawInference(uint256 amount) external onlySafe nonReentrant {\n        (inference, buyback) = _reserves();\n        if (amount > inference) revert InvalidAmount();\n        inference -= amount;\n        _sendETH(REFUEL_SAFE, amount);\n        emit InferenceWithdrawn(amount);\n    }","title":"Trust assumption: the 70/30 earmark is accounting only; REFUEL_SAFE can withdraw 100% of vault ETH to itself and nothing on chain enforces inference spending or buybacks"},{"citation":"resolved","description":"The withdrawals are the ONLY ETH exits from the vault, and the only ETH exit from the whole system (hook fees reach nothing else). The recipient is a compile-time constant with no rotation path, so correctness of the launch depends on the requester actually controlling 0xb1eC9d1C36974d05eb9889eBf8A150b05791E559 on Sepolia and on that account being able to receive ETH (a Safe proxy must exist at that address on Sepolia, not only on mainnet; a contract that rejects ETH makes every withdrawal revert with ETHSendFailed). The README already assigns this check to operators and states the mainnet build changes the constant. Recorded so the judge can require evidence (a 0-value or 1-wei withdrawal from that address on Sepolia) before admission; not a code defect.","line":10,"path":"src/LifeForceVault.sol","reproduction":"State: the vault holds any ETH; no key or Safe proxy controls 0xb1eC9d1C36974d05eb9889eBf8A150b05791E559 on chain 11155111. Any caller other than that address -> `withdrawInference(1)` / `withdrawBuyback(1)` revert Unauthorized (LifeForceVault.sol line 35). No other function moves ETH (burn moves only SVO; receive only adds). Result: the ETH is permanently locked. Conversely, if the address holds code that rejects plain ETH, `vm.etch(safe, RejectETH.code); vm.prank(safe); vault.withdrawInference(7 ether)` reverts ETHSendFailed (test/Vault.t.sol test_rejectingSafeRollsBackBothWithdrawalsAndCanRetry shows the revert).","severity":"info","snippet":"    address public constant REFUEL_SAFE = 0xb1eC9d1C36974d05eb9889eBf8A150b05791E559;","title":"Trust assumption: REFUEL_SAFE is a hardcoded constant whose control on chain 11155111 is not verified; if uncontrolled, every fee is irrecoverable"}],"hash":"27ccece7cce5036ad1a74c9896f99f52aeb4818df87392f1378f576890160e0e","nodeId":"4f7fbab3-0ef5-44eb-b810-083ed28f5d17","outcome":"completed","summary":"The review is complete and recorded in `.imd-findings.json`. Nothing in `src/`, `script/`, `test/` (outside `test/scratch/`) or configuration was changed; git shows only the findings file and my scratch test as untracked.\n\n**Outcome: no critical, high or medium defects in the Access Control, Trust Gap or Asymmetry areas.** The permission model is small and consistent. Every state-changing entry point was traced, and all 14 verifier-listed entry points have a coverage row, plus three invariant rows.\n\nWhat I checked and found to hold:\n\n- **Caller guards** on every hook callback (`onlyManager`), the once-only factory-bound `beforeInitialize`, the self-only reverting quote, and the `redeeming && busy` gate on `unlockCallback`. The vendored Hooks library skips both callbacks when the hook itself is the swapper, so the quote cannot re-enter the fee path.\n- **Vault authority** is exactly one address. Both withdrawals share one reentrancy slot, checkpoint forced ETH, debit before paying, and pay only the Safe. `burn()` has a single destination. The 30/70 split is exact integer math, so the Slither divide-before-multiply lead is a false positive.\n- **Busy flag** cannot stick: the PoolManager either reaches `afterSwap` or reverts the write.\n\nFindings written, in severity order:\n\n1. **Low.** The hook's `receive()` accepts ETH routed through any unlocker's `take`, and anyone can mint id-0 claims to the hook. Neither has an outflow, so both are stranded forever. Only the pusher loses, which keeps it low. Reproduced in `test/scratch/HookInflow.t.sol`. Suggested fix is to remove `receive()` since no path needs the hook to hold ETH.\n2. **Low.** The README tells operators to regenerate or verify the attestation with `script/attest.py`, but the script asserts a top-level `chainId` manifest key the schema forbids and aborts. Two delivery hashes in `launch-attestation.json` are stale. All creation bytecodes and source hashes still match the tree.\n3. **Info, trust assumption.** The 70/30 earmark is labels only. The Safe can withdraw all ETH to itself and nothing on chain buys or burns SVO. This matches the brief and the README says so.\n4. **Info, trust assumption.** `REFUEL_SAFE` is a hardcoded constant and the sole ETH exit. Control of that address on Sepolia is unverified here. If uncontrolled, every fee is locked.\n\nStatic-analysis leads not promoted: locked-ether (became finding 1), divide-before-multiply and incorrect-equality (false positives), reentrancy and timestamp lines (guarded or by design, base behaviour).\n\nNot reached: no live Sepolia check of the Safe address, and no fork run. Both are outside what this offline task can do.","treeHash":null,"usage":{"cachedInputTokens":1220488,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":37431,"runtime":"claude","turns":32,"wallClockMs":604612}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"6208734cdf5317a1","findings":[{"citation":"resolved","description":"README step 4 tells operators to verify the build record with `python3 script/attest.py --check`. The script asserts a top-level `chainId` key in launch.json, but the LaunchManifest schema has additionalProperties:false and no chainId field, so a manifest that is accepted by the launch system makes the script throw before it compares a single hash. Independently of the crash, the committed launch-attestation.json no longer matches the tree: its deliverySha256 entries for launch.json (recorded d5e91f20...) and test/AdversarialFees.t.sol (recorded 2f64e1b3...) differ from the current files (a0739ccf... and cca590b8...). The manifest notes acknowledge the staleness, but the README still presents the record and the --check command as the way to bind chain id, constructor arguments, Safe address and bytecode before deployment, so the binding it promises is not currently verifiable. Boundary exercised: the manifest file read at script/attest.py:26-27; assumption: the manifest carries chainId; actual: schema-valid manifest has none, AssertionError. Minimal fix: drop the chainId assertion (take the chain id from the attestation record or a separate config, as the notes already propose), regenerate launch-attestation.json, and re-run --check so every recorded hash matches.","line":27,"path":"script/attest.py","reproduction":"State: current tree. Run `python3 script/attest.py --check`. Expected: exit 0 with every recorded hash matching. Actual: `AssertionError` raised at script/attest.py line 27 (traceback through build_record at line 100), no hashes compared. Then run `python3 -c \"import json,hashlib;a=json.load(open('launch-attestation.json'));[print(k,v[:12],hashlib.sha256(open(k,'rb').read()).hexdigest()[:12]) for k,v in a['deliverySha256'].items() if hashlib.sha256(open(k,'rb').read()).hexdigest()!=v]\"`. Actual output: `launch.json d5e91f208512 a0739ccf96a0` and `test/AdversarialFees.t.sol 2f64e1b32011 cca590b89cf7`, i.e. two of 23 recorded delivery hashes are stale; expected none.","severity":"low","snippet":"    assert set(manifest) == {\"kind\", \"chainId\", \"hook\", \"token\", \"pool\", \"notes\"}","title":"Launch attestation deliverable is stale and its own check script cannot run against the schema-valid manifest"},{"citation":"resolved","description":"beforeSwap and afterSwap accept a swap on the pool as soon as `initialized` is true, with no liquidity condition. On a pool whose liquidity is zero, v4's swap loop walks the tick bitmap until sqrtPriceLimitX96 with amountIn = amountOut = 0, and persists the new price. Both hook fee formulas evaluate on the zero native delta: in the exact-in sell path afterSwap computes `fee = actual * rate / WAD = 0`, so nothing is taken, no claim is minted, and the swap completes with delta (0, 0) while slot0 now holds the attacker's limit. The brief's opening numbers (sqrtPriceX96 792281625142643375935439503360000, 100,000,000 SVO per ETH, 10 ETH cap for the supply) are therefore only guaranteed if the factory seeds liquidity in the same transaction as PoolManager.initialize. If seeding happens in a later transaction, anyone can move the price first: a one-sided SVO position placed at the opening tick then lands partly or wholly in range and requires ETH the factory does not supply, or trades open at an arbitrary ratio. This is preserved base behaviour and the brief forbids changing swap behaviour, so the remedy is a scope decision: either document and enforce atomic initialize-plus-seed in the launch factory (README currently only requires deployment and initialization to be atomic), or, if a behaviour change is accepted, revert in beforeSwap while `poolManager.getLiquidity(poolId) == 0`. Seam: boundary (zero liquidity, zero-amount fill) x invariant (opening price and cap); the fee math itself behaves correctly at the edge.","line":114,"path":"src/SovrnHook.sol","reproduction":"State: real vendored PoolManager, SovrnToken, SovrnHook at 0x20cc with factory = test contract, pool initialized at sqrtPriceX96 792281625142643375935439503360000, no liquidity added, chain id 11155111. Input: ALICE (holding no SVO) calls a minimal settlement router that unlocks and swaps `SwapParams(zeroForOne=false, amountSpecified=-1, sqrtPriceLimitX96=7922816251426433759354395033600000)` (ten times the opening sqrt price). Actual: swap succeeds, BalanceDelta is (amount0 = 0, amount1 = 0), vault balance stays 0, and `getSlot0(poolId).sqrtPriceX96` becomes 7922816251426433759354395033600000; ALICE paid only gas and no tokens. Expected under the brief: the opening price 792281625142643375935439503360000 (100,000,000 SVO per ETH) holds until the factory's liquidity is in place. Reproduced in test/scratch/MathEdges.t.sol::test_zeroLiquidityPriceMove, which logs `amount0: 0`, `amount1: 0`, `price after: 7922816251426433759354395033600000`.","severity":"low","snippet":"    function beforeSwap(address, PoolKey calldata key, SwapParams calldata params, bytes calldata)","title":"While the initialized pool has zero liquidity, anyone moves its price to any limit at zero cost (boundary x opening-price invariant)"},{"citation":"resolved","description":"Every fee division in beforeSwap (lines 133, 136, 143, 145) and afterSwap (line 191) truncates toward zero, in the trader's favour. The Math Precision checklist asks that fees round up. The effect is bounded by one wei per swap and cannot be amplified: splitting a trade into many dust swaps costs far more gas than the saved wei, and the LP fee on the AMM side still applies. The brief requires fee behaviour identical to the base and the README states that integer divisions round down including tiny-amount rounding, so this is an accepted design property, recorded here with concrete numbers so the author can decide whether to keep it. No change is recommended under the current brief.","line":133,"path":"src/SovrnHook.sol","reproduction":"State: pool seeded with full-range liquidity 1e21 at the launch price, real PoolManager. (a) At elapsed 0 (rate 0.5e18) an exact-in buy of 1 wei ETH: fee = 1 * 0.5e18 / 1e18 = 0; vault receives 0 (expected 50% of gross, which would be 1 wei if rounded up). (b) Exact-in buy of 3 wei at the same rate: fee = 1 wei, an effective 33.3% instead of 50%. (c) At elapsed 3600 s an exact-out sell for 27 wei ETH: gross = 27 * 1e18 / 0.965e18 = 27 (27.97 floored), fee = 27 * 0.035e18 / 1e18 = 0; trader receives 27 wei with no hook fee, whereas 28 wei requested gives gross 29 and fee 1. Reproduced in test/scratch/MathEdges.t.sol::test_dustFeeZero, logging `buy 1 wei amount0: -1`, `vault after 3 wei buy: 1`, `fee on 28 wei exact-out sell: 1`, `fee on 27 wei exact-out sell: 0`, `amount0 27: 27`.","severity":"info","snippet":"                fee = requested * rate / WAD;","title":"All four fee formulas floor, so dust-sized swaps pay a zero or reduced hook fee (base-preserved, README-documented)"}],"hash":"31c3fe4d4221576d79bd48ed0c879481379d8d18245a295f27aea2f8a92aff90","nodeId":"e2caaa81-cdb6-459a-8764-4b0d5c9ba644","outcome":"completed","summary":"Review complete. The findings file is written and validated: all three snippet anchors match the tree, and the coverage record answers all 14 listed entry points plus three invariant rows. No source files were changed; only `.imd-findings.json` and a scratch test under `test/scratch/` were created.\n\n**Result for the assigned area (Math Precision, Boundary, Numerical Gap): no fund-affecting defect found.** The full suite of 63 tests passes on the real vendored PoolManager, and I confirmed the arithmetic independently:\n\n- **Quote identities hold exactly.** For exact-output sells, `R + floor(gross·r/W) == floor(R·W/(W−r))` for every R checked up to 2^127−1, so the real swap always reproduces the quoted native delta. For partial exact-input buys, the recomputed fee never exceeds the original, so the real swap reaches the same price limit and the `QuoteMismatch` guard cannot fire spuriously.\n- **Vault split is exact.** The two-term `_buybackShare` equals `floor(v·3000/10000)` for every input, so Slither's divide-before-multiply lead is a false positive. The sum of the two reserve views equals the ETH balance at all times, including forced ETH and inside the Safe's callback.\n- **Delta orientation** in all four exact modes was checked against v4's `Hooks.afterSwap`, and the decay values at 0/1800/3600 s are 50% / 26.75% / 3.5% as specified.\n\n**Reported findings, most severe first:**\n\n1. **Low, attestation.** `python3 script/attest.py --check` crashes at line 27 because it asserts a top-level `chainId` the manifest schema forbids, and two of the 23 recorded delivery hashes in `launch-attestation.json` are stale (launch.json and test/AdversarialFees.t.sol). The README still presents this script as the pre-deployment binding step.\n2. **Low, boundary × opening-price invariant.** While the initialized pool has zero liquidity, a 1-wei swap with a far price limit moves the pool price for free (reproduced: price went to 10× the opening sqrt price with a (0, 0) delta and no fee). Only matters if the factory seeds liquidity in a later transaction than initialize; base-preserved, so the remedy is a scope decision.\n3. **Info, rounding.** All fee divisions floor, so 1-wei buys at 50% and 27-wei exact-output sells pay zero hook fee. Bounded to one wei per swap, base-preserved and documented in the README.\n\nNone reach critical or high, so no proof test files were attached. Static-analysis leads I checked and dismissed: locked-ether on the hook (no protocol path funds it), reentrancy in `afterSwap` (vault receive makes no external calls), and the strict equality in `burn()` (intended zero-balance revert).","treeHash":null,"usage":{"cachedInputTokens":2186688,"inputTokens":546,"model":"claude-fable-5-1","outputTokens":49597,"runtime":"claude","turns":38,"wallClockMs":779119}},{"artifacts":[],"attempt":1,"bundleHash":"a7fff839b6f26cd41f6df25f74540f39b3a34342b85e23e420a726521d4d49c1","device":"e02faafd8034e613","findings":[],"hash":"3dfd05928b9667e3fab247bb1ead083d9d5afde8872b63c46955b6d2e370a996","nodeId":"4a6b659b-5d3d-466f-addb-a3ce2ba78e1c","outcome":"completed","summary":"Added adversarial fee, settlement rollback, token allowance, vault callback, and stateful accounting tests using the real v4 PoolManager.\n\nValidation passed:\n\n- `forge build`\n- `forge test`: **63 passed, 0 failed, 0 skipped**\n- Both invariant campaigns: **24,576 actions each**\n- Formatting checks\n\nOnly `test/**` changed. Coverage is documented in `test/README.md`. No implementation defect was reproduced.","treeHash":"5d75ab9982ba2f97754de45973077d86a75d95fd","usage":{"cachedInputTokens":2803584,"inputTokens":228947,"model":"gpt-6-astra","outputTokens":22323,"runtime":"codex","turns":7,"wallClockMs":847096}},{"artifacts":[],"attempt":1,"bundleHash":"502c443a37901c8324ab6cfb32900ba5ec51aa72c8c98fa2c3234e9a6e94fa21","device":"b3280f20e86b4e0d","findings":[],"hash":"4ebbcab2dcf7214951d756bfeb7ac19a17e8e88b8ea46230e9b49aa2c968ac88","nodeId":"0a2ad859-b559-4be8-a921-7b71d64e58a7","outcome":"completed","summary":"Implemented SOVRN.ONE/SVO, SovrnHook, and LifeForceVault with full fee routing, 70/30 reserves, Safe-only withdrawals, and permissionless burns. Removed legacy NFT, auction, and team-payment paths.\n\nValidation passed:\n\n- `forge build`\n- `forge test`: 45 passed, using a real local PoolManager\n- `forge fmt --check`\n- Launch attestation verification\n\nREADME, preparation script, attestation, and imported-finding regression notes are included. Protected configuration and dependencies remain unchanged.","treeHash":"622304d5af2523cc875554de2012e098c60d3ae1","usage":{"cachedInputTokens":3740160,"inputTokens":152120,"model":"gpt-6-astra","outputTokens":31687,"runtime":"codex","turns":9,"wallClockMs":1198326}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"f494611affb5524c","findings":[{"citation":"resolved","description":"Settlement of prior finding 9e26dd0b06046328385efb1af19d31d95c79adcde1d6c0c058ab9765e2fec3d2 (medium). The script defect is fixed: script/attest.py line 27 now asserts `set(manifest) == {\"kind\", \"hook\", \"token\", \"pool\", \"notes\"}`, build_record() runs to completion on the schema-valid manifest, and the four Python regressions in test/test_attestation.py pass. The author's statement that `--check` passed in their checkout is also correct: launch-attestation.json was regenerated in commit 4f03418, and the manifest and test/AdversarialFees.t.sol hashes it records (187d34ec..., 2f64e1b3...) are exactly the hashes of those files at that commit. What does not hold is the delivered tree. Two later commits changed delivery files without regenerating the record: 19c642a rewrote launch.json's notes (sha256 now accfc30f...), and the test contributor's commits 87658c5/981c822 (reapplied after 4f03418 in 7382be7) changed test/AdversarialFees.t.sol (now cca590b8...) and added six test files that have no deliverySha256 entry (test/FeeDifferential.t.sol, test/README.md, test/SettlementFailures.t.sol, test/TokenFailurePaths.t.sol, test/VaultCallbackBoundaries.t.sol, test/VaultModelInvariants.t.sol). So the README's step 4 (line 83, 'verify with `python3 script/attest.py --check`') and the sentence quoted above ('The attestation binds the manifest and every delivered test file by SHA-256') are both false at HEAD; the launch.json notes disclose that the record 'must be regenerated... by the attestation stage' but the README does not. All three creationBytecode fields, every sourceSha256 entry, the compiler settings and the ABIs in the record still match the current `forge build`, so the Solidity binding is intact and no funds are affected. Severity reduced from medium to low: the tooling now works and the fix is a single re-run of `python3 script/attest.py` after the manifest and test files settle (and keeping the README accurate about who regenerates it). The remaining advisory items from the prior round are settled without a finding: the hook receive()/stray-claim stranding (df83383c...) is reproduced by test/RevisionBoundaries.t.sol::test_managerRoutedETHAndUnsolicitedClaimsAreNotFeeDeposits and documented at README.md line 51 as I offered; the zero-liquidity price move (d3c40d82...) is reproduced by test_zeroLiquidityPriceMoveIsFree and documented at README.md line 87 and in the manifest notes; the REFUEL_SAFE trust assumption (94244a74...) is reproduced by test_safeCanWithdrawBothReservesWithoutBuyingOrBurning and documented at README.md lines 89-91; the hookless-pool bypass (1177879b...) is reproduced by test_hooklessPoolBypassesTheHookFee and documented at README.md line 32. The contracts in src/ are byte-identical to the tree I reviewed last round (last changed in 425e517), forge build, the 71-test forge suite and forge fmt --check all pass, and the manifest's five keys, patterns and 3627-character notes satisfy the LaunchManifest schema.","line":85,"path":"README.md","reproduction":"State: committed tree at HEAD (19c642a), forge and python3 installed, no network. Run `python3 -I script/attest.py --check`. Expected per README.md line 83: prints 'Launch manifest and attestation match the current build.' and exits 0. Actual: build_record() now completes (the old line-27 AssertionError is gone) but main() raises `AssertionError: launch attestation differs; regenerate after reviewing changes` at script/attest.py line 102, exit 1. Diffing the regenerated record against the committed file: deliverySha256['launch.json'] recorded 187d34ecf9db2c42... (launch-attestation.json line 55) vs actual accfc30ff6be7df6...; deliverySha256['test/AdversarialFees.t.sol'] recorded 2f64e1b320112b9e... (line 63) vs actual cca590b89cf79ea1...; and test/FeeDifferential.t.sol, test/README.md, test/SettlementFailures.t.sol, test/TokenFailurePaths.t.sol, test/VaultCallbackBoundaries.t.sol, test/VaultModelInvariants.t.sol are present in the tree with no recorded entry. `git show 4f03418:launch.json | sha256sum` gives 187d34ec... and `git show 4f03418:test/AdversarialFees.t.sol | sha256sum` gives 2f64e1b3..., confirming the record was correct when generated and went stale through commits 19c642a, 87658c5 and 981c822. Every other field of the regenerated record (chainId, compiler, settings, hookFlags, sourceSha256, contracts incl. creationBytecode) equals the committed one. Expected after fix: `python3 script/attest.py` then `python3 -I script/attest.py --check` exits 0 with the match message.","severity":"low","snippet":"The manifest has exactly five top-level keys: `kind`, `hook`, `token`, `pool` and `notes`. The rehearsal chain id remains in its notes and in the attestation's `chainId` field; deployment tooling must enforce it. The attestation binds the manifest and every delivered test file by SHA-256, excluding disposable `test/scratch/` files.","title":"Committed launch-attestation.json is stale again at HEAD: `python3 script/attest.py --check` fails, so the README's documented verification step does not pass on the delivered tree"}],"hash":"5b49911bcb71bd9d86573a4c36c90d84679d3a907b13d0cfa1aa959d148846cb","nodeId":"7680b4b2-a309-4ebd-9864-58bede70884f","outcome":"completed","summary":"The review is complete and the findings file is written. The tree is clean apart from the untracked findings file the task asks for.\n\n**Outcome.** One finding remains open, downgraded to low. The four advisory items from last round are settled with no finding.\n\n**The prior medium (attestation) is partially fixed.** The script defect is gone: line 27 of the attest script now asserts the five schema keys, build_record runs on the schema-valid manifest, and the four Python regressions pass. The author was also right that the check passed in their checkout. I verified that the recorded hashes equal the manifest and AdversarialFees test exactly as they stood at the regenerating commit. But the delivered tree at HEAD still fails the documented check, now with \"launch attestation differs\" instead of a crash. Two later commits changed delivery files without regenerating the record: the manifest-only commit rewrote the notes, and the test contributor's commits changed one test file and added six unrecorded ones. All creation bytecodes, source hashes, settings and ABIs in the record still match the current build. The fix is a single re-run of the attest script once the manifest and tests settle, and keeping the README accurate about who regenerates it.\n\n**Settled advisories.** Each is reproduced by the new RevisionBoundaries test suite on the real PoolManager and disclosed in the README where I suggested:\n\n- Hook receive and stray claims stranding, README line 51. Documented alternative accepted.\n- Zero-liquidity price move, README line 87 and the manifest notes. Agreed it is inherited v4 behaviour.\n- REFUEL_SAFE sole-recipient trust assumption, README lines 89 to 91.\n- Hookless pool bypassing the fee, README line 32.\n\n**Nothing new.** The contracts in src are byte-identical to the tree reviewed last round. Build, the 71-test Foundry suite, fmt check and the Python unit tests all pass, and the manifest satisfies the LaunchManifest schema including the 4000-character notes cap. The coverage record answers all 14 entry points as holds, plus rows for the settled items and the manifest.","treeHash":null,"usage":{"cachedInputTokens":893926,"inputTokens":260,"model":"claude-fable-5-1","outputTokens":15369,"runtime":"claude","turns":22,"wallClockMs":499682}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"fea57d3e9d0ca7bf","findings":[{"citation":"resolved","description":"OGHook bakes the mainnet SPEPE ERC-721 address into COLLECTION and hands it to OGDistributor/OGAuction. The constructor (line 57) checks that the manager and token have code but never checks COLLECTION. On Sepolia (chain 11155111, the chain the requester names) `cast code 0x999ce0CE8C5f7661e0c74a568FfE27CEB9177bDB` returns 0x (verified 2026-10-08 via ethereum-sepolia-rpc.publicnode.com; on mainnet the same address returns 33985 bytes and symbol() = SPEPE). Every swap still routes `normal + surplus` (2.5% of gross ETH plus the whole launch-decay surplus, i.e. up to 49% of gross in the first hour) into OGDistributor via receiveFees/receiveFeeClaims. All of the distributor's ETH exits (activate -> exit, activateWithETH -> exit) begin with `collection.ownerOf(id)`, a high-level call to a codeless address, which reverts unconditionally (Solidity extcodesize check). totalWeight can never become non-zero, so the ETH accrues in `backlog` with streamEnd = 0 and no function can ever release it: the distributor has no owner, sweep, receive-and-forward or fallback path. Impact: permanent loss of the majority of hook fee revenue if the base were deployed as-is on the stated chain. The same chain-binding applies to OGAuction (holds the collection) and to Interfaces.sol ISpepe. For the launch: remove OGDistributor, OGAuction, ISpepe/IOGPool and the COLLECTION constant entirely and route 100% of the fee to the vault, as the brief requires; do not keep any hardcoded per-chain address other than REFUEL_SAFE. The attached proof must have its imports re-pointed to the renamed hook once that is done; the property it holds the fix to is that no hook fee ETH reaches a recipient that cannot release it on the launch chain.","line":21,"path":"src/OGHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\nimport {SwapParams, ModifyLiquidityParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {BalanceDelta} from \"v4-core/src/types/BalanceDelta.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {OG} from \"src/OG.sol\";\nimport {OGHook} from \"src/OGHook.sol\";\nimport {OGDistributor} from \"src/OGDistributor.sol\";\n\n/// @dev Minimal router: one unlock, one swap or liquidity change, settle both currencies.\ncontract ProofRouter {\n    IPoolManager public immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function trade(PoolKey memory key, SwapParams memory params) external payable returns (BalanceDelta d) {\n        d = abi.decode(manager.unlock(abi.encode(msg.sender, key, true, abi.encode(params))), (BalanceDelta));\n        _refund();\n    }\n\n    function liquidity(PoolKey memory key, ModifyLiquidityParams memory params) external payable returns (BalanceDelta d) {\n        d = abi.decode(manager.unlock(abi.encode(msg.sender, key, false, abi.encode(params))), (BalanceDelta));\n        _refund();\n    }\n\n    function _refund() private {\n        if (address(this).balance > 0) {\n            (bool ok,) = msg.sender.call{value: address(this).balance}(\"\");\n            require(ok);\n        }\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        require(msg.sender == address(manager));\n        (address payer, PoolKey memory key, bool swap, bytes memory params) =\n            abi.decode(data, (address, PoolKey, bool, bytes));\n        BalanceDelta d;\n        if (swap) d = manager.swap(key, abi.decode(params, (SwapParams)), \"\");\n        else (d,) = manager.modifyLiquidity(key, abi.decode(params, (ModifyLiquidityParams)), \"\");\n        _settle(key.currency0, payer, d.amount0());\n        _settle(key.currency1, payer, d.amount1());\n        return abi.encode(d);\n    }\n\n    function _settle(Currency c, address payer, int128 amount) private {\n        if (amount < 0) {\n            uint256 debt = uint256(-int256(amount));\n            manager.sync(c);\n            if (Currency.unwrap(c) == address(0)) {\n                manager.settle{value: debt}();\n            } else {\n                require(OG(Currency.unwrap(c)).transferFrom(payer, address(manager), debt));\n                manager.settle();\n            }\n        } else if (amount > 0) {\n            manager.take(c, payer, uint256(uint128(amount)));\n        }\n    }\n\n    receive() external payable {}\n}\n\n/// @notice On chain 11155111 (the requester's launch chain) the address baked into\n///         OGHook.COLLECTION (0x999ce0CE8C5f7661e0c74a568FfE27CEB9177bDB) has no code\n///         (`cast code` returns 0x on Sepolia; on mainnet it is the SPEPE ERC-721).\n///         Nothing in this fixture deploys code there, so it reproduces the launch chain.\n///         Every reward-bearing swap then pushes 2.5% of gross ETH (plus the launch surplus)\n///         into OGDistributor, whose only ETH exits (activate -> exit) start with\n///         `collection.ownerOf(id)`, a high-level call to a codeless address that always reverts.\n///         The ETH is unrecoverable: no owner, sweep or fallback path exists.\ncontract StrandedRewardsOnLaunchChainTest is Test {\n    uint160 internal constant LAUNCH_PRICE = 792281625142643375935439503360000;\n\n    PoolManager manager;\n    OG token;\n    OGHook hook;\n    OGDistributor dist;\n    ProofRouter router;\n    PoolKey key;\n\n    receive() external payable {}\n\n    function setUp() public {\n        vm.chainId(11155111);\n        vm.deal(address(this), 1000 ether);\n        manager = new PoolManager(address(this));\n        vm.deal(address(manager), 0);\n        token = new OG();\n        address at = address(uint160(0x20cc));\n        deployCodeTo(\"OGHook.sol:OGHook\", abi.encode(IPoolManager(address(manager)), token, address(this)), at);\n        hook = OGHook(payable(at));\n        dist = hook.distributor();\n        key = PoolKey(Currency.wrap(address(0)), Currency.wrap(address(token)), 12500, 60, IHooks(at));\n        manager.initialize(key, LAUNCH_PRICE);\n        router = new ProofRouter(manager);\n        token.approve(address(router), type(uint256).max);\n        token.approve(address(dist), type(uint256).max);\n        router.liquidity{value: 100 ether}(key, ModifyLiquidityParams(-887220, 887220, 1e21, bytes32(0)));\n    }\n\n    function test_rewardETHIsStrandedWhenCollectionHasNoCodeOnLaunchChain() public {\n        // Mirrors the launch chain: the hardcoded collection has no code.\n        assertEq(hook.COLLECTION().code.length, 0, \"fixture: COLLECTION must be codeless, as on chain 11155111\");\n\n        vm.warp(hook.openedAt() + 1 hours);\n        uint256 distBefore = address(dist).balance;\n        BalanceDelta d = router.trade{value: 1 ether}(key, SwapParams(true, -1 ether, TickMath.MIN_SQRT_PRICE + 1));\n        assertEq(d.amount0(), -1 ether);\n        uint256 rewardETH = address(dist).balance - distBefore;\n        // 3.5% hook fee: 1% team, 2.5% rewards -> 0.025 ETH lands in the distributor.\n        assertEq(rewardETH, 0.025 ether, \"2.5% of gross ETH routed to OGDistributor\");\n\n        // Every ETH exit of the distributor begins with collection.ownerOf(id): a high-level\n        // call to an address without code reverts before anything else runs.\n        vm.expectRevert();\n        dist.activate(1, 1);\n        vm.expectRevert();\n        dist.activateWithETH{value: 0.01 ether}(1, 1, LAUNCH_PRICE / 2, block.timestamp);\n        vm.expectRevert();\n        dist.exit(1);\n\n        // No weight can ever exist, so the ETH sits in `backlog` forever with no release path.\n        assertEq(dist.totalWeight(), 0);\n        assertEq(dist.backlogLeft(), rewardETH);\n\n        // The defect: hook fee ETH must not be routed to a recipient that cannot release it on\n        // the launch chain. Fails on the code as it is; passes once the hook stops sending\n        // fee ETH into the NFT distributor (the launch routes 100% of the fee to the vault).\n        assertEq(address(dist).balance, 0, \"fee ETH stranded in OGDistributor on chain 11155111\");\n    }\n}","reproduction":"State: fresh PoolManager, OG, OGHook deployed at a 0x20cc-flag address with factory = test; no code at 0x999ce0CE8C5f7661e0c74a568FfE27CEB9177bDB (as on chain 11155111); pool ETH/OG initialised at 792281625142643375935439503360000 and seeded with 1e21 liquidity over the full range; warp openedAt + 1h. Call: router buy, SwapParams(true, -1 ether, MIN_SQRT_PRICE+1). Actual: address(distributor).balance rises by 0.025 ether (2.5% of gross), dist.backlogLeft() == 0.025 ether, dist.totalWeight() == 0; dist.activate(1,1), dist.activateWithETH{value:0.01 ether}(1,1,price/2,now) and dist.exit(1) all revert (call to codeless COLLECTION). Expected for the launch: 0 ETH to the distributor, 100% of the fee to a recipient that can release it (the vault). Run: forge test --match-path test/scratch/StrandedRewardsOnLaunchChain.t.sol -> FAIL 'fee ETH stranded in OGDistributor on chain 11155111: 25000000000000000 != 0'.","severity":"high","snippet":"    address public constant COLLECTION = 0x999ce0CE8C5f7661e0c74a568FfE27CEB9177bDB;","title":"Hardcoded mainnet SPEPE collection has no code on launch chain 11155111; every swap strands 2.5%+ of gross ETH in OGDistributor"},{"citation":"resolved","description":"afterSwap splits every hook fee as `team = gross / 100` (line 209) and pays it to the immutable TEAM address through _payTeam (line 250), retaining `teamCredit` and exposing permissionless `payTeam()` when the send fails; redeemFees/unlockCallback also carry a `claimTeam` counter. The commissioned launch states that the ENTIRE hook fee goes to LifeForceVault and that teamCredit/payTeam are to be removed, so in the adapted contracts this is a fee paid to an unauthorised party. The address is also an EOA on both chains (no code), so nothing on-chain binds it to the project. Adapter action: delete TEAM, teamCredit, claimTeam, payTeam, _payTeam and the TeamPaid event; collapse the three claim counters into one; send `fee` whole to the vault (direct when manager balance >= fee, else mint one ERC-6909 claim redeemed by permissionless redeemFees to the vault).","line":20,"path":"src/OGHook.sol","reproduction":"State: funded pool (test/Hook.t.sol fixture _system(true,true)), at openedAt. Call: buy with SwapParams(true, -1 ether, MIN). Actual: TEAM.balance increases by exactly 0.01 ether and distributor receives 0.025 + 0.465 ether (test/Hook.t.sol test_decayAndSurplus lines 80-84 assert this today). Expected for the launch: 0 to TEAM, 0.5 ether (100% of the fee) to the vault, split 0.35/0.15 ETH into inferenceReserve/buybackReserve.","severity":"medium","snippet":"    address public constant TEAM = 0x90738ABe9b04622Dc0b3d015a3964Cc7D1Fd1859;","title":"1% of gross ETH on every swap is paid to a hardcoded TEAM EOA; the launch allows no fee recipient other than the vault"},{"citation":"resolved","description":"The token contract is named OG and returns name() = \"OG\", symbol() = \"OG\" (lines 6-7); launch.json token.contract/name/symbol are \"OG\" as well. The commissioned launch requires contract SovrnToken with name exactly the 9 characters \"SOVRN.ONE\" (one full stop) and symbol exactly \"SVO\", with launch.json token.name/token.symbol matching. Everything else in the token (no constructor args, 1e27 fixed supply to msg.sender, 18 decimals, DEAD constant, totalBurned, no mint/owner/pause/blacklist) already satisfies the brief and the protected token floor, and should be kept byte-for-byte apart from the rename. Note README.md line 10 says \"rename the token to SOVRN AI\", which disagrees with the brief's \"SOVRN.ONE\"; the brief wins.","line":6,"path":"src/OG.sol","reproduction":"Call: `new OG()` then `name()` / `symbol()`. Actual: \"OG\" / \"OG\" (asserted today by test/Token.t.sol lines 9-10). Expected: \"SOVRN.ONE\" / \"SVO\"; bytes(name()).length == 9 with byte index 5 == 0x2e.","severity":"medium","snippet":"    string public constant name = \"OG\";","title":"Token identity is OG/OG; launch requires name() == \"SOVRN.ONE\" and symbol() == \"SVO\""},{"citation":"resolved","description":"OGDistributor accrues hook fee ETH to NFT weights (activate/upgrade with OG burns, 24h lock, exit pays `pending(id)` at line 212 and hands the NFT to OGAuction for a perpetual Dutch auction that burns OG). activateWithETH additionally performs an exact-output swap on the launch pool from inside the distributor (line 170) and takes OG to DEAD. None of this is wrong in the base's own terms (the existing invariant suite holds), but the commissioned launch states 'Holders get no payouts', forbids yield/returns wording, and requires deleting OGDistributor, OGAuction, the SPEPE interfaces/constants, and every NFT/reward/auction path, with burning reduced to LifeForceVault.burn() sending the vault's whole SVO balance to DEAD. Deleting it also removes the only external calls the hook makes during afterSwap besides the fee send (receiveFees/receiveFeeClaims/fundFeeClaims), shrinking the hook's initcode (currently ~49 KB because the hook embeds distributor+auction creation code) and its reentrancy surface. Keep: the hook's quote mechanism, four-mode fee maths, claim fallback and redeemFees shape, which the brief declares the contract of record.","line":212,"path":"src/OGDistributor.sol","reproduction":"State: test/Hook.t.sol fixture, NFT id 1 minted to ALICE at COLLECTION, ALICE activates level 1 (burns 50,000 OG). Call: buy SwapParams(true, -1 ether, MIN) at openedAt, warp +24h, ALICE calls dist.exit(1). Actual: ALICE receives 0.025 ether of fee ETH and NFT 1 moves to OGAuction (test/Hook.t.sol test_decayAndSurplus and test/LaunchPolicy.t.sol test_policyPriceTokensOnlyLaunchClaimsAndExit show the payout). Expected for the launch: no address other than REFUEL_SAFE can ever receive fee ETH; dist/auction do not exist.","severity":"medium","snippet":"        _sendETH(msg.sender, amount);","title":"NFT reward/auction path pays ETH to SPEPE holders and swaps from inside the distributor; the launch forbids holder payouts and requires this path removed"},{"citation":"resolved","description":"hook.contract is OGHook, token.contract/name/symbol are OG, and notes describe Ethereum mainnet launch policy 34, the SPEPE collection, the team address, NFT levels and auctions. The schema-valid parts to keep: kind univ4_hook, constructorArgs [\"$poolManager\",\"$token\",\"$factory\"] (matches the hook constructor order and must stay flat), permissions [beforeInitialize, beforeSwap, afterSwap, beforeSwapReturnDelta, afterSwapReturnDelta] (= flags 8396), pool {pairedCurrency 0x0, fee 12500, tickSpacing 60, initialPrice 792281625142643375935439503360000}. The adapter must set hook.contract SovrnHook, token {SovrnToken, \"SOVRN.ONE\", \"SVO\", 18}, must not add the vault (it is constructor-deployed, discoverable via hook.vault()), and must rewrite notes without yield/returns/profit wording or an audit claim.","line":4,"path":"launch.json","reproduction":"Read launch.json lines 4 and 17-19: contract \"OGHook\", token contract/name/symbol \"OG\". Expected: \"SovrnHook\"; \"SovrnToken\"/\"SOVRN.ONE\"/\"SVO\". Deployer resolution of the current manifest would compile a contract named OGHook that will not exist after the rename.","severity":"low","snippet":"    \"contract\": \"OGHook\",","title":"launch.json still describes the OG mainnet launch (OGHook/OG, policy 34, SPEPE, team); must be regenerated for SovrnHook/SovrnToken without the vault"},{"citation":"resolved","description":"initCode() packs type(OGHook).creationCode with (manager, token, factory) and mine() matches HookFlags.OG_FLAGS (line 29). The script is correct for the base, but after the rename the CREATE2 initcode hash changes (SovrnHook's creation code now embeds LifeForceVault's creation code instead of OGDistributor+OGAuction), so every previously mined salt is invalid. The adapter must point initCode at SovrnHook/SovrnToken and rename OG_FLAGS; the flag value itself (0x20cc = 8396) is unchanged because permissions are unchanged. Keep: no env vars, no broadcast, pure functions, the predict() formula.","line":13,"path":"script/PrepareLaunch.s.sol","reproduction":"Call PrepareLaunch.initCode(manager, token, factory) on the base and on the adapted tree: keccak256 differs, so a salt found by mine() against the base hash yields an address whose low 14 bits do not equal 8396 for the new initcode, and Hooks.validateHookPermissions in the hook constructor reverts (HookAddressNotValid). test/Launch.t.sol test_realCreate2DeploymentInitializesAllContracts shows the mine/deploy round-trip that must keep passing after the rename.","severity":"low","snippet":"        return abi.encodePacked(type(OGHook).creationCode, abi.encode(manager, token, factory));","title":"PrepareLaunch mines salts for OGHook initcode and OG_FLAGS; must track the renamed hook's initcode (vault embedded) while keeping flags 8396"},{"citation":"resolved","description":"The README credits the base (launch 1040, commit c1d98b5) and states no audit is claimed, which satisfies two requirements. It names the token \"SOVRN AI\" rather than \"SOVRN.ONE\"/\"SVO\", and it does not list: the 3.5% fee, 50%->3.5% decay over 3,600 s, 70/30 inference/buyback split, REFUEL_SAFE 0xb1eC9d1C36974d05eb9889eBf8A150b05791E559 as the only privileged caller (withdrawInference/withdrawBuyback), permissionless redeemFees/burn, flags 8396, pool fee 12500/tickSpacing 60, opening price, chain id 11155111, the bytecode_hash \"none\" quote, or what happens if the vault rejects ETH (direct take reverts the swap; claims do not). No yield/returns/profit wording is present, which must stay so.","line":10,"path":"README.md","reproduction":"grep -c 'SOVRN.ONE\\|SVO\\|REFUEL_SAFE\\|7000\\|3000\\|8396\\|12500' README.md returns 0 on the current tree; the brief requires each of these to appear.","severity":"info","snippet":"send the hook fee to an immutable `LifeForceVault` (created by the hook constructor), rename the token to SOVRN AI. No audit is claimed.","title":"README does not yet carry the launch's numbers, privileges, change list or correct token name"}],"hash":"6216001836059269cb140661d5b9a9d8c89ae34784daeb7ad791f2a0099131dc","nodeId":"3a397f93-e0d7-4e09-bb01-18e17ca7ea9a","outcome":"completed","summary":"Review complete. The findings file is at `.imd-findings.json` in the repository root, with seven entries and one proof test.\n\n**What I read in full:** all six production sources (`OGHook`, `OG`, `OGDistributor`, `OGAuction`, `Interfaces`, `HookFlags`), `script/PrepareLaunch.s.sol`, `launch.json`, `README.md`, the pinned protected floor tests, and the base test fixtures and fee/launch/regression suites. I traced the hook against the vendored v4-core swap path and `Hooks` library, confirming that the nested quote swap skips the hook's own callbacks, that the exact-output sell rounding never trips `QuoteMismatch`, and that the fee and claim deltas net to zero in all four exact modes. The full offline suite passes (62 tests, 2 mainnet fork tests skipped).\n\n**Not reached:** the two mainnet fork suites were not run against an RPC, the live SPEPE contract was only checked for existence and symbol, and solmate's `SignedWadMath` was treated as a trusted library. The distributor and auction invariant suites were run but not re-derived by hand beyond the accounting paths listed above.\n\n**Hook fee logic is sound as the contract of record.** I found no defect in the fee maths, quote isolation, busy guard, claim fallback or pool binding that would carry into the adapted hook. The findings are about what the base carries that the launch cannot:\n\n- **High.** The hardcoded SPEPE collection address has no code on chain 11155111. I verified this live against Sepolia and mainnet. Every swap would push the reward share of the fee into the distributor, where every exit path reverts on a call to a codeless address, stranding the ETH permanently. Proof at `test/scratch/StrandedRewardsOnLaunchChain.t.sol` fails on the current code for exactly this reason.\n- **Medium.** One percent of gross goes to a hardcoded TEAM address, and the NFT reward and auction path pays ETH to holders. Both conflict with the vault-only, no-payout design and must be deleted.\n- **Medium.** Token name and symbol are OG/OG rather than SOVRN.ONE/SVO.\n- **Low.** `launch.json` and the prepare script still name the OG contracts and the mainnet policy; the script's mined salts become invalid once the hook's initcode embeds the vault, though the flag value 8396 is unchanged.\n- **Info.** README lacks the required numbers and privileges and uses the name SOVRN AI.\n\nEach finding states the adapter action, and the medium items point to the existing base tests that already demonstrate the behaviour to be removed.","treeHash":null,"usage":{"cachedInputTokens":1439141,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":48475,"runtime":"claude","turns":41,"wallClockMs":1179543}},{"artifacts":[],"attempt":1,"bundleHash":"3c0fc1a92139cd472eafab6205d7bd2a2f4a04704e7821813f257819e0b597dc","device":"cb213fe03251a512","findings":[],"hash":"751021e489107c09df96fffebc0f1b2d024550378a7367f3adaa8f10913bd4d5","nodeId":"7883bb1b-2da3-4cdb-9c1f-0f88985a7ba4","outcome":"completed","summary":"Updated only `launch.json`: removed unsupported `chainId`, preserved matching contract and pool fields, and documented imported findings.\n\nValidation passed: schema, constructor ABI, permissions, `forge build`, and all 63 Foundry tests.\n\nThe separate attestation check fails because its script requires `chainId`. The needed script correction and attestation regeneration are outside this task’s scope and documented in notes.","treeHash":"40baaf63a1898b99db18b9ae6a933a887ad51c47","usage":{"cachedInputTokens":1896448,"inputTokens":87667,"model":"gpt-6-astra","outputTokens":8580,"runtime":"codex","turns":6,"wallClockMs":334973}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"5231314e8cca884c","findings":[],"hash":"a3b845f3038b555b3a42e4335cff57bb15aaacf491e731ced2fc109cad475bfc","nodeId":"7883bb1b-2da3-4cdb-9c1f-0f88985a7ba4","outcome":"failed","summary":"could not restore accepted work: accepted work conflicts with a dependency, which plan validation should have prevented: ","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"codex","turns":0,"wallClockMs":2828}},{"artifacts":[],"attempt":1,"bundleHash":"a5892807b623efcd5fa5ebf37b5becc950e5ad232e15da5d2d07b4a82f043644","device":"f4b1cf15e799d705","findings":[],"hash":"c42b46c9d76aec21a0e6480c1f83e59c402e89da12af50391622e1bac182fe14","nodeId":"7883bb1b-2da3-4cdb-9c1f-0f88985a7ba4","outcome":"completed","summary":"Updated only [launch.json](/home/imd-worker/.identitymd/work/31731677-968e-46e0-82b2-1e6a64463c3b/7883bb1b-2da3-4cdb-9c1f-0f88985a7ba4/launch.json), documenting implementation details and all seven historical finding dispositions.\n\nSchema, constructor and permission checks passed. `forge build` passed with warnings; all 71 tests passed.\n\nNotes flag required attestation regeneration, including a pre-existing test-file hash mismatch.","treeHash":"5dfbd44bb3842930dd9d20145a408b3348d1b441","usage":{"cachedInputTokens":1192576,"inputTokens":109139,"model":"gpt-6-astra","outputTokens":7835,"runtime":"codex","turns":5,"wallClockMs":195192}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"87804e27e9c9f85a","findings":[{"citation":"resolved","description":"The deliverable set includes 'the launch attestation' and README.md line 79 instructs operators to 'regenerate with python3 script/attest.py, or verify with python3 script/attest.py --check'. The script requires a top-level chainId key in launch.json, but the delivered launch.json (correctly, since the LaunchManifest schema has additionalProperties:false and no chainId field) has no such key, so both the --check and the regenerate paths abort with AssertionError before any hashing. The committed launch-attestation.json is therefore unverifiable by the shipped tool and already stale: its deliverySha256 entries for launch.json and test/AdversarialFees.t.sol do not match the tree (all three creation bytecodes and all sourceSha256 entries do still match). The launch.json notes field acknowledges this as an 'integration follow-up', but the README still presents the command as the verification step. No funds are at risk; the defect is that the attestation deliverable cannot be reproduced or checked as documented. Minimal fix: drop chainId from the manifest key-set assertion (read the chain id from the script's own constant or a separate config as the notes suggest), then regenerate launch-attestation.json so the recorded delivery hashes match the tree.","line":27,"path":"script/attest.py","reproduction":"State: the committed tree. Run `python3 script/attest.py --check` (or `python3 script/attest.py`). Expected per README.md:79: 'Launch manifest and attestation match the current build.' Actual: Traceback ending in `assert set(manifest) == {\"kind\", \"chainId\", \"hook\", \"token\", \"pool\", \"notes\"}` -> AssertionError at script/attest.py:27, because `python3 -c \"import json;print(sorted(json.load(open('launch.json'))))\"` prints ['hook','kind','notes','pool','token'] (no chainId). Independently: `python3 -c \"import json,hashlib;a=json.load(open('launch-attestation.json'));print([p for p,h in a['deliverySha256'].items() if hashlib.sha256(open(p,'rb').read()).hexdigest()!=h])\"` prints ['launch.json', 'test/AdversarialFees.t.sol'], so the recorded attestation no longer describes the delivered files.","severity":"low","snippet":"    assert set(manifest) == {\"kind\", \"chainId\", \"hook\", \"token\", \"pool\", \"notes\"}","title":"Shipped launch attestation is stale and its check/regenerate script cannot run against the delivered manifest"},{"citation":"resolved","description":"Economic-security note on the revenue assumption, not a code bug: the brief mandates a plain ERC-20 with no transfer restrictions, so nothing stops anyone from initializing a second ETH/SVO pool on the same PoolManager (or any other venue) with hooks = address(0). Trades there pay the LP fee only; the vault receives nothing and the 50%->3.5% launch-window buy fee does not apply. Once a hookless pool has liquidity, routers and aggregators will prefer it for most of each trade, so the fee base funding inference is the hooked pool's share of volume rather than 'SVO trading fees' in general, and the anti-snipe launch fee can be sidestepped by anyone who obtains SVO (e.g. a first-hour buyer who pays the fee once) and seeds a hookless pool. This is inherent to the requested design and cannot be fixed without changing the token (out of scope). The README's launch-parameters and fee sections state every number and privilege but never say that the fee is per-pool and bypassable, which the Safe operators and holders need in order to size revenue expectations. Suggested fix: one sentence in README 'Fees and settlement' stating that the hook fee applies only to the pool keyed by poolKey() and that hookless SVO/ETH pools trade without it.","line":3,"path":"README.md","reproduction":"Reproduced in test/scratch/HooklessBypass.t.sol on the real vendored PoolManager. State: hook at 0x...20cc, hooked pool (ETH/SVO, fee 12500, spacing 60) initialized at sqrtPriceX96 792281625142643375935439503360000 and seeded with SVO only in ticks [166200,184200], block.timestamp == openedAt (launchFeeNow() == 0.5e18). Calls by an unprivileged account: (1) manager.initialize(PoolKey(ETH, SVO, 3000, 60, IHooks(address(0))), 792281625142643375935439503360000); (2) modifyLiquidity on that key with 10 ETH + SVO; (3) swap(hookless key, SwapParams(true, -1 ether, MIN_SQRT_PRICE+1)). Expected if 'SVO trading fees fund inference' held for all trading: vault balance + hook.claimFees() rises by 0.5 ether. Actual: vault balance and claimFees unchanged (0), swapper delta amount0 == -1 ether, i.e. the full ETH went to the AMM. The same swap on the hooked key in the same block pays exactly gross/2 to the vault. A sell of 1,000,000 SVO on the hookless key likewise moves nothing to the vault.","severity":"info","snippet":"SVO trading fees fund the inference of a voice AI. All hook fees, collected only in native ETH, go to one immutable LifeForceVault: 70% is earmarked for inference and 30% for manually buying SVO to burn. Holders receive no payouts. The vault only accounts for ETH; the Safe performs purchases outside the vault.","title":"Hook fee is collectable only on the single hooked pool; any hookless SVO/ETH pool trades with zero fee to the vault and the README does not disclose this"}],"hash":"cb66dce37eabde249cb46aff9f27b8f3b9b0389d99e3627c351e3d505defbbd3","nodeId":"b507be72-872e-448e-8ef6-25ee9dfdcfdb","outcome":"completed","summary":"Review complete. The findings file is written at `.imd-findings.json` with 2 findings and 22 coverage rows, and `git status` is clean apart from the ignored scratch directory and the findings file itself.\n\n**Outcome: no contract-level economic defect found.** Across the Economic Security, Invariant and Flow Gap checklists, every entry point and every conservation law I traced holds. The two items reported are a deliverable defect and a design disclosure gap.\n\n**Findings**\n\n- **Low, attestation deliverable.** `script/attest.py` asserts that `launch.json` contains a `chainId` key, which the manifest schema forbids. Both the verify and regenerate paths abort with an AssertionError. The committed attestation also records stale hashes for two delivery files, though all three creation bytecodes still match the current build.\n- **Info, fee bypass disclosure.** The hook fee applies only to the one hooked pool. Anyone can open a hookless SVO/ETH pool on the same PoolManager and trade with zero vault fee, including during the 50% launch window. This is inherent to the plain-token design the brief mandates, so the only fix is a README sentence. I reproduced it on the real vendored PoolManager.\n\n**What I verified in depth**\n\n- Fee arithmetic is byte-identical to the imported base except the destination block (git diff against the base commit).\n- Sell-exact-ETH-out cannot hit the QuoteMismatch revert: I proved algebraically that `requested + fee == quoted gross` always, and confirmed numerically across the full small range and random large values. Partial fills are also consistent.\n- The 70/30 split is an exact floor to uint256 max. Slither's divide-before-multiply lead is a false positive.\n- Vault ETH balance equals the sum of reserves at every boundary, including forced ETH and mid-callback reentry by the Safe.\n- A first buy on a shared manager that already holds other pools' ETH pays directly and settlement restores the manager's balance.\n- The pinned protected hook and token suites pass 11 of 11 with the verifier's environment variables emulated.\n- Slither and Aderyn leads on locked ether and reentrancy reduce to self-harm or guarded paths, so none became findings.\n\n**Not reached:** no live Sepolia check of whether the hardcoded REFUEL_SAFE exists and accepts ETH there. It is recorded as an operator trust assumption in the coverage record rather than a finding.","treeHash":null,"usage":{"cachedInputTokens":2747006,"inputTokens":546,"model":"claude-fable-5-1","outputTokens":52438,"runtime":"claude","turns":52,"wallClockMs":1240004}},{"artifacts":[],"attempt":1,"bundleHash":"eaca92e50be7049e99cdbcaa02ebf3855170429f9017192d01b604d5c75bbefc","device":"d0a17ab6722865f6","findings":[],"hash":"d81c1ac6e5bd1956bb25bd9e4c2b8381258e632425a69987d1482fe2d457c849","nodeId":"0a2ad859-b559-4be8-a921-7b71d64e58a7","outcome":"completed","summary":"Fixed the attestation schema mismatch, regenerated the record, added regression tests, and documented the advisory limits. Production contracts remain unchanged.\n\nAll 12 findings are answered in `.imd-responses.json`.\n\nPassed: `forge build`, 50 Foundry tests, `forge fmt --check`, four Python regressions, and attestation `--check`.","treeHash":"6dd7809085e581128776fba473dacecb436d6a17","usage":{"cachedInputTokens":1507968,"inputTokens":110142,"model":"gpt-6-astra","outputTokens":13973,"runtime":"codex","turns":5,"wallClockMs":479726}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"f9a12c57a094ca8d","findings":[{"citation":"resolved","description":"Merged from all four specialists (audit_math, audit_permissions, audit_flow, audit_economics), who reported the same root cause. The brief lists 'the launch attestation' as a deliverable and README step 4 (line 79) tells operators to regenerate it with `python3 script/attest.py` and verify it with `python3 script/attest.py --check` before deployment. The script asserts a top-level `chainId` key in launch.json, but the delivered manifest correctly carries only the five LaunchManifest keys (the schema has additionalProperties:false and no chainId), so build_record() raises AssertionError before hashing anything, in both modes. Independently, the committed launch-attestation.json no longer describes the tree: deliverySha256 for launch.json and test/AdversarialFees.t.sol differ from the files, and six delivered test files (test/FeeDifferential.t.sol, test/README.md, test/SettlementFailures.t.sol, test/TokenFailurePaths.t.sol, test/VaultCallbackBoundaries.t.sol, test/VaultModelInvariants.t.sol) have no entry. The three creation bytecodes and every sourceSha256 entry still match the current forge build, so the Solidity side is unaffected; the defect is that the attestation deliverable cannot be reproduced or checked as the README promises and the manifest binding it advertises does not hold. The manifest notes disclose the gap but the README does not. Severity is medium because a documented pre-deployment guarantee is broken, not because funds are at risk. Minimal fix: drop chainId from the asserted key set (keep the 11155111 constant the script already emits on line 80, and assert set(manifest) == {kind, hook, token, pool, notes}), regenerate launch-attestation.json, re-run --check, and keep the README text accurate.","line":27,"path":"script/attest.py","reproduction":"State: committed tree at HEAD, forge and python3 installed. Run `python3 -I script/attest.py --check`. Expected per README.md line 79: prints 'Launch manifest and attestation match the current build.' Actual: traceback ending `File script/attest.py, line 27, in build_record: assert set(manifest) == {\"kind\", \"chainId\", \"hook\", \"token\", \"pool\", \"notes\"}` -> AssertionError; `python3 -I -c \"import json;print(sorted(json.load(open('launch.json'))))\"` prints ['hook','kind','notes','pool','token']. Running without --check fails identically, so the record cannot be regenerated either. Then compare hashes: `python3 -I -c \"import json,hashlib;a=json.load(open('launch-attestation.json'));print([p for p,h in a['deliverySha256'].items() if hashlib.sha256(open(p,'rb').read()).hexdigest()!=h])\"` prints ['launch.json', 'test/AdversarialFees.t.sol'] (recorded d5e91f208512.. vs actual a0739ccf96a0.., recorded 2f64e1b32011.. vs actual cca590b89cf7..); expected []. Listing test/** against deliverySha256 shows six unrecorded files. All sourceSha256 entries and the three creationBytecode fields do match the current `forge build` output.","severity":"medium","snippet":"    assert set(manifest) == {\"kind\", \"chainId\", \"hook\", \"token\", \"pool\", \"notes\"}","title":"Launch attestation deliverable is stale and its documented check/regenerate command crashes on the schema-valid manifest"},{"citation":"resolved","description":"Merged from audit_permissions (low) and audit_flow (info); also answers the slither locked-ether and aderyn contract-locks-ether leads. In the base, afterSwap and unlockCallback took fee ETH to the hook itself and forwarded it, so receive() was needed. In this adaptation every fee path names the vault as take recipient (afterSwap line 200 `poolManager.take(key.currency0, address(vault), fee)`, unlockCallback line 231 `poolManager.take(Currency.wrap(address(0)), address(vault), amount)`), the hook never reads its own ETH balance, and there is no owner, sweep or selfdestruct. The manager-only guard stops plain sends but not ETH routed through the manager by any account's own unlock (`settle{value}` then `take(native, hook, amount)`), which receive() accepts. Likewise anyone can `mint(hook, 0, amount)` ERC-6909 native claims to the hook; redeemFees() burns exactly `claimFees`, so claims above the counter are never redeemed. Only the pusher loses value, so this is low, but it contradicts the README's 'All hook fees go to one immutable vault' framing for anyone who reads the hook as unable to hold ETH, and the attested bytecode is final. Minimal fix preserving the brief: delete receive() (a native take to the hook then reverts with NativeTransferFailed instead of locking), and optionally have redeemFees() redeem `poolManager.balanceOf(address(this), 0)` instead of the counter so stray claims still reach the vault. Both change the creation bytecode, so salts and the attestation must be regenerated; alternatively document in the README that the hook cannot forward ETH or stray claims.","line":236,"path":"src/SovrnHook.sol","reproduction":"State: real vendored PoolManager, SovrnToken, SovrnHook at 0x20cc (factory = test contract), pool initialized at 792281625142643375935439503360000, chain id 11155111; reproduced in test/scratch/JudgeRepro.t.sol::test_managerRoutedETHAndClaimsStrandInHook (passes as written, demonstrating the stranding). (a) `address(hook).call{value: 1 ether}(\"\")` from the test -> returns false (Unauthorized), as intended. (b) A Pusher contract calls `manager.unlock(..)`; in its callback: `manager.sync(Currency.wrap(address(0))); manager.settle{value: 1 ether}(); manager.take(Currency.wrap(address(0)), address(hook), 1 ether);` -> succeeds. Actual: `address(hook).balance == 1 ether`, `hook.claimFees() == 0`, `hook.redeemFees()` leaves the hook balance at 1 ether and the vault balance at 0. Expected for a hook not meant to hold ETH: the take is refused or the ETH is forwardable. (c) Same callback with `manager.mint(address(hook), 0, 1 ether)` -> `manager.balanceOf(hook, 0) == 1 ether`, `hook.claimFees() == 0`; after `hook.redeemFees()` the claim balance is still 1 ether and the vault balance 0.","severity":"low","snippet":"    receive() external payable {\n        if (msg.sender != address(poolManager)) revert Unauthorized();","title":"SovrnHook.receive() is dead code inherited from the base that lets manager-routed ETH and third-party native claims strand in the hook forever"},{"citation":"resolved","description":"From audit_math (low), recalibrated to info. beforeSwap and afterSwap accept a swap as soon as `initialized` is true with no liquidity condition. On a zero-liquidity pool v4's swap loop walks to sqrtPriceLimitX96 with amountIn = amountOut = 0 and persists the new price; both hook fee formulas evaluate to 0 on the zero native delta, so the swap completes with delta (0,0). This is unchanged base behaviour that the brief forbids altering, it is a property of every v4 pool, and the hook's fee math behaves correctly at the edge. It matters only if PoolManager.initialize and the factory's liquidity seeding are in different transactions; the README already requires deployment and initialization to be atomic and the IMD factory initializes in its own transaction. Recorded so the launch process confirms that seeding happens in the same transaction as initialize (or that a moved price cannot make the factory's one-sided SVO position require ETH); no code change is recommended under the current brief.","line":114,"path":"src/SovrnHook.sol","reproduction":"State: real vendored PoolManager, SovrnToken, SovrnHook at 0x20cc, pool initialized at sqrtPriceX96 792281625142643375935439503360000, no liquidity, chain id 11155111; test/scratch/JudgeRepro.t.sol::test_zeroLiquidityPriceMoveIsFree. ALICE (holding no SVO, 100 ETH) swaps through a settling router with `SwapParams(zeroForOne=false, amountSpecified=-1, sqrtPriceLimitX96=7922816251426433759354395033600000)`. Actual: swap succeeds, BalanceDelta is (0, 0), `getSlot0(poolId).sqrtPriceX96 == 7922816251426433759354395033600000` (ten times the opening sqrt price), vault balance 0, ALICE balance unchanged at 100 ether. Expected under the brief's opening numbers: the price stays 792281625142643375935439503360000 until the factory's liquidity is in place.","severity":"info","snippet":"    function beforeSwap(address, PoolKey calldata key, SwapParams calldata params, bytes calldata)","title":"Inherited v4 property: while the initialized pool holds no liquidity, anyone can move its price to any limit at zero cost, so the opening price only holds if the factory seeds liquidity atomically"},{"citation":"resolved","description":"Merged from the two audit_permissions trust-assumption notes. Both withdrawals are the only ETH exits from the vault and therefore from the whole system (hook fees reach nothing else), both go to the same constant address, and nothing on chain enforces inference spending, buybacks or burns: `burn()` only acts on SVO somebody chose to send. The split constrains two counters, not where the ETH ends up. This is exactly the design the brief specifies ('a Safe withdraws and does the buyback by hand') and the README documents it, so it is not a defect; it is recorded as the system's sole discretionary trust assumption, with the admission-time evidence it implies: the requester must control 0xb1eC9d1C36974d05eb9889eBf8A150b05791E559 on Sepolia and that account must be able to receive plain ETH (a Safe proxy must exist there on Sepolia, not only on mainnet), otherwise every fee is permanently locked or every withdrawal reverts with ETHSendFailed. No code change is required by the brief; enforcing the earmark on chain would be a scope decision.","line":10,"path":"src/LifeForceVault.sol","reproduction":"State: fresh vault; test/scratch/JudgeRepro.t.sol::test_vaultSplitAndInvariantSpots plus the delivered test/Vault.t.sol. Fund the vault with 10 ether via receive(): inferenceReserve()==7 ether, buybackReserve()==3 ether. `vm.prank(0xb1eC9d1C36974d05eb9889eBf8A150b05791E559); vault.withdrawBuyback(3 ether); vault.withdrawInference(7 ether);` -> vault balance 0, the Safe holds all 10 ether, token.balanceOf(DEAD)==0, totalBurned()==0; nothing reverted and no SVO was bought or burned. Any other caller: `withdrawInference(1)` reverts Unauthorized (line 35). If the Safe address holds code that rejects ETH, `vm.etch(safe, RejectETH.code); vm.prank(safe); vault.withdrawInference(7 ether)` reverts ETHSendFailed (test/Vault.t.sol::test_rejectingSafeRollsBackBothWithdrawalsAndCanRetry).","severity":"info","snippet":"    address public constant REFUEL_SAFE = 0xb1eC9d1C36974d05eb9889eBf8A150b05791E559;","title":"Trust assumption: REFUEL_SAFE is a hardcoded single address that can withdraw 100% of vault ETH to itself; the 70/30 earmark is accounting only and the Safe's control on chain 11155111 is unverified"},{"citation":"resolved","description":"From audit_economics (info), reproduced. The brief mandates a plain ERC-20 with no transfer restrictions, so nothing prevents anyone from initializing another ETH/SVO pool on the same PoolManager (or any other venue) with hooks = address(0). Trades there pay only the LP fee: the vault receives nothing and the 50%->3.5% launch-window buy fee does not apply, so once such a pool has liquidity the revenue base is the hooked pool's share of volume, not 'SVO trading fees' in general. This is inherent to the requested design and cannot be changed without changing the token (out of scope). The README states every number and privilege but never says the fee is per-pool and bypassable, which operators sizing inference revenue need to know. Suggested one-sentence addition to 'Fees and settlement': the hook fee applies only to the pool keyed by poolKey(); hookless SVO/ETH pools trade without it.","line":3,"path":"README.md","reproduction":"State: real vendored PoolManager, hooked pool (ETH/SVO, fee 12500, spacing 60, hook 0x20cc) initialized at 792281625142643375935439503360000 and seeded with SVO only in ticks [166200,184200], block.timestamp == openedAt; test/scratch/JudgeRepro.t.sol::test_hooklessPoolBypassesTheFee. An unprivileged account: (1) `manager.initialize(PoolKey(ETH, SVO, 3000, 60, IHooks(address(0))), 792281625142643375935439503360000)` succeeds; (2) adds full-range liquidity 1e22 with 10 ETH + SVO; (3) ALICE swaps `SwapParams(true, -1 ether, MIN_SQRT_PRICE+1)` on that key. Actual: delta.amount0 == -1 ether (all ETH to the AMM), vault balance 0, hook.claimFees() 0. The same swap on the hooked key in the same block pays a positive fee to the vault (a partial fill at the SVO-only range, charged on the actual AMM delta). Expected if 'SVO trading fees fund inference' held for all trading: the vault would receive 50% of the gross ETH.","severity":"info","snippet":"SVO trading fees fund the inference of a voice AI. All hook fees, collected only in native ETH, go to one immutable LifeForceVault: 70% is earmarked for inference and 30% for manually buying SVO to burn.","title":"README does not disclose that the hook fee applies only to the single hooked pool; a hookless SVO/ETH pool trades with zero fee to the vault"}],"hash":"e5e1a57173aa5ca1bc84c17265517acae0cb9763cbe602ddc720ab0296be799d","nodeId":"7680b4b2-a309-4ebd-9864-58bede70884f","outcome":"completed","summary":"Review complete. The findings file is written and validated, and the working tree has no changes to tracked files (scratch tests and the findings file are gitignored or expected outputs).\n\n**Outcome.** The Solidity is sound. The hook is a line-for-line port of the base with only the fee destination changed, the vault's accounting and privileges match the brief, and every one of the 63 delivered tests plus the 11 protected floor tests pass against the attested creation code. One real defect reopens the author's work, and it is in a deliverable rather than the contracts.\n\n**Findings kept, after reproduction and merging:**\n\n- **Medium, attest.py line 27.** All four specialists reported this and I merged them. The README's documented `python3 script/attest.py --check` command crashes with an AssertionError because the script demands a `chainId` manifest key the schema forbids. The committed attestation is also stale: two recorded hashes differ from the tree and six delivered test files are unrecorded. Creation bytecodes and source hashes still match, so only the deliverable and its procedure are broken.\n- **Low, SovrnHook receive().** Merged from two specialists. The function is dead code from the base's team-payment path. ETH routed through the manager, or claims minted to the hook by a third party, strand there with no exit. Only the pusher loses value. Reproduced on the real PoolManager.\n- **Info, zero-liquidity price move.** Inherited v4 property, reproduced. Matters only if the factory seeds liquidity in a later transaction than initialize. Downgraded from the specialist's low.\n- **Info, REFUEL_SAFE trust assumption.** Merged the two permission notes. The 70/30 split is accounting only and the Safe's control on Sepolia is unverified. Design as specified, recorded for admission evidence.\n- **Info, README disclosure.** A hookless SVO/ETH pool pays nothing to the vault. Reproduced. Inherent to a plain ERC-20.\n\n**Dropped.** The dust-rounding note reproduces but is not a defect: the brief mandates fee math identical to the base and the README documents the rounding.\n\n**Coverage.** All 14 entry points answered, plus three invariant rows. No row is unreached. Things I verified by hand beyond the specialists: the exact-out sell identity that requested plus fee equals the quoted gross, the split formula's exactness, the ERC-6909 burn authorization that keeps third parties from burning hook claims, and v4's self-call skip that makes the quote swap safe.","treeHash":null,"usage":{"cachedInputTokens":2058948,"inputTokens":482,"model":"claude-fable-5-1","outputTokens":34645,"runtime":"claude","turns":37,"wallClockMs":663708}}],"verification":[{"checks":[{"durationMs":58241,"exitCode":0,"name":"build","output":"Compiling 91 files with Solc 0.8.26\nSolc 0.8.26 finished in 58.08s\nCompiler run successful with warnings:\nWarning (5159): \"selfdestruct\" has been deprecated. Note that, starting from the Cancun hard fork, the underlying opcode no longer deletes the code and data associated with an account and only transfers its Ether to the beneficiary, unless executed in the same transaction in which the contract was created (see EIP-6780). Any use in newly deployed contracts is strongly discouraged even if the new behavior is taken into account. Future changes to the EVM might further reduce the functionality of the opcode.\n  --> test/Vault.t.sol:29:9:\n   |\n29 |         selfdestruct(to);\n   |         ^^^^^^^^^^^^\n\nwarning[reentrancy-eth]: uncapped ETH transfer can be reentered before `entered` is updated\n   ╭▸ src/Interfaces.sol:17:22\n   │\n17 │         (bool ok,) = to.call{value: amount}(\"\");\n   │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/LifeForceVault.sol:59:66\n   │\n59 │     function withdrawInference(uint256 amount) external onlySafe nonReentrant {\n   │                                                                  ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:64:9\n   │\n64 │         emit InferenceWithdrawn(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/LifeForceVault.sol:67:64\n   │\n67 │     function withdrawBuyback(uint256 amount) external onlySafe nonReentrant {\n   │                                                                ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:72:9\n   │\n72 │         emit BuybackWithdrawn(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:80:9\n   │\n80 │         emit Burned(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/LifeForceVault.sol:93:16\n   │\n93 │         return (amount / 10_000) * BUYBACK_BPS + (amount % 10_000) * BUYBACK_BPS / 10_000;\n   │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SovrnHook.sol:102:16\n    │\n102 │         return elapsed >= DECAY ? NORMAL_FEE : NORMAL_FEE + 0.465e18 * (DECAY - elapsed) / DECAY;\n    │                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SovrnHook.sol:108:16\n    │\n108 │         return elapsed >= DECAY ? 0 : (DECAY - elapsed + 59) / 60;\n    │                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SovrnHook.sol:148:25\n    │\n148 │             quotedFee = fee;\n    │                         ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:131:33\n    │\n131 │             uint256 requested = uint256(params.amountSpecified < 0 ? -params.amountSpecified : params.amountSpecified);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:134:48\n    │\n134 │                 quoteParams.amountSpecified = -int256(requested - fee);\n    │                                                ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:137:29\n    │\n137 │                 if (gross > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:137:37\n    │\n137 │                 if (gross > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:138:47\n    │\n138 │                 quoteParams.amountSpecified = int256(gross);\n    │                                               ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:143:31\n    │\n143 │                 if (actual != uint256(-quoteParams.amountSpecified)) fee = actual * rate / (WAD - rate);\n    │                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n   ╭▸ src/Interfaces.sol:17:22\n   │\n17 │         (bool ok,) = to.call{value: amount}(\"\");\n   │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:157:40\n    │\n157 │             if (reason.length != 36 || bytes4(reason) != QuoteResult.selector) {\n    │                                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:197:17\n    │\n197 │                 poolManager.mint(address(this), 0, fee);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:200:17\n    │\n200 │                 poolManager.take(key.currency0, address(vault), fee);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SovrnHook.sol:203:9\n    │\n203 │         emit FeePaid(sender, params.zeroForOne, gross, fee, asClaim);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:209:16\n    │\n209 │         return uint256(value < 0 ? -int256(value) : int256(value));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:213:21\n    │\n213 │         if (value > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:213:29\n    │\n213 │         if (value > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:214:16\n    │\n214 │         return int128(int256(value));\n    │                ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:214:23\n    │\n214 │         return int128(int256(value));\n    │                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:223:9\n    │\n223 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SovrnHook.sol:223:9\n    │\n223 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SovrnHook.sol:232:9\n    │\n232 │         emit ClaimsRedeemed(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":2668,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/LaunchPolicy.t.sol:LaunchPolicyTest\n[PASS] test_allFeeETHIsReleasableOnLaunchChain() (gas: 6202634)\n[PASS] test_launchPriceSupportsFourModesAndFullVaultFunding() (gas: 6818956)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.73ms (2.93ms CPU time)\n\nRan 1 test for test/Launch.t.sol:LaunchTest\n[PASS] test_realCreate2DeploymentInitializesAllContracts() (gas: 8959317)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.84ms (2.61ms CPU time)\n\nRan 3 tests for test/Token.t.sol:TokenTest\n[PASS] testFuzz_plainTransfers(uint256) (runs: 256, μ: 69454, ~: 69768)\n[PASS] test_infiniteAllowanceSelfTransferAndInsufficientBalance() (gas: 214138)\n[PASS] test_supplyPlainTransferAllowanceAndBurn() (gas: 317436)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 4.40ms (4.97ms CPU time)\n\nRan 8 tests for test/SettlementFailures.t.sol:SettlementFailuresTest\n[PASS] test_dustFeesSplitPerReceiptForDirectAndAccumulatedClaims() (gas: 2382101)\n[PASS] test_failedQuoteBubblesUpAndDoesNotKeepBusyState() (gas: 633861)\n[PASS] test_failedSettlementPreservesEarlierClaimsInBothPaymentModes() (gas: 2091730)\n[PASS] test_managerBalanceAtFeeThresholdPaysEntireFeeOneWay() (gas: 2338202)\n[PASS] test_nestedUnlockRedemptionFailsAtomicallyAndCanRetry() (gas: 1265215)\n[PASS] test_underpaidSwapRollsBackDirectFeesAndClaims() (gas: 2040355)\n[PASS] test_unpaidTokenInputRollsBackSellAndRestoresAllowance() (gas: 1035871)\n[PASS] test_zeroRoundedFeePreservesPendingClaims() (gas: 634403)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 11.40ms (10.26ms CPU time)\n\nRan 7 tests for test/Security.t.sol:SecurityTest\n[PASS] test_constructorCodeChecksAndInvalidFlags() (gas: 6338)\n[PASS] test_directFeeRejectionRollsBackSwapAndBusyGuard() (gas: 621494)\n[PASS] test_exactPermissionsAndFlags() (gas: 29484)\n[PASS] test_feeCallbackCannotReenterRedemption() (gas: 556515)\n[PASS] test_firstInitEveryFieldAndSecondInitRejected() (gas: 904478)\n[PASS] test_noAdministrationEvenForFactoryOrSafe() (gas: 1683978)\n[PASS] test_runtimeHasNoEscapeOpcodes() (gas: 1547379)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 50.65ms (7.82ms CPU time)\n\nRan 5 tests for test/RevisionBoundaries.t.sol:RevisionBoundariesTest\n[PASS] test_hooklessPoolBypassesTheHookFee() (gas: 1792763)\n[PASS] test_managerRoutedETHAndUnsolicitedClaimsAreNotFeeDeposits() (gas: 544591)\n[PASS] test_safeCanWithdrawBothReservesWithoutBuyingOrBurning() (gas: 225949)\n[PASS] test_strayClaimsDoNotDivertRecordedFeeRedemption() (gas: 1067033)\n[PASS] test_zeroLiquidityPriceMoveIsFree() (gas: 141093)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 56.06ms (3.83ms CPU time)\n\nRan 11 tests for test/Hook.t.sol:HookTest\n[PASS] testFuzz_feeBuyAndSell(uint96,bool,bool) (runs: 256, μ: 247329, ~: 222117)\n[PASS] test_decayAndFullFeeToVault() (gas: 358115)\n[PASS] test_exactModesRespectAmount() (gas: 734176)\n[PASS] test_feeAllFourModes() (gas: 831462)\n[PASS] test_partialBuyExactInput() (gas: 275259)\n[PASS] test_partialBuyExactOutput() (gas: 221269)\n[PASS] test_partialSellExactInput() (gas: 216685)\n[PASS] test_partialSellExactOutput() (gas: 275064)\n[PASS] test_permissionsAndUnauthorizedCallbacks() (gas: 147954)\n[PASS] test_sellsStayAtNormalFee() (gas: 450642)\n[PASS] test_wrongPoolRejected() (gas: 77960)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 67.43ms (75.30ms CPU time)\n\nRan 5 tests for test/LaunchPolicy.t.sol:FreshManagerTest\n[PASS] testFuzz_firstBuyClaimsBothExactModesAndDecay(bool,uint16) (runs: 256, μ: 489702, ~: 529417)\n[PASS] test_allFourModesMintClaimsInOneUnlockOnEmptyManager() (gas: 1240376)\n[PASS] test_claimAndDirectFeesMixedBeforeRedemption() (gas: 653328)\n[PASS] test_failedRedemptionRestoresClaimsAndCanRetry() (gas: 507350)\n[PASS] test_rejectingVaultDoesNotBlockClaimMintButRedemptionReverts() (gas: 607478)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 67.46ms (70.62ms CPU time)\n\nRan 5 tests for test/AdversarialFees.t.sol:AdversarialFeesTest\n[PASS] testFuzz_actualSettlementAndEvents(uint96,uint16,bool,bool,bool) (runs: 1000, μ: 322392, ~: 306202)\n[PASS] test_callbackRejectsZeroAndNarrowingOverflow() (gas: 484870)\n[PASS] test_decaySecondAndMinuteBoundaries() (gas: 255759)\n[PASS] test_quoteLeavesSamePriceLiquidityAndLPGrowthAsUnhookedPool() (gas: 1159176)\n[PASS] test_wrongPoolEveryBoundFieldAndUnpairedAfterSwap() (gas: 374302)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 86.47ms (86.87ms CPU time)\n\nRan 8 tests for test/Vault.t.sol:VaultTest\n[PASS] testFuzz_fundWithdrawRoundtrip(uint96,bool) (runs: 1000, μ: 229245, ~: 231527)\n[PASS] testFuzz_sameAndCrossWithdrawalReentryBlocked(bool) (runs: 256, μ: 720762, ~: 720762)\n[PASS] test_burnEntireBalanceToDeadWithoutETHMovement() (gas: 360864)\n[PASS] test_forcedETHIncludedAndWithdrawableWithoutAccountingDrift() (gas: 344262)\n[PASS] test_noTokenRescueApprovalOrAlternateDestinationEvenForSafe() (gas: 259508)\n[PASS] test_onlySafeAndMatchingReserve() (gas: 585074)\n[PASS] test_rejectingSafeRollsBackBothWithdrawalsAndCanRetry() (gas: 414171)\n[PASS] test_splitRoundingAndEvents() (gas: 1029898)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 89.71ms (158.30ms CPU time)\n\nRan 2 tests for test/ReviewRegression.t.sol:ReviewRegressionTest\n[PASS] testFuzz_tinyFeeRounding(uint16,bool,bool,uint16) (runs: 256, μ: 236489, ~: 233895)\n[PASS] test_fourModesInSameUnlockSettleNetAmounts() (gas: 1046425)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 89.67ms (89.15ms CPU time)\n\nRan 5 tests for test/VaultCallbackBoundaries.t.sol:VaultCallbackBoundariesTest\n[PASS] testFuzz_refundAndBurnDuringEitherWithdrawal(bool) (runs: 256, μ: 1186682, ~: 1186682)\n[PASS] testFuzz_rejectionRollsBackRefundBurnAndBothLedgers(bool) (runs: 256, μ: 1258046, ~: 1258046)\n[PASS] testFuzz_uint256ReceiptAndExit(uint256,bool) (runs: 1000, μ: 208043, ~: 209976)\n[PASS] test_counterfactualPrefundingAndForcedDustRemainWithdrawable() (gas: 865122)\n[PASS] test_maxUint256ReceiptDoesNotOverflowSplit() (gas: 210271)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 89.75ms (234.77ms CPU time)\n\nRan 2 tests for test/FeeDifferential.t.sol:FeeDifferentialTest\n[PASS] testFuzz_referencePoolAllModes(uint8,uint96,uint16,uint16,bool) (runs: 1000, μ: 586087, ~: 568648)\n[PASS] test_allFourModesAtEveryRequiredDecayTime() (gas: 7196529)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 89.72ms (97.06ms CPU time)\n\nRan 5 tests for test/TokenFailurePaths.t.sol:TokenFailurePathsTest\n[PASS] testFuzz_allowanceCannotBeBypassedOrBorrowed(uint96) (runs: 1000, μ: 190114, ~: 190560)\n[PASS] testFuzz_failedTransferFromRestoresFiniteAllowance(uint96,bool) (runs: 1000, μ: 251047, ~: 245394)\n[PASS] test_approvalOverwriteRevocationAndSelfSpend() (gas: 259407)\n[PASS] test_infiniteAllowanceSurvivesBurnAndFailedBalanceCheck() (gas: 241137)\n[PASS] test_zeroTransferFromNeedsNoAllowanceButCannotTargetZero() (gas: 98636)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 89.80ms (175.41ms CPU time)\n\nRan 1 test for test/VaultModelInvariants.t.sol:VaultModelInvariantTest\n[PASS] invariant_independentReserveAndAssetLedgers() (runs: 256, calls: 24576, reverts: 0)\n\n╭-------------------+--------------+-------+---------+----------╮\n| Contract          | Selector     | Calls | Reverts | Discards |\n+===============================================================+\n| VaultModelHandler | burn         | 3434  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | fund         | 3495  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | overdraw     | 3504  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | receiver     | 3513  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | sendTokens   | 3533  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | unauthorized | 3501  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | withdraw     | 3596  | 0       | 0        |\n╰-------------------+--------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.35s (1.35s CPU time)\n\nRan 1 test for test/LifecycleInvariants.t.sol:LifecycleInvariantTest\n[PASS]\nLifecycleInvariantTest invariants:\n[PASS] invariant_ETHAccountingAndOnlySafeReceivesWithdrawals\n[PASS] invariant_fixedSupplyAndEveryBurnConserved\n LifecycleInvariantTest invariants (runs: 256, calls: 24576, reverts: 0)\n\n╭------------------+----------------------+-------+---------+----------╮\n| Contract         | Selector             | Calls | Reverts | Discards |\n+======================================================================+\n| LifecycleHandler | advance              | 3076  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | donate               | 3098  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | moveAndBurn          | 3124  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | receiver             | 3022  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | redeem               | 3006  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | swap                 | 3112  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | unauthorizedWithdraw | 3065  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | withdraw             | 3073  | 0       | 0        |\n╰------------------+----------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.58s (2.57s CPU time)\n\nRan 16 test suites in 2.58s (4.72s CPU time): 71 tests passed, 0 failed, 0 skipped (71 total tests)\n","passed":true},{"durationMs":58,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"LifeForceVault.burn()\",\"LifeForceVault.receive()\",\"LifeForceVault.withdrawBuyback(uint256)\",\"LifeForceVault.withdrawInference(uint256)\",\"SovrnHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"SovrnHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"SovrnHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"SovrnHook.quoteNative((address,address,uint24,int24,address),(bool,int256,uint160))\",\"SovrnHook.receive()\",\"SovrnHook.redeemFees()\",\"SovrnHook.unlockCallback(bytes)\",\"SovrnToken.approve(address,uint256)\",\"SovrnToken.transfer(address,uint256)\",\"SovrnToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"README.md\":101,\"foundry.toml\":19,\"launch-attestation.json\":1367,\"launch.json\":31,\"script/PrepareLaunch.s.sol\":32,\"script/attest.py\":110,\"src/HookFlags.sol\":30,\"src/Interfaces.sol\":20,\"src/LifeForceVault.sol\":95,\"src/SovrnHook.sol\":239,\"src/SovrnToken.sol\":48,\"test/AdversarialFees.t.sol\":176,\"test/FeeDifferential.t.sol\":116,\"test/Hook.t.sol\":128,\"test/Launch.t.sol\":67,\"test/LaunchPolicy.t.sol\":167,\"test/LifecycleInvariants.t.sol\":171,\"test/PoolRouter.sol\":65,\"test/README.md\":64,\"test/REVIEW.md\":60,\"test/ReviewRegression.t.sol\":68,\"test/RevisionBoundaries.t.sol\":128,\"test/Security.t.sol\":167,\"test/SettlementFailures.t.sol\":276,\"test/SystemBase.sol\":67,\"test/Token.t.sol\":60,\"test/TokenFailurePaths.t.sol\":114,\"test/Vault.t.sol\":212,\"test/VaultCallbackBoundaries.t.sol\":189,\"test/VaultModelInvariants.t.sol\":166,\"test/mocks/MockERC20.sol\":9,\"test/test_attestation.py\":98},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"274fea5fd6b5238338796ff7706efe06c1caebe205faf1ed014805ee31e3ec4d","verifiedTreeHash":"25176c566effd8003f7be54b2204689d0817d04a","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":53512,"exitCode":0,"name":"build","output":"Compiling 90 files with Solc 0.8.26\nSolc 0.8.26 finished in 53.35s\nCompiler run successful with warnings:\nWarning (5159): \"selfdestruct\" has been deprecated. Note that, starting from the Cancun hard fork, the underlying opcode no longer deletes the code and data associated with an account and only transfers its Ether to the beneficiary, unless executed in the same transaction in which the contract was created (see EIP-6780). Any use in newly deployed contracts is strongly discouraged even if the new behavior is taken into account. Future changes to the EVM might further reduce the functionality of the opcode.\n  --> test/Vault.t.sol:29:9:\n   |\n29 |         selfdestruct(to);\n   |         ^^^^^^^^^^^^\n\nwarning[reentrancy-eth]: uncapped ETH transfer can be reentered before `entered` is updated\n   ╭▸ src/Interfaces.sol:17:22\n   │\n17 │         (bool ok,) = to.call{value: amount}(\"\");\n   │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/LifeForceVault.sol:59:66\n   │\n59 │     function withdrawInference(uint256 amount) external onlySafe nonReentrant {\n   │                                                                  ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:64:9\n   │\n64 │         emit InferenceWithdrawn(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/LifeForceVault.sol:67:64\n   │\n67 │     function withdrawBuyback(uint256 amount) external onlySafe nonReentrant {\n   │                                                                ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:72:9\n   │\n72 │         emit BuybackWithdrawn(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:80:9\n   │\n80 │         emit Burned(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/LifeForceVault.sol:93:16\n   │\n93 │         return (amount / 10_000) * BUYBACK_BPS + (amount % 10_000) * BUYBACK_BPS / 10_000;\n   │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SovrnHook.sol:102:16\n    │\n102 │         return elapsed >= DECAY ? NORMAL_FEE : NORMAL_FEE + 0.465e18 * (DECAY - elapsed) / DECAY;\n    │                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SovrnHook.sol:108:16\n    │\n108 │         return elapsed >= DECAY ? 0 : (DECAY - elapsed + 59) / 60;\n    │                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SovrnHook.sol:148:25\n    │\n148 │             quotedFee = fee;\n    │                         ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:131:33\n    │\n131 │             uint256 requested = uint256(params.amountSpecified < 0 ? -params.amountSpecified : params.amountSpecified);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:134:48\n    │\n134 │                 quoteParams.amountSpecified = -int256(requested - fee);\n    │                                                ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:137:29\n    │\n137 │                 if (gross > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:137:37\n    │\n137 │                 if (gross > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:138:47\n    │\n138 │                 quoteParams.amountSpecified = int256(gross);\n    │                                               ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:143:31\n    │\n143 │                 if (actual != uint256(-quoteParams.amountSpecified)) fee = actual * rate / (WAD - rate);\n    │                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n   ╭▸ src/Interfaces.sol:17:22\n   │\n17 │         (bool ok,) = to.call{value: amount}(\"\");\n   │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:157:40\n    │\n157 │             if (reason.length != 36 || bytes4(reason) != QuoteResult.selector) {\n    │                                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:197:17\n    │\n197 │                 poolManager.mint(address(this), 0, fee);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:200:17\n    │\n200 │                 poolManager.take(key.currency0, address(vault), fee);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SovrnHook.sol:203:9\n    │\n203 │         emit FeePaid(sender, params.zeroForOne, gross, fee, asClaim);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:209:16\n    │\n209 │         return uint256(value < 0 ? -int256(value) : int256(value));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:213:21\n    │\n213 │         if (value > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:213:29\n    │\n213 │         if (value > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:214:16\n    │\n214 │         return int128(int256(value));\n    │                ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:214:23\n    │\n214 │         return int128(int256(value));\n    │                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:223:9\n    │\n223 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SovrnHook.sol:223:9\n    │\n223 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SovrnHook.sol:232:9\n    │\n232 │         emit ClaimsRedeemed(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":2682,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Launch.t.sol:LaunchTest\n[PASS] test_realCreate2DeploymentInitializesAllContracts() (gas: 8959317)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.74ms (2.47ms CPU time)\n\nRan 5 tests for test/SettlementFailures.t.sol:SettlementFailuresTest\n[PASS] test_failedQuoteBubblesUpAndDoesNotKeepBusyState() (gas: 633839)\n[PASS] test_managerBalanceAtFeeThresholdPaysEntireFeeOneWay() (gas: 2338086)\n[PASS] test_nestedUnlockRedemptionFailsAtomicallyAndCanRetry() (gas: 1265172)\n[PASS] test_underpaidSwapRollsBackDirectFeesAndClaims() (gas: 2040208)\n[PASS] test_unpaidTokenInputRollsBackSellAndRestoresAllowance() (gas: 1035849)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 16.53ms (7.61ms CPU time)\n\nRan 2 tests for test/LaunchPolicy.t.sol:LaunchPolicyTest\n[PASS] test_allFeeETHIsReleasableOnLaunchChain() (gas: 6202634)\n[PASS] test_launchPriceSupportsFourModesAndFullVaultFunding() (gas: 6818956)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 59.51ms (4.12ms CPU time)\n\nRan 5 tests for test/VaultCallbackBoundaries.t.sol:VaultCallbackBoundariesTest\n[PASS] testFuzz_refundAndBurnDuringEitherWithdrawal(bool) (runs: 256, μ: 1186676, ~: 1186575)\n[PASS] testFuzz_rejectionRollsBackRefundBurnAndBothLedgers(bool) (runs: 256, μ: 1258042, ~: 1257973)\n[PASS] testFuzz_uint256ReceiptAndExit(uint256,bool) (runs: 1000, μ: 208360, ~: 209976)\n[PASS] test_counterfactualPrefundingAndForcedDustRemainWithdrawable() (gas: 865122)\n[PASS] test_maxUint256ReceiptDoesNotOverflowSplit() (gas: 210271)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 72.52ms (161.80ms CPU time)\n\nRan 3 tests for test/Token.t.sol:TokenTest\n[PASS] testFuzz_plainTransfers(uint256) (runs: 256, μ: 69702, ~: 69774)\n[PASS] test_infiniteAllowanceSelfTransferAndInsufficientBalance() (gas: 214138)\n[PASS] test_supplyPlainTransferAllowanceAndBurn() (gas: 317436)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 93.46ms (72.57ms CPU time)\n\nRan 5 tests for test/TokenFailurePaths.t.sol:TokenFailurePathsTest\n[PASS] testFuzz_allowanceCannotBeBypassedOrBorrowed(uint96) (runs: 1000, μ: 190024, ~: 190560)\n[PASS] testFuzz_failedTransferFromRestoresFiniteAllowance(uint96,bool) (runs: 1000, μ: 251302, ~: 245394)\n[PASS] test_approvalOverwriteRevocationAndSelfSpend() (gas: 259407)\n[PASS] test_infiniteAllowanceSurvivesBurnAndFailedBalanceCheck() (gas: 241137)\n[PASS] test_zeroTransferFromNeedsNoAllowanceButCannotTargetZero() (gas: 98636)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 93.55ms (178.66ms CPU time)\n\nRan 2 tests for test/ReviewRegression.t.sol:ReviewRegressionTest\n[PASS] testFuzz_tinyFeeRounding(uint16,bool,bool,uint16) (runs: 256, μ: 233465, ~: 233901)\n[PASS] test_fourModesInSameUnlockSettleNetAmounts() (gas: 1046425)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 93.71ms (88.71ms CPU time)\n\nRan 7 tests for test/Security.t.sol:SecurityTest\n[PASS] test_constructorCodeChecksAndInvalidFlags() (gas: 6338)\n[PASS] test_directFeeRejectionRollsBackSwapAndBusyGuard() (gas: 621494)\n[PASS] test_exactPermissionsAndFlags() (gas: 29484)\n[PASS] test_feeCallbackCannotReenterRedemption() (gas: 556515)\n[PASS] test_firstInitEveryFieldAndSecondInitRejected() (gas: 904478)\n[PASS] test_noAdministrationEvenForFactoryOrSafe() (gas: 1683978)\n[PASS] test_runtimeHasNoEscapeOpcodes() (gas: 1547379)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 96.07ms (7.26ms CPU time)\n\nRan 5 tests for test/AdversarialFees.t.sol:AdversarialFeesTest\n[PASS] testFuzz_actualSettlementAndEvents(uint96,uint16,bool,bool,bool) (runs: 1000, μ: 321480, ~: 306106)\n[PASS] test_callbackRejectsZeroAndNarrowingOverflow() (gas: 484870)\n[PASS] test_decaySecondAndMinuteBoundaries() (gas: 255759)\n[PASS] test_quoteLeavesSamePriceLiquidityAndLPGrowthAsUnhookedPool() (gas: 1159176)\n[PASS] test_wrongPoolEveryBoundFieldAndUnpairedAfterSwap() (gas: 374302)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 96.22ms (97.49ms CPU time)\n\nRan 8 tests for test/Vault.t.sol:VaultTest\n[PASS] testFuzz_fundWithdrawRoundtrip(uint96,bool) (runs: 1000, μ: 229553, ~: 231527)\n[PASS] testFuzz_sameAndCrossWithdrawalReentryBlocked(bool) (runs: 256, μ: 720756, ~: 720667)\n[PASS] test_burnEntireBalanceToDeadWithoutETHMovement() (gas: 360864)\n[PASS] test_forcedETHIncludedAndWithdrawableWithoutAccountingDrift() (gas: 344262)\n[PASS] test_noTokenRescueApprovalOrAlternateDestinationEvenForSafe() (gas: 259508)\n[PASS] test_onlySafeAndMatchingReserve() (gas: 585074)\n[PASS] test_rejectingSafeRollsBackBothWithdrawalsAndCanRetry() (gas: 414171)\n[PASS] test_splitRoundingAndEvents() (gas: 1029898)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 96.20ms (192.44ms CPU time)\n\nRan 11 tests for test/Hook.t.sol:HookTest\n[PASS] testFuzz_feeBuyAndSell(uint96,bool,bool) (runs: 256, μ: 246755, ~: 222083)\n[PASS] test_decayAndFullFeeToVault() (gas: 358115)\n[PASS] test_exactModesRespectAmount() (gas: 734176)\n[PASS] test_feeAllFourModes() (gas: 831462)\n[PASS] test_partialBuyExactInput() (gas: 275259)\n[PASS] test_partialBuyExactOutput() (gas: 221269)\n[PASS] test_partialSellExactInput() (gas: 216685)\n[PASS] test_partialSellExactOutput() (gas: 275064)\n[PASS] test_permissionsAndUnauthorizedCallbacks() (gas: 147954)\n[PASS] test_sellsStayAtNormalFee() (gas: 450642)\n[PASS] test_wrongPoolRejected() (gas: 77960)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 96.17ms (105.00ms CPU time)\n\nRan 2 tests for test/FeeDifferential.t.sol:FeeDifferentialTest\n[PASS] testFuzz_referencePoolAllModes(uint8,uint96,uint16,uint16,bool) (runs: 1000, μ: 587057, ~: 596738)\n[PASS] test_allFourModesAtEveryRequiredDecayTime() (gas: 7196529)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 96.28ms (96.84ms CPU time)\n\nRan 5 tests for test/LaunchPolicy.t.sol:FreshManagerTest\n[PASS] testFuzz_firstBuyClaimsBothExactModesAndDecay(bool,uint16) (runs: 256, μ: 481866, ~: 443815)\n[PASS] test_allFourModesMintClaimsInOneUnlockOnEmptyManager() (gas: 1240376)\n[PASS] test_claimAndDirectFeesMixedBeforeRedemption() (gas: 653328)\n[PASS] test_failedRedemptionRestoresClaimsAndCanRetry() (gas: 507350)\n[PASS] test_rejectingVaultDoesNotBlockClaimMintButRedemptionReverts() (gas: 607478)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 96.44ms (97.74ms CPU time)\n\nRan 1 test for test/VaultModelInvariants.t.sol:VaultModelInvariantTest\n[PASS] invariant_independentReserveAndAssetLedgers() (runs: 256, calls: 24576, reverts: 0)\n\n╭-------------------+--------------+-------+---------+----------╮\n| Contract          | Selector     | Calls | Reverts | Discards |\n+===============================================================+\n| VaultModelHandler | burn         | 3579  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | fund         | 3481  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | overdraw     | 3460  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | receiver     | 3508  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | sendTokens   | 3448  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | unauthorized | 3520  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | withdraw     | 3580  | 0       | 0        |\n╰-------------------+--------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.39s (1.39s CPU time)\n\nRan 1 test for test/LifecycleInvariants.t.sol:LifecycleInvariantTest\n[PASS]\nLifecycleInvariantTest invariants:\n[PASS] invariant_ETHAccountingAndOnlySafeReceivesWithdrawals\n[PASS] invariant_fixedSupplyAndEveryBurnConserved\n LifecycleInvariantTest invariants (runs: 256, calls: 24576, reverts: 0)\n\n╭------------------+----------------------+-------+---------+----------╮\n| Contract         | Selector             | Calls | Reverts | Discards |\n+======================================================================+\n| LifecycleHandler | advance              | 2979  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | donate               | 3016  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | moveAndBurn          | 3035  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | receiver             | 3106  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | redeem               | 3084  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | swap                 | 3075  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | unauthorizedWithdraw | 3166  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | withdraw             | 3115  | 0       | 0        |\n╰------------------+----------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.59s (2.57s CPU time)\n\nRan 15 test suites in 2.59s (4.99s CPU time): 63 tests passed, 0 failed, 0 skipped (63 total tests)\n","passed":true},{"durationMs":64,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"LifeForceVault.burn()\",\"LifeForceVault.receive()\",\"LifeForceVault.withdrawBuyback(uint256)\",\"LifeForceVault.withdrawInference(uint256)\",\"SovrnHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"SovrnHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"SovrnHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"SovrnHook.quoteNative((address,address,uint24,int24,address),(bool,int256,uint160))\",\"SovrnHook.receive()\",\"SovrnHook.redeemFees()\",\"SovrnHook.unlockCallback(bytes)\",\"SovrnToken.approve(address,uint256)\",\"SovrnToken.transfer(address,uint256)\",\"SovrnToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"README.md\":89,\"foundry.toml\":19,\"launch-attestation.json\":1365,\"launch.json\":32,\"script/PrepareLaunch.s.sol\":32,\"script/attest.py\":110,\"src/HookFlags.sol\":30,\"src/Interfaces.sol\":20,\"src/LifeForceVault.sol\":95,\"src/SovrnHook.sol\":239,\"src/SovrnToken.sol\":48,\"test/AdversarialFees.t.sol\":176,\"test/FeeDifferential.t.sol\":116,\"test/Hook.t.sol\":128,\"test/Launch.t.sol\":67,\"test/LaunchPolicy.t.sol\":167,\"test/LifecycleInvariants.t.sol\":171,\"test/PoolRouter.sol\":65,\"test/README.md\":62,\"test/REVIEW.md\":43,\"test/ReviewRegression.t.sol\":68,\"test/Security.t.sol\":167,\"test/SettlementFailures.t.sol\":186,\"test/SystemBase.sol\":67,\"test/Token.t.sol\":60,\"test/TokenFailurePaths.t.sol\":114,\"test/Vault.t.sol\":212,\"test/VaultCallbackBoundaries.t.sol\":189,\"test/VaultModelInvariants.t.sol\":166,\"test/mocks/MockERC20.sol\":9},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"3dfd05928b9667e3fab247bb1ead083d9d5afde8872b63c46955b6d2e370a996","verifiedTreeHash":"5d75ab9982ba2f97754de45973077d86a75d95fd","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":40949,"exitCode":0,"name":"build","output":"Compiling 84 files with Solc 0.8.26\nSolc 0.8.26 finished in 40.79s\nCompiler run successful with warnings:\nWarning (5159): \"selfdestruct\" has been deprecated. Note that, starting from the Cancun hard fork, the underlying opcode no longer deletes the code and data associated with an account and only transfers its Ether to the beneficiary, unless executed in the same transaction in which the contract was created (see EIP-6780). Any use in newly deployed contracts is strongly discouraged even if the new behavior is taken into account. Future changes to the EVM might further reduce the functionality of the opcode.\n  --> test/Vault.t.sol:29:9:\n   |\n29 |         selfdestruct(to);\n   |         ^^^^^^^^^^^^\n\nwarning[reentrancy-eth]: uncapped ETH transfer can be reentered before `entered` is updated\n   ╭▸ src/Interfaces.sol:17:22\n   │\n17 │         (bool ok,) = to.call{value: amount}(\"\");\n   │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/LifeForceVault.sol:59:66\n   │\n59 │     function withdrawInference(uint256 amount) external onlySafe nonReentrant {\n   │                                                                  ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:64:9\n   │\n64 │         emit InferenceWithdrawn(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/LifeForceVault.sol:67:64\n   │\n67 │     function withdrawBuyback(uint256 amount) external onlySafe nonReentrant {\n   │                                                                ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:72:9\n   │\n72 │         emit BuybackWithdrawn(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:80:9\n   │\n80 │         emit Burned(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/LifeForceVault.sol:93:16\n   │\n93 │         return (amount / 10_000) * BUYBACK_BPS + (amount % 10_000) * BUYBACK_BPS / 10_000;\n   │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SovrnHook.sol:102:16\n    │\n102 │         return elapsed >= DECAY ? NORMAL_FEE : NORMAL_FEE + 0.465e18 * (DECAY - elapsed) / DECAY;\n    │                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SovrnHook.sol:108:16\n    │\n108 │         return elapsed >= DECAY ? 0 : (DECAY - elapsed + 59) / 60;\n    │                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n   ╭▸ src/Interfaces.sol:17:22\n   │\n17 │         (bool ok,) = to.call{value: amount}(\"\");\n   │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SovrnHook.sol:148:25\n    │\n148 │             quotedFee = fee;\n    │                         ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:131:33\n    │\n131 │             uint256 requested = uint256(params.amountSpecified < 0 ? -params.amountSpecified : params.amountSpecified);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:134:48\n    │\n134 │                 quoteParams.amountSpecified = -int256(requested - fee);\n    │                                                ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:137:29\n    │\n137 │                 if (gross > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:137:37\n    │\n137 │                 if (gross > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:138:47\n    │\n138 │                 quoteParams.amountSpecified = int256(gross);\n    │                                               ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:143:31\n    │\n143 │                 if (actual != uint256(-quoteParams.amountSpecified)) fee = actual * rate / (WAD - rate);\n    │                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:157:40\n    │\n157 │             if (reason.length != 36 || bytes4(reason) != QuoteResult.selector) {\n    │                                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:197:17\n    │\n197 │                 poolManager.mint(address(this), 0, fee);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:200:17\n    │\n200 │                 poolManager.take(key.currency0, address(vault), fee);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SovrnHook.sol:203:9\n    │\n203 │         emit FeePaid(sender, params.zeroForOne, gross, fee, asClaim);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:209:16\n    │\n209 │         return uint256(value < 0 ? -int256(value) : int256(value));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:213:21\n    │\n213 │         if (value > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:213:29\n    │\n213 │         if (value > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:214:16\n    │\n214 │         return int128(int256(value));\n    │                ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:214:23\n    │\n214 │         return int128(int256(value));\n    │                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:223:9\n    │\n223 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SovrnHook.sol:223:9\n    │\n223 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SovrnHook.sol:232:9\n    │\n232 │         emit ClaimsRedeemed(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":2682,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Launch.t.sol:LaunchTest\n[PASS] test_realCreate2DeploymentInitializesAllContracts() (gas: 8959317)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.40ms (2.14ms CPU time)\n\nRan 2 tests for test/ReviewRegression.t.sol:ReviewRegressionTest\n[PASS] testFuzz_tinyFeeRounding(uint16,bool,bool,uint16) (runs: 256, μ: 241080, ~: 234103)\n[PASS] test_fourModesInSameUnlockSettleNetAmounts() (gas: 1046425)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 31.94ms (32.26ms CPU time)\n\nRan 7 tests for test/Security.t.sol:SecurityTest\n[PASS] test_constructorCodeChecksAndInvalidFlags() (gas: 6338)\n[PASS] test_directFeeRejectionRollsBackSwapAndBusyGuard() (gas: 621494)\n[PASS] test_exactPermissionsAndFlags() (gas: 29484)\n[PASS] test_feeCallbackCannotReenterRedemption() (gas: 556515)\n[PASS] test_firstInitEveryFieldAndSecondInitRejected() (gas: 904478)\n[PASS] test_noAdministrationEvenForFactoryOrSafe() (gas: 1683978)\n[PASS] test_runtimeHasNoEscapeOpcodes() (gas: 1547379)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 38.10ms (8.43ms CPU time)\n\nRan 11 tests for test/Hook.t.sol:HookTest\n[PASS] testFuzz_feeBuyAndSell(uint96,bool,bool) (runs: 256, μ: 248212, ~: 265527)\n[PASS] test_decayAndFullFeeToVault() (gas: 358115)\n[PASS] test_exactModesRespectAmount() (gas: 734176)\n[PASS] test_feeAllFourModes() (gas: 831462)\n[PASS] test_partialBuyExactInput() (gas: 275259)\n[PASS] test_partialBuyExactOutput() (gas: 221269)\n[PASS] test_partialSellExactInput() (gas: 216685)\n[PASS] test_partialSellExactOutput() (gas: 275064)\n[PASS] test_permissionsAndUnauthorizedCallbacks() (gas: 147954)\n[PASS] test_sellsStayAtNormalFee() (gas: 450642)\n[PASS] test_wrongPoolRejected() (gas: 77960)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 38.07ms (44.44ms CPU time)\n\nRan 2 tests for test/LaunchPolicy.t.sol:LaunchPolicyTest\n[PASS] test_allFeeETHIsReleasableOnLaunchChain() (gas: 6202634)\n[PASS] test_launchPriceSupportsFourModesAndFullVaultFunding() (gas: 6818956)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 44.20ms (3.80ms CPU time)\n\nRan 5 tests for test/LaunchPolicy.t.sol:FreshManagerTest\n[PASS] testFuzz_firstBuyClaimsBothExactModesAndDecay(bool,uint16) (runs: 256, μ: 485964, ~: 443815)\n[PASS] test_allFourModesMintClaimsInOneUnlockOnEmptyManager() (gas: 1240376)\n[PASS] test_claimAndDirectFeesMixedBeforeRedemption() (gas: 653328)\n[PASS] test_failedRedemptionRestoresClaimsAndCanRetry() (gas: 507350)\n[PASS] test_rejectingVaultDoesNotBlockClaimMintButRedemptionReverts() (gas: 607478)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 44.24ms (46.97ms CPU time)\n\nRan 8 tests for test/Vault.t.sol:VaultTest\n[PASS] testFuzz_fundWithdrawRoundtrip(uint96,bool) (runs: 1000, μ: 230081, ~: 231527)\n[PASS] testFuzz_sameAndCrossWithdrawalReentryBlocked(bool) (runs: 256, μ: 720762, ~: 720857)\n[PASS] test_burnEntireBalanceToDeadWithoutETHMovement() (gas: 360864)\n[PASS] test_forcedETHIncludedAndWithdrawableWithoutAccountingDrift() (gas: 344262)\n[PASS] test_noTokenRescueApprovalOrAlternateDestinationEvenForSafe() (gas: 259508)\n[PASS] test_onlySafeAndMatchingReserve() (gas: 585074)\n[PASS] test_rejectingSafeRollsBackBothWithdrawalsAndCanRetry() (gas: 414171)\n[PASS] test_splitRoundingAndEvents() (gas: 1029898)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 44.26ms (91.19ms CPU time)\n\nRan 3 tests for test/Token.t.sol:TokenTest\n[PASS] testFuzz_plainTransfers(uint256) (runs: 256, μ: 69593, ~: 69768)\n[PASS] test_infiniteAllowanceSelfTransferAndInsufficientBalance() (gas: 214138)\n[PASS] test_supplyPlainTransferAllowanceAndBurn() (gas: 317436)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 44.36ms (44.90ms CPU time)\n\nRan 5 tests for test/AdversarialFees.t.sol:AdversarialFeesTest\n[PASS] testFuzz_actualSettlementAndEvents(uint96,uint16,bool,bool,bool) (runs: 1000, μ: 321576, ~: 306065)\n[PASS] test_callbackRejectsZeroAndNarrowingOverflow() (gas: 484870)\n[PASS] test_decaySecondAndMinuteBoundaries() (gas: 255759)\n[PASS] test_quoteLeavesSamePriceLiquidityAndLPGrowthAsUnhookedPool() (gas: 1159176)\n[PASS] test_wrongPoolEveryBoundFieldAndUnpairedAfterSwap() (gas: 227501)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 44.40ms (46.64ms CPU time)\n\nRan 1 test for test/LifecycleInvariants.t.sol:LifecycleInvariantTest\n[PASS]\nLifecycleInvariantTest invariants:\n[PASS] invariant_ETHAccountingAndOnlySafeReceivesWithdrawals\n[PASS] invariant_fixedSupplyAndEveryBurnConserved\n LifecycleInvariantTest invariants (runs: 256, calls: 24576, reverts: 0)\n\n╭------------------+----------------------+-------+---------+----------╮\n| Contract         | Selector             | Calls | Reverts | Discards |\n+======================================================================+\n| LifecycleHandler | advance              | 3088  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | donate               | 3061  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | moveAndBurn          | 3080  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | receiver             | 3073  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | redeem               | 3007  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | swap                 | 3112  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | unauthorizedWithdraw | 3080  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | withdraw             | 3075  | 0       | 0        |\n╰------------------+----------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.59s (2.59s CPU time)\n\nRan 10 test suites in 2.59s (2.93s CPU time): 45 tests passed, 0 failed, 0 skipped (45 total tests)\n","passed":true},{"durationMs":53,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"LifeForceVault.burn()\",\"LifeForceVault.receive()\",\"LifeForceVault.withdrawBuyback(uint256)\",\"LifeForceVault.withdrawInference(uint256)\",\"SovrnHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"SovrnHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"SovrnHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"SovrnHook.quoteNative((address,address,uint24,int24,address),(bool,int256,uint160))\",\"SovrnHook.receive()\",\"SovrnHook.redeemFees()\",\"SovrnHook.unlockCallback(bytes)\",\"SovrnToken.approve(address,uint256)\",\"SovrnToken.transfer(address,uint256)\",\"SovrnToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"README.md\":89,\"foundry.toml\":19,\"launch-attestation.json\":1365,\"launch.json\":32,\"script/PrepareLaunch.s.sol\":32,\"script/attest.py\":110,\"src/HookFlags.sol\":30,\"src/Interfaces.sol\":20,\"src/LifeForceVault.sol\":95,\"src/SovrnHook.sol\":239,\"src/SovrnToken.sol\":48,\"test/AdversarialFees.t.sol\":173,\"test/Hook.t.sol\":128,\"test/Launch.t.sol\":67,\"test/LaunchPolicy.t.sol\":167,\"test/LifecycleInvariants.t.sol\":171,\"test/PoolRouter.sol\":65,\"test/REVIEW.md\":43,\"test/ReviewRegression.t.sol\":68,\"test/Security.t.sol\":167,\"test/SystemBase.sol\":67,\"test/Token.t.sol\":60,\"test/Vault.t.sol\":212,\"test/mocks/MockERC20.sol\":9},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1780,"exitCode":0,"name":"slither","output":"[medium/medium] divide-before-multiply at src/LifeForceVault.sol:91: LifeForceVault._buybackShare(uint256) (src/LifeForceVault.sol#91-94) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/LifeForceVault.sol:75: LifeForceVault.burn() (src/LifeForceVault.sol#75-81) uses a dangerous strict equality:\n[medium/high] locked-ether at src/SovrnHook.sol:236: Contract locking ether found:\n[medium/medium] reentrancy-no-eth at src/SovrnHook.sol:172: Reentrancy in SovrnHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/SovrnHook.sol#172-206):\n[medium/medium] uninitialized-local at src/SovrnHook.sol:126: SovrnHook.beforeSwap(address,PoolKey,SwapParams,bytes).fee (src/SovrnHook.sol#126) is a local variable never initialized\n[medium/medium] unused-return at src/SovrnHook.sol:218: SovrnHook.redeemFees() (src/SovrnHook.sol#218-225) ignores return value by poolManager.unlock(abi.encode(amount)) (src/SovrnHook.sol#223)\n[low/medium] reentrancy-benign at src/SovrnHook.sol:218: Reentrancy in SovrnHook.redeemFees() (src/SovrnHook.sol#218-225):\n[low/medium] reentrancy-benign at src/SovrnHook.sol:172: Reentrancy in SovrnHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/SovrnHook.sol#172-206):\n[low/medium] reentrancy-benign at src/SovrnHook.sol:114: Reentrancy in SovrnHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/SovrnHook.sol#114-151):\n[low/medium] reentrancy-events at src/LifeForceVault.sol:75: Reentrancy in LifeForceVault.burn() (src/LifeForceVault.sol#75-81):\n[low/medium] reentrancy-events at src/SovrnHook.sol:172: Reentrancy in SovrnHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/SovrnHook.sol#172-206):\n[low/medium] reentrancy-events at src/SovrnHook.sol:227: Reentrancy in SovrnHook.unlockCallback(bytes) (src/SovrnHook.sol#227-234):\n[low/medium] timestamp at src/SovrnHook.sol:114: SovrnHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/SovrnHook.sol#114-151) uses timestamp for comparisons\n[low/medium] timestamp at src/SovrnHook.sol:212: SovrnHook._int128(uint256) (src/SovrnHook.sol#212-215) uses timestamp for comparisons\n[low/medium] timestamp at src/SovrnHook.sol:172: SovrnHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/SovrnHook.sol#172-206) uses timestamp for comparisons\n[low/medium] timestamp at src/SovrnHook.sol:105: SovrnHook.decayMinutesLeft() (src/SovrnHook.sol#105-109) uses timestamp for comparisons\n[low/medium] timestamp at src/SovrnHook.sol:99: SovrnHook.launchFeeNow() (src/SovrnHook.sol#99-103) uses timestamp for comparisons","passed":true},{"durationMs":352,"exitCode":0,"name":"aderyn","output":"[high] contract-locks-ether at src/SovrnHook.sol:15: Contract locks Ether without a withdraw function\n[high] reentrancy-state-change at src/SovrnHook.sol:197: Reentrancy: State change after external call (3 places)\n[low] internal-function-used-once at src/HookFlags.sol:23: Internal Function Used Only Once\n[low] large-numeric-literal at src/LifeForceVault.sol:93: Large Numeric Literal (4 places)\n[low] literal-instead-of-constant at src/LifeForceVault.sol:93: Literal Instead of Constant (8 places)\n[low] missing-inheritance at src/SovrnToken.sol:5: Missing Inheritance\n[low] modifier-used-only-once at src/SovrnHook.sol:55: Modifier Invoked Only Once\n[low] non-reentrant-not-first at src/LifeForceVault.sol:59: `nonReentrant` is Not the First Modifier (2 places)\n[low] state-change-without-event at src/SovrnHook.sol:114: State Change Without Event (2 places)\n[low] unchecked-return at src/SovrnHook.sol:223: Unchecked Return\n[low] unsafe-erc20-operation at src/LifeForceVault.sol:79: Unsafe ERC20 Operation\n[low] unused-public-function at src/LifeForceVault.sol:47: Public Function Not Used Internally (3 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"4ebbcab2dcf7214951d756bfeb7ac19a17e8e88b8ea46230e9b49aa2c968ac88","verifiedTreeHash":"622304d5af2523cc875554de2012e098c60d3ae1","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":52569,"exitCode":0,"name":"build","output":"Compiling 90 files with Solc 0.8.26\nSolc 0.8.26 finished in 52.42s\nCompiler run successful with warnings:\nWarning (5159): \"selfdestruct\" has been deprecated. Note that, starting from the Cancun hard fork, the underlying opcode no longer deletes the code and data associated with an account and only transfers its Ether to the beneficiary, unless executed in the same transaction in which the contract was created (see EIP-6780). Any use in newly deployed contracts is strongly discouraged even if the new behavior is taken into account. Future changes to the EVM might further reduce the functionality of the opcode.\n  --> test/Vault.t.sol:29:9:\n   |\n29 |         selfdestruct(to);\n   |         ^^^^^^^^^^^^\n\nwarning[reentrancy-eth]: uncapped ETH transfer can be reentered before `entered` is updated\n   ╭▸ src/Interfaces.sol:17:22\n   │\n17 │         (bool ok,) = to.call{value: amount}(\"\");\n   │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/LifeForceVault.sol:59:66\n   │\n59 │     function withdrawInference(uint256 amount) external onlySafe nonReentrant {\n   │                                                                  ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:64:9\n   │\n64 │         emit InferenceWithdrawn(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/LifeForceVault.sol:67:64\n   │\n67 │     function withdrawBuyback(uint256 amount) external onlySafe nonReentrant {\n   │                                                                ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:72:9\n   │\n72 │         emit BuybackWithdrawn(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:80:9\n   │\n80 │         emit Burned(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/LifeForceVault.sol:93:16\n   │\n93 │         return (amount / 10_000) * BUYBACK_BPS + (amount % 10_000) * BUYBACK_BPS / 10_000;\n   │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SovrnHook.sol:102:16\n    │\n102 │         return elapsed >= DECAY ? NORMAL_FEE : NORMAL_FEE + 0.465e18 * (DECAY - elapsed) / DECAY;\n    │                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SovrnHook.sol:108:16\n    │\n108 │         return elapsed >= DECAY ? 0 : (DECAY - elapsed + 59) / 60;\n    │                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SovrnHook.sol:148:25\n    │\n148 │             quotedFee = fee;\n    │                         ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:131:33\n    │\n131 │             uint256 requested = uint256(params.amountSpecified < 0 ? -params.amountSpecified : params.amountSpecified);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:134:48\n    │\n134 │                 quoteParams.amountSpecified = -int256(requested - fee);\n    │                                                ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:137:29\n    │\n137 │                 if (gross > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:137:37\n    │\n137 │                 if (gross > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:138:47\n    │\n138 │                 quoteParams.amountSpecified = int256(gross);\n    │                                               ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:143:31\n    │\n143 │                 if (actual != uint256(-quoteParams.amountSpecified)) fee = actual * rate / (WAD - rate);\n    │                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n   ╭▸ src/Interfaces.sol:17:22\n   │\n17 │         (bool ok,) = to.call{value: amount}(\"\");\n   │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:157:40\n    │\n157 │             if (reason.length != 36 || bytes4(reason) != QuoteResult.selector) {\n    │                                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:197:17\n    │\n197 │                 poolManager.mint(address(this), 0, fee);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:200:17\n    │\n200 │                 poolManager.take(key.currency0, address(vault), fee);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SovrnHook.sol:203:9\n    │\n203 │         emit FeePaid(sender, params.zeroForOne, gross, fee, asClaim);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:209:16\n    │\n209 │         return uint256(value < 0 ? -int256(value) : int256(value));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:213:21\n    │\n213 │         if (value > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:213:29\n    │\n213 │         if (value > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:214:16\n    │\n214 │         return int128(int256(value));\n    │                ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:214:23\n    │\n214 │         return int128(int256(value));\n    │                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:223:9\n    │\n223 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SovrnHook.sol:223:9\n    │\n223 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SovrnHook.sol:232:9\n    │\n232 │         emit ClaimsRedeemed(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":2644,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Launch.t.sol:LaunchTest\n[PASS] test_realCreate2DeploymentInitializesAllContracts() (gas: 8959317)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.92ms (2.64ms CPU time)\n\nRan 3 tests for test/Token.t.sol:TokenTest\n[PASS] testFuzz_plainTransfers(uint256) (runs: 256, μ: 69838, ~: 69792)\n[PASS] test_infiniteAllowanceSelfTransferAndInsufficientBalance() (gas: 214138)\n[PASS] test_supplyPlainTransferAllowanceAndBurn() (gas: 317436)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 30.78ms (31.25ms CPU time)\n\nRan 2 tests for test/LaunchPolicy.t.sol:LaunchPolicyTest\n[PASS] test_allFeeETHIsReleasableOnLaunchChain() (gas: 6202634)\n[PASS] test_launchPriceSupportsFourModesAndFullVaultFunding() (gas: 6818956)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 33.42ms (3.39ms CPU time)\n\nRan 2 tests for test/ReviewRegression.t.sol:ReviewRegressionTest\n[PASS] testFuzz_tinyFeeRounding(uint16,bool,bool,uint16) (runs: 256, μ: 238093, ~: 234091)\n[PASS] test_fourModesInSameUnlockSettleNetAmounts() (gas: 1046425)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 47.12ms (47.32ms CPU time)\n\nRan 7 tests for test/Security.t.sol:SecurityTest\n[PASS] test_constructorCodeChecksAndInvalidFlags() (gas: 6338)\n[PASS] test_directFeeRejectionRollsBackSwapAndBusyGuard() (gas: 621494)\n[PASS] test_exactPermissionsAndFlags() (gas: 29484)\n[PASS] test_feeCallbackCannotReenterRedemption() (gas: 556515)\n[PASS] test_firstInitEveryFieldAndSecondInitRejected() (gas: 904478)\n[PASS] test_noAdministrationEvenForFactoryOrSafe() (gas: 1683978)\n[PASS] test_runtimeHasNoEscapeOpcodes() (gas: 1547379)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 58.89ms (10.94ms CPU time)\n\nRan 11 tests for test/Hook.t.sol:HookTest\n[PASS] testFuzz_feeBuyAndSell(uint96,bool,bool) (runs: 256, μ: 248014, ~: 265354)\n[PASS] test_decayAndFullFeeToVault() (gas: 358115)\n[PASS] test_exactModesRespectAmount() (gas: 734176)\n[PASS] test_feeAllFourModes() (gas: 831462)\n[PASS] test_partialBuyExactInput() (gas: 275259)\n[PASS] test_partialBuyExactOutput() (gas: 221269)\n[PASS] test_partialSellExactInput() (gas: 216685)\n[PASS] test_partialSellExactOutput() (gas: 275064)\n[PASS] test_permissionsAndUnauthorizedCallbacks() (gas: 147954)\n[PASS] test_sellsStayAtNormalFee() (gas: 450642)\n[PASS] test_wrongPoolRejected() (gas: 77960)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 88.61ms (91.24ms CPU time)\n\nRan 5 tests for test/LaunchPolicy.t.sol:FreshManagerTest\n[PASS] testFuzz_firstBuyClaimsBothExactModesAndDecay(bool,uint16) (runs: 256, μ: 479475, ~: 443607)\n[PASS] test_allFourModesMintClaimsInOneUnlockOnEmptyManager() (gas: 1240376)\n[PASS] test_claimAndDirectFeesMixedBeforeRedemption() (gas: 653328)\n[PASS] test_failedRedemptionRestoresClaimsAndCanRetry() (gas: 507350)\n[PASS] test_rejectingVaultDoesNotBlockClaimMintButRedemptionReverts() (gas: 607478)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 89.55ms (81.02ms CPU time)\n\nRan 5 tests for test/TokenFailurePaths.t.sol:TokenFailurePathsTest\n[PASS] testFuzz_allowanceCannotBeBypassedOrBorrowed(uint96) (runs: 1000, μ: 189998, ~: 190596)\n[PASS] testFuzz_failedTransferFromRestoresFiniteAllowance(uint96,bool) (runs: 1000, μ: 251365, ~: 245394)\n[PASS] test_approvalOverwriteRevocationAndSelfSpend() (gas: 259407)\n[PASS] test_infiniteAllowanceSurvivesBurnAndFailedBalanceCheck() (gas: 241137)\n[PASS] test_zeroTransferFromNeedsNoAllowanceButCannotTargetZero() (gas: 98636)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 89.51ms (151.71ms CPU time)\n\nRan 8 tests for test/Vault.t.sol:VaultTest\n[PASS] testFuzz_fundWithdrawRoundtrip(uint96,bool) (runs: 1000, μ: 229680, ~: 231527)\n[PASS] testFuzz_sameAndCrossWithdrawalReentryBlocked(bool) (runs: 256, μ: 720764, ~: 720857)\n[PASS] test_burnEntireBalanceToDeadWithoutETHMovement() (gas: 360864)\n[PASS] test_forcedETHIncludedAndWithdrawableWithoutAccountingDrift() (gas: 344262)\n[PASS] test_noTokenRescueApprovalOrAlternateDestinationEvenForSafe() (gas: 259508)\n[PASS] test_onlySafeAndMatchingReserve() (gas: 585074)\n[PASS] test_rejectingSafeRollsBackBothWithdrawalsAndCanRetry() (gas: 414171)\n[PASS] test_splitRoundingAndEvents() (gas: 1029898)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 95.51ms (128.53ms CPU time)\n\nRan 2 tests for test/FeeDifferential.t.sol:FeeDifferentialTest\n[PASS] testFuzz_referencePoolAllModes(uint8,uint96,uint16,uint16,bool) (runs: 1000, μ: 586753, ~: 596738)\n[PASS] test_allFourModesAtEveryRequiredDecayTime() (gas: 7196529)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 97.13ms (100.34ms CPU time)\n\nRan 5 tests for test/SettlementFailures.t.sol:SettlementFailuresTest\n[PASS] test_failedQuoteBubblesUpAndDoesNotKeepBusyState() (gas: 633839)\n[PASS] test_managerBalanceAtFeeThresholdPaysEntireFeeOneWay() (gas: 2338086)\n[PASS] test_nestedUnlockRedemptionFailsAtomicallyAndCanRetry() (gas: 1265172)\n[PASS] test_underpaidSwapRollsBackDirectFeesAndClaims() (gas: 2040208)\n[PASS] test_unpaidTokenInputRollsBackSellAndRestoresAllowance() (gas: 1035849)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 99.70ms (8.14ms CPU time)\n\nRan 5 tests for test/VaultCallbackBoundaries.t.sol:VaultCallbackBoundariesTest\n[PASS] testFuzz_refundAndBurnDuringEitherWithdrawal(bool) (runs: 256, μ: 1186684, ~: 1186789)\n[PASS] testFuzz_rejectionRollsBackRefundBurnAndBothLedgers(bool) (runs: 256, μ: 1258048, ~: 1258120)\n[PASS] testFuzz_uint256ReceiptAndExit(uint256,bool) (runs: 1000, μ: 208428, ~: 209976)\n[PASS] test_counterfactualPrefundingAndForcedDustRemainWithdrawable() (gas: 865122)\n[PASS] test_maxUint256ReceiptDoesNotOverflowSplit() (gas: 210271)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 99.72ms (251.91ms CPU time)\n\nRan 5 tests for test/AdversarialFees.t.sol:AdversarialFeesTest\n[PASS] testFuzz_actualSettlementAndEvents(uint96,uint16,bool,bool,bool) (runs: 1000, μ: 321287, ~: 306170)\n[PASS] test_callbackRejectsZeroAndNarrowingOverflow() (gas: 484870)\n[PASS] test_decaySecondAndMinuteBoundaries() (gas: 255759)\n[PASS] test_quoteLeavesSamePriceLiquidityAndLPGrowthAsUnhookedPool() (gas: 1159176)\n[PASS] test_wrongPoolEveryBoundFieldAndUnpairedAfterSwap() (gas: 374302)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 99.82ms (104.95ms CPU time)\n\nRan 1 test for test/VaultModelInvariants.t.sol:VaultModelInvariantTest\n[PASS] invariant_independentReserveAndAssetLedgers() (runs: 256, calls: 24576, reverts: 0)\n\n╭-------------------+--------------+-------+---------+----------╮\n| Contract          | Selector     | Calls | Reverts | Discards |\n+===============================================================+\n| VaultModelHandler | burn         | 3524  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | fund         | 3530  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | overdraw     | 3471  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | receiver     | 3568  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | sendTokens   | 3557  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | unauthorized | 3408  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | withdraw     | 3518  | 0       | 0        |\n╰-------------------+--------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.33s (1.33s CPU time)\n\nRan 1 test for test/LifecycleInvariants.t.sol:LifecycleInvariantTest\n[PASS]\nLifecycleInvariantTest invariants:\n[PASS] invariant_ETHAccountingAndOnlySafeReceivesWithdrawals\n[PASS] invariant_fixedSupplyAndEveryBurnConserved\n LifecycleInvariantTest invariants (runs: 256, calls: 24576, reverts: 0)\n\n╭------------------+----------------------+-------+---------+----------╮\n| Contract         | Selector             | Calls | Reverts | Discards |\n+======================================================================+\n| LifecycleHandler | advance              | 3116  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | donate               | 3024  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | moveAndBurn          | 3058  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | receiver             | 3089  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | redeem               | 3088  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | swap                 | 3121  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | unauthorizedWithdraw | 3071  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | withdraw             | 3009  | 0       | 0        |\n╰------------------+----------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.55s (2.55s CPU time)\n\nRan 15 test suites in 2.56s (4.82s CPU time): 63 tests passed, 0 failed, 0 skipped (63 total tests)\n","passed":true},{"durationMs":60,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"LifeForceVault.burn()\",\"LifeForceVault.receive()\",\"LifeForceVault.withdrawBuyback(uint256)\",\"LifeForceVault.withdrawInference(uint256)\",\"SovrnHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"SovrnHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"SovrnHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"SovrnHook.quoteNative((address,address,uint24,int24,address),(bool,int256,uint160))\",\"SovrnHook.receive()\",\"SovrnHook.redeemFees()\",\"SovrnHook.unlockCallback(bytes)\",\"SovrnToken.approve(address,uint256)\",\"SovrnToken.transfer(address,uint256)\",\"SovrnToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"README.md\":89,\"foundry.toml\":19,\"launch-attestation.json\":1365,\"launch.json\":31,\"script/PrepareLaunch.s.sol\":32,\"script/attest.py\":110,\"src/HookFlags.sol\":30,\"src/Interfaces.sol\":20,\"src/LifeForceVault.sol\":95,\"src/SovrnHook.sol\":239,\"src/SovrnToken.sol\":48,\"test/AdversarialFees.t.sol\":176,\"test/FeeDifferential.t.sol\":116,\"test/Hook.t.sol\":128,\"test/Launch.t.sol\":67,\"test/LaunchPolicy.t.sol\":167,\"test/LifecycleInvariants.t.sol\":171,\"test/PoolRouter.sol\":65,\"test/README.md\":62,\"test/REVIEW.md\":43,\"test/ReviewRegression.t.sol\":68,\"test/Security.t.sol\":167,\"test/SettlementFailures.t.sol\":186,\"test/SystemBase.sol\":67,\"test/Token.t.sol\":60,\"test/TokenFailurePaths.t.sol\":114,\"test/Vault.t.sol\":212,\"test/VaultCallbackBoundaries.t.sol\":189,\"test/VaultModelInvariants.t.sol\":166,\"test/mocks/MockERC20.sol\":9},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"751021e489107c09df96fffebc0f1b2d024550378a7367f3adaa8f10913bd4d5","verifiedTreeHash":"40baaf63a1898b99db18b9ae6a933a887ad51c47","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":62612,"exitCode":0,"name":"build","output":"Compiling 91 files with Solc 0.8.26\nSolc 0.8.26 finished in 62.43s\nCompiler run successful with warnings:\nWarning (5159): \"selfdestruct\" has been deprecated. Note that, starting from the Cancun hard fork, the underlying opcode no longer deletes the code and data associated with an account and only transfers its Ether to the beneficiary, unless executed in the same transaction in which the contract was created (see EIP-6780). Any use in newly deployed contracts is strongly discouraged even if the new behavior is taken into account. Future changes to the EVM might further reduce the functionality of the opcode.\n  --> test/Vault.t.sol:29:9:\n   |\n29 |         selfdestruct(to);\n   |         ^^^^^^^^^^^^\n\nwarning[reentrancy-eth]: uncapped ETH transfer can be reentered before `entered` is updated\n   ╭▸ src/Interfaces.sol:17:22\n   │\n17 │         (bool ok,) = to.call{value: amount}(\"\");\n   │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/LifeForceVault.sol:59:66\n   │\n59 │     function withdrawInference(uint256 amount) external onlySafe nonReentrant {\n   │                                                                  ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:64:9\n   │\n64 │         emit InferenceWithdrawn(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/LifeForceVault.sol:67:64\n   │\n67 │     function withdrawBuyback(uint256 amount) external onlySafe nonReentrant {\n   │                                                                ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:72:9\n   │\n72 │         emit BuybackWithdrawn(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:80:9\n   │\n80 │         emit Burned(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/LifeForceVault.sol:93:16\n   │\n93 │         return (amount / 10_000) * BUYBACK_BPS + (amount % 10_000) * BUYBACK_BPS / 10_000;\n   │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SovrnHook.sol:102:16\n    │\n102 │         return elapsed >= DECAY ? NORMAL_FEE : NORMAL_FEE + 0.465e18 * (DECAY - elapsed) / DECAY;\n    │                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SovrnHook.sol:108:16\n    │\n108 │         return elapsed >= DECAY ? 0 : (DECAY - elapsed + 59) / 60;\n    │                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SovrnHook.sol:148:25\n    │\n148 │             quotedFee = fee;\n    │                         ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:131:33\n    │\n131 │             uint256 requested = uint256(params.amountSpecified < 0 ? -params.amountSpecified : params.amountSpecified);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:134:48\n    │\n134 │                 quoteParams.amountSpecified = -int256(requested - fee);\n    │                                                ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:137:29\n    │\n137 │                 if (gross > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:137:37\n    │\n137 │                 if (gross > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:138:47\n    │\n138 │                 quoteParams.amountSpecified = int256(gross);\n    │                                               ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:143:31\n    │\n143 │                 if (actual != uint256(-quoteParams.amountSpecified)) fee = actual * rate / (WAD - rate);\n    │                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n   ╭▸ src/Interfaces.sol:17:22\n   │\n17 │         (bool ok,) = to.call{value: amount}(\"\");\n   │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:157:40\n    │\n157 │             if (reason.length != 36 || bytes4(reason) != QuoteResult.selector) {\n    │                                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:197:17\n    │\n197 │                 poolManager.mint(address(this), 0, fee);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:200:17\n    │\n200 │                 poolManager.take(key.currency0, address(vault), fee);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SovrnHook.sol:203:9\n    │\n203 │         emit FeePaid(sender, params.zeroForOne, gross, fee, asClaim);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:209:16\n    │\n209 │         return uint256(value < 0 ? -int256(value) : int256(value));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:213:21\n    │\n213 │         if (value > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:213:29\n    │\n213 │         if (value > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:214:16\n    │\n214 │         return int128(int256(value));\n    │                ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:214:23\n    │\n214 │         return int128(int256(value));\n    │                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:223:9\n    │\n223 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SovrnHook.sol:223:9\n    │\n223 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SovrnHook.sol:232:9\n    │\n232 │         emit ClaimsRedeemed(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":2796,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Launch.t.sol:LaunchTest\n[PASS] test_realCreate2DeploymentInitializesAllContracts() (gas: 8959317)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.93ms (2.66ms CPU time)\n\nRan 2 tests for test/LaunchPolicy.t.sol:LaunchPolicyTest\n[PASS] test_allFeeETHIsReleasableOnLaunchChain() (gas: 6202634)\n[PASS] test_launchPriceSupportsFourModesAndFullVaultFunding() (gas: 6818956)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 5.71ms (4.68ms CPU time)\n\nRan 5 tests for test/RevisionBoundaries.t.sol:RevisionBoundariesTest\n[PASS] test_hooklessPoolBypassesTheHookFee() (gas: 1792763)\n[PASS] test_managerRoutedETHAndUnsolicitedClaimsAreNotFeeDeposits() (gas: 544591)\n[PASS] test_safeCanWithdrawBothReservesWithoutBuyingOrBurning() (gas: 225949)\n[PASS] test_strayClaimsDoNotDivertRecordedFeeRedemption() (gas: 1067033)\n[PASS] test_zeroLiquidityPriceMoveIsFree() (gas: 141093)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 25.31ms (3.18ms CPU time)\n\nRan 2 tests for test/ReviewRegression.t.sol:ReviewRegressionTest\n[PASS] testFuzz_tinyFeeRounding(uint16,bool,bool,uint16) (runs: 256, μ: 234809, ~: 233907)\n[PASS] test_fourModesInSameUnlockSettleNetAmounts() (gas: 1046425)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 62.20ms (61.55ms CPU time)\n\nRan 8 tests for test/SettlementFailures.t.sol:SettlementFailuresTest\n[PASS] test_dustFeesSplitPerReceiptForDirectAndAccumulatedClaims() (gas: 2382101)\n[PASS] test_failedQuoteBubblesUpAndDoesNotKeepBusyState() (gas: 633861)\n[PASS] test_failedSettlementPreservesEarlierClaimsInBothPaymentModes() (gas: 2091730)\n[PASS] test_managerBalanceAtFeeThresholdPaysEntireFeeOneWay() (gas: 2338202)\n[PASS] test_nestedUnlockRedemptionFailsAtomicallyAndCanRetry() (gas: 1265215)\n[PASS] test_underpaidSwapRollsBackDirectFeesAndClaims() (gas: 2040355)\n[PASS] test_unpaidTokenInputRollsBackSellAndRestoresAllowance() (gas: 1035871)\n[PASS] test_zeroRoundedFeePreservesPendingClaims() (gas: 634403)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 64.93ms (13.58ms CPU time)\n\nRan 11 tests for test/Hook.t.sol:HookTest\n[PASS] testFuzz_feeBuyAndSell(uint96,bool,bool) (runs: 256, μ: 248305, ~: 269941)\n[PASS] test_decayAndFullFeeToVault() (gas: 358115)\n[PASS] test_exactModesRespectAmount() (gas: 734176)\n[PASS] test_feeAllFourModes() (gas: 831462)\n[PASS] test_partialBuyExactInput() (gas: 275259)\n[PASS] test_partialBuyExactOutput() (gas: 221269)\n[PASS] test_partialSellExactInput() (gas: 216685)\n[PASS] test_partialSellExactOutput() (gas: 275064)\n[PASS] test_permissionsAndUnauthorizedCallbacks() (gas: 147954)\n[PASS] test_sellsStayAtNormalFee() (gas: 450642)\n[PASS] test_wrongPoolRejected() (gas: 77960)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 78.38ms (61.20ms CPU time)\n\nRan 5 tests for test/AdversarialFees.t.sol:AdversarialFeesTest\n[PASS] testFuzz_actualSettlementAndEvents(uint96,uint16,bool,bool,bool) (runs: 1000, μ: 323123, ~: 306253)\n[PASS] test_callbackRejectsZeroAndNarrowingOverflow() (gas: 484870)\n[PASS] test_decaySecondAndMinuteBoundaries() (gas: 255759)\n[PASS] test_quoteLeavesSamePriceLiquidityAndLPGrowthAsUnhookedPool() (gas: 1159176)\n[PASS] test_wrongPoolEveryBoundFieldAndUnpairedAfterSwap() (gas: 374302)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 78.50ms (79.84ms CPU time)\n\nRan 8 tests for test/Vault.t.sol:VaultTest\n[PASS] testFuzz_fundWithdrawRoundtrip(uint96,bool) (runs: 1000, μ: 229368, ~: 231527)\n[PASS] testFuzz_sameAndCrossWithdrawalReentryBlocked(bool) (runs: 256, μ: 720760, ~: 720667)\n[PASS] test_burnEntireBalanceToDeadWithoutETHMovement() (gas: 360864)\n[PASS] test_forcedETHIncludedAndWithdrawableWithoutAccountingDrift() (gas: 344262)\n[PASS] test_noTokenRescueApprovalOrAlternateDestinationEvenForSafe() (gas: 259508)\n[PASS] test_onlySafeAndMatchingReserve() (gas: 585074)\n[PASS] test_rejectingSafeRollsBackBothWithdrawalsAndCanRetry() (gas: 414171)\n[PASS] test_splitRoundingAndEvents() (gas: 1029898)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 81.94ms (133.40ms CPU time)\n\nRan 3 tests for test/Token.t.sol:TokenTest\n[PASS] testFuzz_plainTransfers(uint256) (runs: 256, μ: 69406, ~: 69768)\n[PASS] test_infiniteAllowanceSelfTransferAndInsufficientBalance() (gas: 214138)\n[PASS] test_supplyPlainTransferAllowanceAndBurn() (gas: 317436)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 89.60ms (90.12ms CPU time)\n\nRan 5 tests for test/LaunchPolicy.t.sol:FreshManagerTest\n[PASS] testFuzz_firstBuyClaimsBothExactModesAndDecay(bool,uint16) (runs: 256, μ: 494866, ~: 530927)\n[PASS] test_allFourModesMintClaimsInOneUnlockOnEmptyManager() (gas: 1240376)\n[PASS] test_claimAndDirectFeesMixedBeforeRedemption() (gas: 653328)\n[PASS] test_failedRedemptionRestoresClaimsAndCanRetry() (gas: 507350)\n[PASS] test_rejectingVaultDoesNotBlockClaimMintButRedemptionReverts() (gas: 607478)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 89.68ms (91.41ms CPU time)\n\nRan 5 tests for test/VaultCallbackBoundaries.t.sol:VaultCallbackBoundariesTest\n[PASS] testFuzz_refundAndBurnDuringEitherWithdrawal(bool) (runs: 256, μ: 1186680, ~: 1186575)\n[PASS] testFuzz_rejectionRollsBackRefundBurnAndBothLedgers(bool) (runs: 256, μ: 1258045, ~: 1257973)\n[PASS] testFuzz_uint256ReceiptAndExit(uint256,bool) (runs: 1000, μ: 208244, ~: 209976)\n[PASS] test_counterfactualPrefundingAndForcedDustRemainWithdrawable() (gas: 865122)\n[PASS] test_maxUint256ReceiptDoesNotOverflowSplit() (gas: 210271)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 89.73ms (215.74ms CPU time)\n\nRan 7 tests for test/Security.t.sol:SecurityTest\n[PASS] test_constructorCodeChecksAndInvalidFlags() (gas: 6338)\n[PASS] test_directFeeRejectionRollsBackSwapAndBusyGuard() (gas: 621494)\n[PASS] test_exactPermissionsAndFlags() (gas: 29484)\n[PASS] test_feeCallbackCannotReenterRedemption() (gas: 556515)\n[PASS] test_firstInitEveryFieldAndSecondInitRejected() (gas: 904478)\n[PASS] test_noAdministrationEvenForFactoryOrSafe() (gas: 1683978)\n[PASS] test_runtimeHasNoEscapeOpcodes() (gas: 1547379)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 89.75ms (6.65ms CPU time)\n\nRan 5 tests for test/TokenFailurePaths.t.sol:TokenFailurePathsTest\n[PASS] testFuzz_allowanceCannotBeBypassedOrBorrowed(uint96) (runs: 1000, μ: 190032, ~: 190560)\n[PASS] testFuzz_failedTransferFromRestoresFiniteAllowance(uint96,bool) (runs: 1000, μ: 251593, ~: 258981)\n[PASS] test_approvalOverwriteRevocationAndSelfSpend() (gas: 259407)\n[PASS] test_infiniteAllowanceSurvivesBurnAndFailedBalanceCheck() (gas: 241137)\n[PASS] test_zeroTransferFromNeedsNoAllowanceButCannotTargetZero() (gas: 98636)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 89.86ms (151.73ms CPU time)\n\nRan 2 tests for test/FeeDifferential.t.sol:FeeDifferentialTest\n[PASS] testFuzz_referencePoolAllModes(uint8,uint96,uint16,uint16,bool) (runs: 1000, μ: 589057, ~: 597052)\n[PASS] test_allFourModesAtEveryRequiredDecayTime() (gas: 7196529)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 89.97ms (97.04ms CPU time)\n\nRan 1 test for test/VaultModelInvariants.t.sol:VaultModelInvariantTest\n[PASS] invariant_independentReserveAndAssetLedgers() (runs: 256, calls: 24576, reverts: 0)\n\n╭-------------------+--------------+-------+---------+----------╮\n| Contract          | Selector     | Calls | Reverts | Discards |\n+===============================================================+\n| VaultModelHandler | burn         | 3564  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | fund         | 3462  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | overdraw     | 3427  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | receiver     | 3512  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | sendTokens   | 3588  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | unauthorized | 3496  | 0       | 0        |\n|-------------------+--------------+-------+---------+----------|\n| VaultModelHandler | withdraw     | 3527  | 0       | 0        |\n╰-------------------+--------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.40s (1.40s CPU time)\n\nRan 1 test for test/LifecycleInvariants.t.sol:LifecycleInvariantTest\n[PASS]\nLifecycleInvariantTest invariants:\n[PASS] invariant_ETHAccountingAndOnlySafeReceivesWithdrawals\n[PASS] invariant_fixedSupplyAndEveryBurnConserved\n LifecycleInvariantTest invariants (runs: 256, calls: 24576, reverts: 0)\n\n╭------------------+----------------------+-------+---------+----------╮\n| Contract         | Selector             | Calls | Reverts | Discards |\n+======================================================================+\n| LifecycleHandler | advance              | 3079  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | donate               | 3119  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | moveAndBurn          | 3029  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | receiver             | 3092  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | redeem               | 3049  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | swap                 | 3019  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | unauthorizedWithdraw | 3112  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | withdraw             | 3077  | 0       | 0        |\n╰------------------+----------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.69s (2.68s CPU time)\n\nRan 16 test suites in 2.69s (5.02s CPU time): 71 tests passed, 0 failed, 0 skipped (71 total tests)\n","passed":true},{"durationMs":79,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"LifeForceVault.burn()\",\"LifeForceVault.receive()\",\"LifeForceVault.withdrawBuyback(uint256)\",\"LifeForceVault.withdrawInference(uint256)\",\"SovrnHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"SovrnHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"SovrnHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"SovrnHook.quoteNative((address,address,uint24,int24,address),(bool,int256,uint160))\",\"SovrnHook.receive()\",\"SovrnHook.redeemFees()\",\"SovrnHook.unlockCallback(bytes)\",\"SovrnToken.approve(address,uint256)\",\"SovrnToken.transfer(address,uint256)\",\"SovrnToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"README.md\":101,\"foundry.toml\":19,\"launch-attestation.json\":1367,\"launch.json\":31,\"script/PrepareLaunch.s.sol\":32,\"script/attest.py\":110,\"src/HookFlags.sol\":30,\"src/Interfaces.sol\":20,\"src/LifeForceVault.sol\":95,\"src/SovrnHook.sol\":239,\"src/SovrnToken.sol\":48,\"test/AdversarialFees.t.sol\":176,\"test/FeeDifferential.t.sol\":116,\"test/Hook.t.sol\":128,\"test/Launch.t.sol\":67,\"test/LaunchPolicy.t.sol\":167,\"test/LifecycleInvariants.t.sol\":171,\"test/PoolRouter.sol\":65,\"test/README.md\":64,\"test/REVIEW.md\":60,\"test/ReviewRegression.t.sol\":68,\"test/RevisionBoundaries.t.sol\":128,\"test/Security.t.sol\":167,\"test/SettlementFailures.t.sol\":276,\"test/SystemBase.sol\":67,\"test/Token.t.sol\":60,\"test/TokenFailurePaths.t.sol\":114,\"test/Vault.t.sol\":212,\"test/VaultCallbackBoundaries.t.sol\":189,\"test/VaultModelInvariants.t.sol\":166,\"test/mocks/MockERC20.sol\":9,\"test/test_attestation.py\":98},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"c42b46c9d76aec21a0e6480c1f83e59c402e89da12af50391622e1bac182fe14","verifiedTreeHash":"5dfbd44bb3842930dd9d20145a408b3348d1b441","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":43231,"exitCode":0,"name":"build","output":"Compiling 85 files with Solc 0.8.26\nSolc 0.8.26 finished in 43.07s\nCompiler run successful with warnings:\nWarning (5159): \"selfdestruct\" has been deprecated. Note that, starting from the Cancun hard fork, the underlying opcode no longer deletes the code and data associated with an account and only transfers its Ether to the beneficiary, unless executed in the same transaction in which the contract was created (see EIP-6780). Any use in newly deployed contracts is strongly discouraged even if the new behavior is taken into account. Future changes to the EVM might further reduce the functionality of the opcode.\n  --> test/Vault.t.sol:29:9:\n   |\n29 |         selfdestruct(to);\n   |         ^^^^^^^^^^^^\n\nwarning[reentrancy-eth]: uncapped ETH transfer can be reentered before `entered` is updated\n   ╭▸ src/Interfaces.sol:17:22\n   │\n17 │         (bool ok,) = to.call{value: amount}(\"\");\n   │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/LifeForceVault.sol:59:66\n   │\n59 │     function withdrawInference(uint256 amount) external onlySafe nonReentrant {\n   │                                                                  ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:64:9\n   │\n64 │         emit InferenceWithdrawn(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/LifeForceVault.sol:67:64\n   │\n67 │     function withdrawBuyback(uint256 amount) external onlySafe nonReentrant {\n   │                                                                ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:72:9\n   │\n72 │         emit BuybackWithdrawn(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/LifeForceVault.sol:80:9\n   │\n80 │         emit Burned(amount);\n   │         ━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n   ╭▸ src/LifeForceVault.sol:93:16\n   │\n93 │         return (amount / 10_000) * BUYBACK_BPS + (amount % 10_000) * BUYBACK_BPS / 10_000;\n   │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SovrnHook.sol:102:16\n    │\n102 │         return elapsed >= DECAY ? NORMAL_FEE : NORMAL_FEE + 0.465e18 * (DECAY - elapsed) / DECAY;\n    │                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SovrnHook.sol:108:16\n    │\n108 │         return elapsed >= DECAY ? 0 : (DECAY - elapsed + 59) / 60;\n    │                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n   ╭▸ src/Interfaces.sol:17:22\n   │\n17 │         (bool ok,) = to.call{value: amount}(\"\");\n   │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SovrnHook.sol:148:25\n    │\n148 │             quotedFee = fee;\n    │                         ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:131:33\n    │\n131 │             uint256 requested = uint256(params.amountSpecified < 0 ? -params.amountSpecified : params.amountSpecified);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:134:48\n    │\n134 │                 quoteParams.amountSpecified = -int256(requested - fee);\n    │                                                ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:137:29\n    │\n137 │                 if (gross > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:137:37\n    │\n137 │                 if (gross > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:138:47\n    │\n138 │                 quoteParams.amountSpecified = int256(gross);\n    │                                               ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:143:31\n    │\n143 │                 if (actual != uint256(-quoteParams.amountSpecified)) fee = actual * rate / (WAD - rate);\n    │                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:157:40\n    │\n157 │             if (reason.length != 36 || bytes4(reason) != QuoteResult.selector) {\n    │                                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:197:17\n    │\n197 │                 poolManager.mint(address(this), 0, fee);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:200:17\n    │\n200 │                 poolManager.take(key.currency0, address(vault), fee);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SovrnHook.sol:203:9\n    │\n203 │         emit FeePaid(sender, params.zeroForOne, gross, fee, asClaim);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:209:16\n    │\n209 │         return uint256(value < 0 ? -int256(value) : int256(value));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:213:21\n    │\n213 │         if (value > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:213:29\n    │\n213 │         if (value > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:214:16\n    │\n214 │         return int128(int256(value));\n    │                ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SovrnHook.sol:214:23\n    │\n214 │         return int128(int256(value));\n    │                       ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `busy` is updated\n    ╭▸ src/SovrnHook.sol:223:9\n    │\n223 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/SovrnHook.sol:223:9\n    │\n223 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SovrnHook.sol:232:9\n    │\n232 │         emit ClaimsRedeemed(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":2680,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/LaunchPolicy.t.sol:LaunchPolicyTest\n[PASS] test_allFeeETHIsReleasableOnLaunchChain() (gas: 6202634)\n[PASS] test_launchPriceSupportsFourModesAndFullVaultFunding() (gas: 6818956)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.02ms (2.79ms CPU time)\n\nRan 1 test for test/Launch.t.sol:LaunchTest\n[PASS] test_realCreate2DeploymentInitializesAllContracts() (gas: 8959317)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.79ms (3.84ms CPU time)\n\nRan 3 tests for test/Token.t.sol:TokenTest\n[PASS] testFuzz_plainTransfers(uint256) (runs: 256, μ: 69559, ~: 69768)\n[PASS] test_infiniteAllowanceSelfTransferAndInsufficientBalance() (gas: 214138)\n[PASS] test_supplyPlainTransferAllowanceAndBurn() (gas: 317436)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 25.19ms (25.68ms CPU time)\n\nRan 5 tests for test/RevisionBoundaries.t.sol:RevisionBoundariesTest\n[PASS] test_hooklessPoolBypassesTheHookFee() (gas: 1792763)\n[PASS] test_managerRoutedETHAndUnsolicitedClaimsAreNotFeeDeposits() (gas: 544591)\n[PASS] test_safeCanWithdrawBothReservesWithoutBuyingOrBurning() (gas: 225949)\n[PASS] test_strayClaimsDoNotDivertRecordedFeeRedemption() (gas: 1067033)\n[PASS] test_zeroLiquidityPriceMoveIsFree() (gas: 141093)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 28.42ms (4.46ms CPU time)\n\nRan 2 tests for test/ReviewRegression.t.sol:ReviewRegressionTest\n[PASS] testFuzz_tinyFeeRounding(uint16,bool,bool,uint16) (runs: 256, μ: 235286, ~: 233993)\n[PASS] test_fourModesInSameUnlockSettleNetAmounts() (gas: 1046425)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 34.07ms (34.14ms CPU time)\n\nRan 7 tests for test/Security.t.sol:SecurityTest\n[PASS] test_constructorCodeChecksAndInvalidFlags() (gas: 6338)\n[PASS] test_directFeeRejectionRollsBackSwapAndBusyGuard() (gas: 621494)\n[PASS] test_exactPermissionsAndFlags() (gas: 29484)\n[PASS] test_feeCallbackCannotReenterRedemption() (gas: 556515)\n[PASS] test_firstInitEveryFieldAndSecondInitRejected() (gas: 904478)\n[PASS] test_noAdministrationEvenForFactoryOrSafe() (gas: 1683978)\n[PASS] test_runtimeHasNoEscapeOpcodes() (gas: 1547379)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 34.79ms (9.50ms CPU time)\n\nRan 5 tests for test/LaunchPolicy.t.sol:FreshManagerTest\n[PASS] testFuzz_firstBuyClaimsBothExactModesAndDecay(bool,uint16) (runs: 256, μ: 483574, ~: 443815)\n[PASS] test_allFourModesMintClaimsInOneUnlockOnEmptyManager() (gas: 1240376)\n[PASS] test_claimAndDirectFeesMixedBeforeRedemption() (gas: 653328)\n[PASS] test_failedRedemptionRestoresClaimsAndCanRetry() (gas: 507350)\n[PASS] test_rejectingVaultDoesNotBlockClaimMintButRedemptionReverts() (gas: 607478)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 39.91ms (42.32ms CPU time)\n\nRan 11 tests for test/Hook.t.sol:HookTest\n[PASS] testFuzz_feeBuyAndSell(uint96,bool,bool) (runs: 256, μ: 246495, ~: 222065)\n[PASS] test_decayAndFullFeeToVault() (gas: 358115)\n[PASS] test_exactModesRespectAmount() (gas: 734176)\n[PASS] test_feeAllFourModes() (gas: 831462)\n[PASS] test_partialBuyExactInput() (gas: 275259)\n[PASS] test_partialBuyExactOutput() (gas: 221269)\n[PASS] test_partialSellExactInput() (gas: 216685)\n[PASS] test_partialSellExactOutput() (gas: 275064)\n[PASS] test_permissionsAndUnauthorizedCallbacks() (gas: 147954)\n[PASS] test_sellsStayAtNormalFee() (gas: 450642)\n[PASS] test_wrongPoolRejected() (gas: 77960)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 40.02ms (44.22ms CPU time)\n\nRan 8 tests for test/Vault.t.sol:VaultTest\n[PASS] testFuzz_fundWithdrawRoundtrip(uint96,bool) (runs: 1000, μ: 229451, ~: 231527)\n[PASS] testFuzz_sameAndCrossWithdrawalReentryBlocked(bool) (runs: 256, μ: 720756, ~: 720667)\n[PASS] test_burnEntireBalanceToDeadWithoutETHMovement() (gas: 360864)\n[PASS] test_forcedETHIncludedAndWithdrawableWithoutAccountingDrift() (gas: 344262)\n[PASS] test_noTokenRescueApprovalOrAlternateDestinationEvenForSafe() (gas: 259508)\n[PASS] test_onlySafeAndMatchingReserve() (gas: 585074)\n[PASS] test_rejectingSafeRollsBackBothWithdrawalsAndCanRetry() (gas: 414171)\n[PASS] test_splitRoundingAndEvents() (gas: 1029898)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 40.02ms (67.56ms CPU time)\n\nRan 5 tests for test/AdversarialFees.t.sol:AdversarialFeesTest\n[PASS] testFuzz_actualSettlementAndEvents(uint96,uint16,bool,bool,bool) (runs: 1000, μ: 323427, ~: 306206)\n[PASS] test_callbackRejectsZeroAndNarrowingOverflow() (gas: 484870)\n[PASS] test_decaySecondAndMinuteBoundaries() (gas: 255759)\n[PASS] test_quoteLeavesSamePriceLiquidityAndLPGrowthAsUnhookedPool() (gas: 1159176)\n[PASS] test_wrongPoolEveryBoundFieldAndUnpairedAfterSwap() (gas: 227501)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 40.12ms (43.00ms CPU time)\n\nRan 1 test for test/LifecycleInvariants.t.sol:LifecycleInvariantTest\n[PASS]\nLifecycleInvariantTest invariants:\n[PASS] invariant_ETHAccountingAndOnlySafeReceivesWithdrawals\n[PASS] invariant_fixedSupplyAndEveryBurnConserved\n LifecycleInvariantTest invariants (runs: 256, calls: 24576, reverts: 0)\n\n╭------------------+----------------------+-------+---------+----------╮\n| Contract         | Selector             | Calls | Reverts | Discards |\n+======================================================================+\n| LifecycleHandler | advance              | 3104  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | donate               | 3108  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | moveAndBurn          | 3180  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | receiver             | 3045  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | redeem               | 3037  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | swap                 | 2978  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | unauthorizedWithdraw | 2975  | 0       | 0        |\n|------------------+----------------------+-------+---------+----------|\n| LifecycleHandler | withdraw             | 3149  | 0       | 0        |\n╰------------------+----------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.58s (2.58s CPU time)\n\nRan 11 test suites in 2.58s (2.87s CPU time): 50 tests passed, 0 failed, 0 skipped (50 total tests)\n","passed":true},{"durationMs":49,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"LifeForceVault.burn()\",\"LifeForceVault.receive()\",\"LifeForceVault.withdrawBuyback(uint256)\",\"LifeForceVault.withdrawInference(uint256)\",\"SovrnHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"SovrnHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"SovrnHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"SovrnHook.quoteNative((address,address,uint24,int24,address),(bool,int256,uint160))\",\"SovrnHook.receive()\",\"SovrnHook.redeemFees()\",\"SovrnHook.unlockCallback(bytes)\",\"SovrnToken.approve(address,uint256)\",\"SovrnToken.transfer(address,uint256)\",\"SovrnToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"README.md\":101,\"foundry.toml\":19,\"launch-attestation.json\":1367,\"launch.json\":31,\"script/PrepareLaunch.s.sol\":32,\"script/attest.py\":110,\"src/HookFlags.sol\":30,\"src/Interfaces.sol\":20,\"src/LifeForceVault.sol\":95,\"src/SovrnHook.sol\":239,\"src/SovrnToken.sol\":48,\"test/AdversarialFees.t.sol\":173,\"test/Hook.t.sol\":128,\"test/Launch.t.sol\":67,\"test/LaunchPolicy.t.sol\":167,\"test/LifecycleInvariants.t.sol\":171,\"test/PoolRouter.sol\":65,\"test/REVIEW.md\":60,\"test/ReviewRegression.t.sol\":68,\"test/RevisionBoundaries.t.sol\":128,\"test/Security.t.sol\":167,\"test/SystemBase.sol\":67,\"test/Token.t.sol\":60,\"test/Vault.t.sol\":212,\"test/mocks/MockERC20.sol\":9,\"test/test_attestation.py\":98},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"d81c1ac6e5bd1956bb25bd9e4c2b8381258e632425a69987d1482fe2d457c849","verifiedTreeHash":"6dd7809085e581128776fba473dacecb436d6a17","verifierVersion":"0.1.0+ad90ce4c"}]}