{"assessments":[],"deployments":[],"fuzz":[],"identity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"interpretation":"Records acceptance and evidence. Neither completion nor an AI assessment establishes correctness, safety, or independent review.","jobId":"f22f7879-f886-4e3f-a792-5e82948a5540","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"40cabcc8573212cb33e1bb7f4697ead09de3a9ed010055a7c3936460ecb68523","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"2fe060d345f278794deed43a81340d83611513024f3d6787e013be177f025fd0","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"83ca2779c8e7c4ab9eb0019e4ece91207cd3603ab9c38eabd6b2b9eefc593254","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"b89279670f5a00f5d26286edee00c97dd158b997031350a01cafe6e0329ad227","skillId":"audit-judge","subagents":4,"tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"b89279670f5a00f5d26286edee00c97dd158b997031350a01cafe6e0329ad227","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"00fff33838ed7092b1c89dd9d484d7dfceb9881c8a82cb1c61651b84901daf99","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"058b31f08aab1a2efcfa155ae356cf94e271e8b75669d6b188cd52b422097b9b","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"e15b2b328157bffd1feaf53e67a52a406432c87fc22c2df6510e16414d949343","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"15b04fc8f60521936b9e2b37c09a804685ec689fc98cd5b04510d986d3106a15","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"15b04fc8f60521936b9e2b37c09a804685ec689fc98cd5b04510d986d3106a15","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"24f8c58dcea5362b84b7a04d35b9bdbb944457b5787c557cf2fea34b03ea1089","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"f2f3661f7c702c35b05243debedb8027a2a8db3d2273e74aeacf529c1d201dfd","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"68506fdcd4ed07ed9dcf40cddecfe9a7d7282e399586d1d75a902eca2f5d692b","skillId":"write-foundry-tests","subagents":4,"tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"68506fdcd4ed07ed9dcf40cddecfe9a7d7282e399586d1d75a902eca2f5d692b","skillId":"write-foundry-tests","state":"accepted"}],"objective":"PROJECT: THE CHEESE VAULT — Smart Contracts V1\n\nBuild a production-quality, fully tested Solidity smart contract system for Ethereum Sepolia (chain ID 11155111).\n\nThis is a NEW confidential project. Do not modify or reuse existing deployed BLOOM contracts.\n\nOBJECTIVE\n\nCreate CHEESE ($CHEESE), a Squeekchain-powered farming ecosystem. CHEESE issuance must follow the actual on-chain SQK issuance schedule, driven by verified Squeek Blocks.\n\n1. OFFICIAL SQUEEKCHAIN INTEGRATION\n\nUse:\nhttps://docs.squeekchain.com/\nhttps://docs.squeekchain.com/llms-full.txt\nhttps://docs.squeekchain.com/assets/deployments/sepolia.json\n\nVerify the current official Sepolia deployment, proxy implementations, chain ID, SqueekCore block counter, SqueekMining issuance schedule, halving rules, launch bonus, and actual minting behavior.\n\nRead and verify on-chain values. Do not invent addresses, ABI functions, or emission parameters.\n\nIf the official documentation and deployed contracts disagree, stop and report the discrepancy before deployment.\n\n2. CHEESE TOKEN\n\nName: CHEESE\nSymbol: CHEESE\nDecimals: 18\nMaximum Supply: 2,100,000,000 CHEESE\n\nMirror SQK's verified premint allocation, mining allowance, launch bonus, halving boundaries, and issuance schedule.\n\nPremint only the allocation permitted by the verified SQK model. Do not premint farming emissions.\n\nCHEESE mining emissions must be minted only against newly eligible, verified Squeek Blocks, without double counting or retroactive minting before CHEESE activation.\n\nRestrict minting to an immutable or tightly controlled emission authority. No arbitrary owner minting.\n\n3. MASTER EMISSION CONTROLLER\n\nBuild a single global CHEESE emission controller using the verified Squeekchain block counter.\n\nUse SQK's exact issuance schedule and halving boundaries.\n\nSupport permissionless checkpointing, bounded processing, accurate halving-crossing calculations, and strict maximum-supply enforcement.\n\nHandle delayed checkpoints, zero block movement, emission completion, and temporarily unavailable Squeekchain data.\n\n4. DYNAMIC POOL ALLOCATION\n\nInitial allocation:\n\nCHEESE Pool: 40% (400 points)\nETH Pool: 20% (200 points)\nsIMD Pool: 20% (200 points)\nFuture Pool Reserve: 20% (200 points)\n\nTotal: 1,000 points.\n\nThe Future Pool Reserve must remain inactive and unminted until assigned to an actual pool. It must never be distributed retroactively.\n\nSupport:\n- addPool\n- setPoolWeight\n- batchSetPoolWeights\n- disablePool\n- enablePool\n- checkpoint\n- getPoolAllocation\n\nAll allocation changes must apply prospectively only.\n\nCheckpoint accrued emission under the previous weights before any allocation change.\n\nUse scalable lazy accounting. Avoid unbounded loops across every pool.\n\nNew pools and new depositors must never receive rewards accrued before their eligibility.\n\n5. FARMING SYSTEM\n\nInitial farms:\n- CHEESE\n- Native ETH\n- sIMD\n\nUse independent, nontransferable 18-decimal internal shares and full-precision accounting.\n\nDeposits: 0% fee.\nWithdrawals: immediately eligible, no cooldown.\nReward claims: separate from principal withdrawals.\n\nWithdrawal fee (Hamster Bite):\n- 15% of gross share-backed withdrawn assets.\n- 85% returned to withdrawer.\n- 14% retained in the same farm, increasing assets per share for remaining depositors.\n- 1% transferred to Treasury.\n- If no shares remain, the full 15% goes to Treasury.\n\nPartial withdrawers may benefit from redistribution through their remaining shares.\n\nProtect against donation inflation, rounding exploits, reentrancy, share-accounting errors, and reward/principal reserve mixing.\n\nWithdrawal fee operation must remain functional even after CHEESE mining rewards end.\n\n6. ADMINISTRATION AND SECURITY\n\nOwner may add pools, adjust future allocation weights, configure Treasury, and pause emissions.\n\nOwner must not be able to confiscate user principal or already accrued rewards.\n\nImplement role separation, event logging, emergency procedures, and appropriate access controls.\n\n7. TESTING\n\nUse Foundry or an equivalent rigorous testing framework.\n\nInclude unit tests, fuzz tests, invariants, and Sepolia integration scripts.\n\nTest:\n- Exact SQK emission matching\n- Halving boundaries\n- Supply cap\n- No double minting\n- Dynamic allocation changes\n- Adding new pools\n- Inactive future reserve\n- No retroactive rewards\n- Deposit and withdrawal accounting\n- 15% Hamster Bite distribution\n- Last depositor withdrawal\n- Reward claim independence\n- Reward emission ending\n- Reentrancy and access control\n- Gas scalability\n\n8. DELIVERABLES\n\nProvide:\n- Complete Solidity source code\n- Full automated tests and results\n- Deployment scripts\n- Verified Squeekchain integration references\n- Contract architecture\n- Security findings\n- README\n- Website integration ABI and configuration\n- IMD Swarm Job ID and explorer URL\n\nDo not fabricate test results, deployment addresses, or verification status.\n\nIMPORTANT EXECUTION RULES\n\nComplete development and local testing autonomously.\n\nDo not deploy or spend gas until all tests pass and the official Squeekchain integration has been verified.\n\nRequest one explicit deployment approval with the exact deployer, owner, Treasury, contract addresses, and estimated Sepolia gas budget before broadcasting deployment transactions.\n\nDo not use Ethereum Mainnet or spend real ETH.\n\nKeep all project information confidential.\n\nBuild the complete Smart Contract system first. Website development will be handled by a separate IMD Swarm Job.","parentJobId":null,"planHash":"2275af523f56f12e5147134dd406935c5e97a2c6c3d43aa18f68bf3998e3c515","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"f22f7879-f886-4e3f-a792-5e82948a5540","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-3097-project-cheese-vault"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51367","feedbackHash":"7ecf1beb3f36b8c516f2aa1c276e0e42d095022231e0133b9c261f5564f6fd94","nodeKey":"audit_economics","submissionHash":"40cabcc8573212cb33e1bb7f4697ead09de3a9ed010055a7c3936460ecb68523","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52615","feedbackHash":"0dd7d99dedc94c76c3ec121d5700fe1dd97b84265ce98344123783a9ab7ae14e","nodeKey":"audit_flow","submissionHash":"2fe060d345f278794deed43a81340d83611513024f3d6787e013be177f025fd0","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52665","feedbackHash":"c7e4973715e59c12ccfa427669d3382abea0512ba12269ecb52182b597c2dab5","nodeKey":"audit_judge","submissionHash":"83ca2779c8e7c4ab9eb0019e4ece91207cd3603ab9c38eabd6b2b9eefc593254","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51374","feedbackHash":"1cba708027cf845fa9e506176228cf9c81e76118f876bc8ea8286831efd24212","nodeKey":"audit_judge","submissionHash":"15b36283a9bce9b88c254b3a6ca77560d39b9aa380edc1431cc3f518438d74ef","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52680","feedbackHash":"77d8edc4833d5a2da0d2773f70ef4b2d63a8bfed670e27e68d4814b1f261e584","nodeKey":"audit_judge","submissionHash":"5fff20e6862a66e73b7261d04b2a70208fe64d107d4d89c8eb64b71e0f000845","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"54149","feedbackHash":"0be1b482ab67f4791e0a8b2ff2aaeb3ab001fc4dc579c6c8b8f0ecaaccca49f0","nodeKey":"audit_math","submissionHash":"00fff33838ed7092b1c89dd9d484d7dfceb9881c8a82cb1c61651b84901daf99","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52138","feedbackHash":"c54b4887592fd1138c3702290a26780a34badc75f022cef0b3abcb91cb836376","nodeKey":"audit_permissions","submissionHash":"058b31f08aab1a2efcfa155ae356cf94e271e8b75669d6b188cd52b422097b9b","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52396","feedbackHash":"fac2bd9b970f450f3efe3f1c2f4ff4b1c9fe6fc485404ed8234991ce6d12a84e","nodeKey":"build_contract_project","submissionHash":"b29b2150bffba095b6456e83862ea93076cf8c5f60cf02fc3e64df6f2bc6efc7","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"54235","feedbackHash":"dff2baa51db786e59a409a49f16cb6432c704dc785d5f20b79ed47029e2dbb28","nodeKey":"build_contract_project","submissionHash":"58e33fbc7d4c3166dd705b3e579d83c7434ef91588c1f376ce57a0352e6a4a35","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52137","feedbackHash":"236e5a7a9df56bccbe823f49003197d530f2601b2286070da0da89f5328adf78","nodeKey":"build_contract_project","submissionHash":"e15b2b328157bffd1feaf53e67a52a406432c87fc22c2df6510e16414d949343","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52599","feedbackHash":"38207d935deb251aaa18bac73d8f2e6d668b18ee92ae4c9e3cee25b2f25a7573","nodeKey":"manifest","submissionHash":"6761efc4875b38d87e66f31e76773c689ec194d87983ba9781a35223c6e20a02","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52701","feedbackHash":"82ca825441816037d1678c59b416c3d28b4d0f2a00479db6acdcb05b96e1770f","nodeKey":"manifest","submissionHash":"24f8c58dcea5362b84b7a04d35b9bdbb944457b5787c557cf2fea34b03ea1089","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52600","feedbackHash":"ae6ff9ef6d1879c06ca72d647af5a29d50907820d3e3e9e02469bbc268f28415","nodeKey":"manifest","submissionHash":"d251f89f27d35127eb0e4d5d81a21b5a32d94d7185a8112e2bacf2ddd617bea7","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52681","feedbackHash":"2a191d030fca2703b2333d1d18c833dde884e77f4d8f10fb3e393d8d13f85338","nodeKey":"write_foundry_tests","submissionHash":"b3a13cdff0669c04f9f01878994106da7145803872c2198bb4917b005d26af09","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52603","feedbackHash":"96c1df7bd5b382f4781e527ab5a800a2a5d8e7dbab54594046c2bb16427f9719","nodeKey":"write_foundry_tests","submissionHash":"544321344773be95daf0bd75a7b28f522f7fc7a25cc907813ee785b2ca5f1efc","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52624","feedbackHash":"54ed33322ab5b6b3e19428f50c29b43861aebeef9cd9260476c1b87bf9d709a4","nodeKey":"write_foundry_tests","submissionHash":"f2f3661f7c702c35b05243debedb8027a2a8db3d2273e74aeacf529c1d201dfd","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"c50be3100e3332a843ad6147a3805a8997cfec9b2ae3fd719ead7a6637777dca","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"06d00e98fbdd1ee9","findings":[{"citation":"resolved","description":"CheeseFarm tracks rewards MasterChef-style with ACC_PRECISION = 1e36 (src/CheeseFarm.sol:42) and shares that are 1000x the asset amount (VIRTUAL_SHARES 1e3 / VIRTUAL_ASSETS 1). `_accrue` adds `amount * 1e36 / totalShares` to `accRewardPerShare` (src/CheeseFarm.sol:281). When totalShares is tiny the accumulator grows enormously: with a single 1-wei deposit (1000 shares) the ETH pool's share of one 40-block Squeek round (about 2.94e22 wei of CHEESE) raises accRewardPerShare to about 2.94e55. Every later product `sharesOf * accRewardPerShare` (deposit at line 171, `_harvestDebt` at line 288, `pendingRewards` at line 232) is a plain checked uint256 multiplication, not a 512-bit mulDiv, so any account whose shares exceed 2^256 / acc (about 3.9e21 shares, i.e. a 3.9 ETH deposit after one round) reverts with panic 0x11. The accumulator never decreases and there is no admin reset, so the farm is permanently unusable for deposits above a threshold that keeps falling while the attacker's 1 wei is the only stake (after 24 rounds, about 4 hours, a 0.5 ETH deposit already reverts). Cost to the attacker: 1 wei plus gas, and being first (or being present whenever totalShares drops to a few shares, since any such window ratchets the accumulator up for good). The same holds for the CHEESE and sIMD farms with 1 wei of their asset. The existing invariant handler swallows deposit reverts in a try/catch, which is why the suite never observed this. Fix: compute every `shares * accRewardPerShare / ACC_PRECISION` with `Math.mulDiv(shares, accRewardPerShare, ACC_PRECISION)` (OpenZeppelin, already imported), or lower ACC_PRECISION to 1e18 and/or enforce a minimum first deposit. The proof below fails on the current code and passes with mulDiv.","line":171,"path":"src/CheeseFarm.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {CheeseToken} from \"src/CheeseToken.sol\";\nimport {CheeseEmissionController} from \"src/CheeseEmissionController.sol\";\nimport {CheeseFarmETH} from \"src/farms/CheeseFarmETH.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Minimal SqueekCore double: certifies rounds and completes blocks.\ncontract TinyCore is ISqueekCore {\n    uint64 public genesisTimestamp;\n    uint64 public settledThroughRound;\n    uint96 public cumulativeBlockUnits;\n    mapping(uint64 => uint96) public unitsAfterRound;\n\n    constructor(uint64 genesis) {\n        genesisTimestamp = genesis;\n    }\n\n    function settle(uint64 round, uint64 newBlocks) external {\n        cumulativeBlockUnits += uint96(newBlocks) * 1_000_000_000;\n        for (uint64 r = settledThroughRound + 1; r <= round; ++r) {\n            unitsAfterRound[r] = cumulativeBlockUnits;\n        }\n        settledThroughRound = round;\n    }\n\n    function state() external view returns (CoreState memory result) {\n        result.settledThroughRound = settledThroughRound;\n        result.cumulativeBlockUnits = cumulativeBlockUnits;\n        result.blockHeight = uint64(cumulativeBlockUnits / 1_000_000_000);\n    }\n\n    function configuration() external view returns (CoreConfiguration memory result) {\n        result.genesisTimestamp = genesisTimestamp;\n        result.roundDuration = 600;\n        result.blockUnitsPerBlock = 1_000_000_000;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256)\n        external\n        view\n        returns (RoundPage memory result)\n    {\n        result.certifiedThroughRound = throughRound;\n        result.start.cumulativeBlockUnits = unitsAfterRound[afterRound];\n        result.end.cumulativeBlockUnits = unitsAfterRound[throughRound];\n    }\n\n    function blockInfo(uint64) external pure returns (BlockInfo memory) {\n        revert(\"BlockNotSettled\");\n    }\n\n    function paused() external pure returns (bool) {\n        return false;\n    }\n}\n\n/// @notice A 1-wei first depositor makes `accRewardPerShare` grow by amount * 1e36 / 1000 per pull.\n///         After one Squeek round the ETH farm refuses every deposit of a few ETH: `sharesOf * accRewardPerShare`\n///         in CheeseFarm._deposit overflows uint256 and reverts. The accumulator never decreases, so the farm\n///         is permanently unusable for ordinary deposits. Fails on the current code; passes once the\n///         share * accumulator products use a 512-bit mulDiv (or ACC_PRECISION is lowered).\ncontract AccOverflowTest is Test {\n    uint64 internal constant GENESIS = 1_793_735_400;\n\n    address internal owner = makeAddr(\"owner\");\n    address internal treasury = makeAddr(\"treasury\");\n    address internal attacker = makeAddr(\"attacker\");\n    address internal victim = makeAddr(\"victim\");\n\n    TinyCore internal core;\n    CheeseEmissionController internal controller;\n    CheeseToken internal cheese;\n    CheeseFarmETH internal ethFarm;\n\n    function setUp() public {\n        vm.warp(GENESIS + 100 * 600 + 17);\n        core = new TinyCore(GENESIS);\n        controller = new CheeseEmissionController(owner, address(core), treasury);\n        cheese = new CheeseToken(owner, owner, owner, address(controller));\n        ethFarm = new CheeseFarmETH(owner, address(controller));\n        vm.startPrank(owner);\n        controller.bindToken(address(cheese));\n        controller.addPool(address(ethFarm), 200);\n        controller.activate();\n        vm.stopPrank();\n        // certify through startRound - 1 with zero blocks so h0 == 0\n        core.settle(controller.startRound() - 1, 0);\n        controller.checkpoint();\n        assertTrue(controller.startCaptured());\n        vm.deal(attacker, 1 ether);\n        vm.deal(victim, 100 ether);\n    }\n\n    function test_oneWeiFirstDepositorBricksDepositsAfterOneRound() public {\n        // 1. attacker is the first depositor with 1 wei -> 1000 shares\n        vm.prank(attacker);\n        ethFarm.deposit{value: 1}();\n        assertEq(ethFarm.totalShares(), 1000);\n\n        // 2. one Squeek round of 40 blocks is certified (about ten minutes)\n        core.settle(core.settledThroughRound() + 1, 40);\n\n        // 3. the victim deposits 10 ETH. Expected: shares minted, deposit accepted.\n        //    Actual: arithmetic overflow in `sharesOf[msg.sender] * accRewardPerShare` (panic 0x11).\n        vm.prank(victim);\n        uint256 shares = ethFarm.deposit{value: 10 ether}();\n        assertGt(shares, 0, \"victim should receive shares\");\n        assertEq(ethFarm.totalAssets(), 10 ether + 1);\n    }\n\n    function test_thresholdDropsWithEveryRound() public {\n        vm.prank(attacker);\n        ethFarm.deposit{value: 1}();\n        // 24 rounds (~4 hours) with the 1-wei staker alone\n        for (uint256 i; i < 24; ++i) {\n            core.settle(core.settledThroughRound() + 1, 40);\n            controller.checkpoint();\n        }\n        // even 0.5 ETH is refused now\n        vm.prank(victim);\n        uint256 shares = ethFarm.deposit{value: 0.5 ether}();\n        assertGt(shares, 0, \"0.5 ETH deposit should succeed\");\n    }\n}","reproduction":"Fresh deployment, ETH farm registered with 200 points, controller activated, h0 captured. 1) attacker: ethFarm.deposit{value: 1}() -> totalShares = 1000. 2) SqueekCore certifies one round of 40 blocks (issued(40) = 1.4698e23; pool share 200/1000 = 2.9397e22). 3) victim: ethFarm.deposit{value: 10 ether}(). Expected: ~1e22 shares minted, deposit accepted. Actual: _accrue pulls 2.9397e22 and sets accRewardPerShare = 2.9397e22 * 1e36 / 1000 = 2.9397e55; then sharesOf (1e22) * 2.9397e55 = 2.94e77 > 2^256 (1.158e77) -> panic 0x11, deposit reverts. Max deposit that still works after 1 round: type(uint256).max / acc / 1000 = ~3.9e18 wei. After 24 rounds (test 2) even 0.5 ether reverts. The threshold only falls over time; no function can lower accRewardPerShare.","severity":"high","snippet":"        _rewardDebt[msg.sender] = (sharesOf[msg.sender] * accRewardPerShare) / ACC_PRECISION;","title":"Reward accumulator at 1e36 precision overflows share*acc products: a 1-wei first depositor permanently bricks ordinary deposits"},{"citation":"resolved","description":"The farm closes deposits while emissions are paused precisely so that the catch-up checkpoint after the pause does not pay blocks certified earlier to depositors who were not staked then (comment at src/CheeseFarm.sol:157-158). The same catch-up happens when `_sync` fails for the other two reasons it can fail: SqueekCore.state()/roundPage() reverting (CoreUnavailable) or a height regression (CoreRegression). In those cases `pullRewards` runs `_sync()` best-effort (src/CheeseEmissionController.sol:298) and returns 0, `_deposit` proceeds, mints shares to the newcomer at the stale accRewardPerShare, and the next successful checkpoint prices all the blocks certified before the deposit and spreads them over the enlarged share base. The brief's rule 'new depositors must never receive rewards accrued before their eligibility' is broken for every outage window, and the outage is one the project itself anticipates (README: a changed ABI makes checkpoint() return false 'until the owner pauses'). A depositor who watches CoreUnavailable events can size a deposit to take almost all of the backlog from the stakers who earned it. Fix: in `_deposit`, require a successful sync, e.g. `(bool ok,) = controller.checkpoint(); if (!ok) revert SyncRequired();` (or have `_accrue` surface the sync result), mirroring the EmissionsPaused rule; withdrawals and claims can keep the best-effort behaviour.","line":159,"path":"src/CheeseFarm.sol","reproduction":"activateWithHistory(0); alice deposits 1 ether into the ETH farm; 100 blocks are certified (not yet checkpointed); SqueekCore.state() starts reverting (mock setUnavailable(true)); controller.checkpoint() returns (false, 0); bob deposits 99 ether (succeeds; no revert path checks the failed sync); SqueekCore recovers; controller.checkpoint(). Pool emission for the 100 blocks = issued(100)*200/1000 = 73,491.414 CHEESE (73491414000220262451165 wei). Expected: alice pending = 73,491 CHEESE, bob pending = 0. Actual (test/scratch/OutageRetro.t.sol): bob pending = 72,756.4998 CHEESE (72756499860218059826653), alice pending = 734.914 CHEESE. Bob received 99% of emission for blocks certified while alice was the only staker.","severity":"medium","snippet":"        if (controller.emissionsPaused()) revert EmissionsPausedDepositsClosed();","title":"Deposits stay open while the Squeek read fails, so a late depositor captures emission for blocks certified before their deposit"},{"citation":"resolved","description":"The 14% retained part of a withdrawal stays in totalAssets and is spread over all shares, including the VIRTUAL_SHARES = 1e3 that back VIRTUAL_ASSETS = 1 wei. Normally the virtual shares are a negligible fraction, but when the real share count is small the virtual holder captures most of the retained fee, and that value can never be withdrawn by anyone: `_surplus` excludes totalAssets, so skimSurplus cannot reach it, and a new depositor's shares are priced at the inflated rate so they get back exactly what they put in. A sole depositor who withdraws all but one share therefore (a) strands 14% of their deposit permanently, (b) raises the price per share to retained/1001, making deposits below that revert with ZeroShares and costing every later depositor up to one share of rounding, and (c) when the last real share leaves, leaves the whole inflated totalAssets locked with totalShares == 0. The actor pays 15% of the deposit, so this is griefing rather than profit, but the brief asks for protection against donation inflation and rounding exploits and the stranded value is unrecoverable by design. Mitigations: credit the retained fee only to real shares (e.g. add it to totalAssets scaled by totalShares/(totalShares+VIRTUAL_SHARES) and send the virtual part to Treasury), route retained to Treasury when totalShares is below a floor, or enforce a minimum deposit/share count.","line":188,"path":"src/CheeseFarm.sol","reproduction":"Empty ETH farm. alice deposits 1000 ether -> 1e24 shares. alice withdraws 1e24 - 1 shares: paid 850 ETH, treasury 10 ETH, retained 140 ETH; state totalAssets = 140e18, totalShares = 1, price per share = 139860139860139860 wei (0.1399 ETH). bob: convertToShares(0.1 ether) = 0 -> deposit of 0.1 ETH reverts ZeroShares; deposit of 1 ether mints 7 shares backed by 0.979166 ETH (2.1% rounding loss to the pool). alice withdraws her last share; bob withdraws his 7 shares (totalShares -> 0, full 15% to Treasury) and is paid 0.8324 ETH. Final state (test/scratch/Probe.t.sol test_probeInflationResidual): totalAssets = 139900399583865323688 wei (139.9 ETH) with totalShares = 0, surplus() = 0, no function can move it; the next depositor of D wei receives shares worth exactly D, so the 139.9 ETH is owned by the virtual shares forever. Expected: the retained fee benefits remaining real depositors (or Treasury when none remain); actual: it is lost.","severity":"low","snippet":"        uint256 retained = (gross * RETAINED_FEE_BPS) / BPS;","title":"Retained Hamster Bite accrues to the 1e3 virtual shares; with few real shares it is stranded forever and inflates the share price"}],"hash":"00fff33838ed7092b1c89dd9d484d7dfceb9881c8a82cb1c61651b84901daf99","nodeId":"0446dfdc-910b-45a6-ba76-c8fa013dbe35","outcome":"completed","summary":"Everything validated: three findings recorded with exact snippets matching the tree, the high finding carries a self-contained proof that fails on the current code, and all 51 entry points have coverage rows. No source files were modified; only `.imd-findings.json` and the scratch tests were written.\n\n## Summary\n\nI audited the math of the CHEESE contracts against the Math Precision, Boundary and Numerical Gap guides. The baseline suite of 70 tests passes, and the results are in `.imd-findings.json`.\n\n**Findings**\n\n1. **High. Reward accumulator overflow bricks deposits.** `ACC_PRECISION` is 1e36 and shares are 1000x the asset amount. A first depositor of 1 wei holds 1000 shares, so one Squeek round of ETH pool emission pushes `accRewardPerShare` to about 2.9e55. The product `sharesOf * accRewardPerShare` in `_deposit` is a plain checked multiply, so a 10 ETH deposit then reverts with panic 0x11. The threshold keeps falling while the stake stays tiny, the accumulator never decreases, and there is no reset. The proof test under `test/scratch/AccOverflow.t.sol` fails on the current code. The fix is to use `Math.mulDiv` for every share-times-accumulator product, or lower the precision.\n\n2. **Medium. Retroactive rewards during a Squeek read failure.** Deposits are refused while emissions are paused so the catch-up checkpoint cannot pay newcomers, but the same catch-up happens when `_sync` fails because SqueekCore is unreadable or regressed. In that window deposits proceed. In the reproduction, a depositor arriving after 100 certified blocks collected 72,756 CHEESE of the 73,491 that the sole earlier staker had earned. The fix is to require a successful sync in `_deposit`, mirroring the pause rule.\n\n3. **Low. Retained fee stranded on virtual shares.** The 14% retained share is credited to all shares including the 1000 virtual ones. A sole depositor who exits all but one share leaves 14% of the deposit owned by the virtual shares. In the probe, 139.9 ETH ended up permanently locked with zero shares, unreachable by users, Treasury or `skimSurplus`, and the inflated price made deposits under 0.14 ETH revert. The actor pays 15% to do this, so it is griefing only.\n\n**What holds in my area.** The issuance library is exact at every halving and never exceeds the allowance. The checkpoint pricing is grouping-independent, the clip never binds, and completion triggers at 3H. The `roundPage(r, r, 0)` point read matches the documented Squeekchain semantics. Fee pieces always sum to gross, `gross` never exceeds `totalAssets`, and per-point and per-share floors only under-mint by dust.\n\n**Test gap worth noting.** The invariant handler wraps deposits in try/catch and the fuzz tests floor deposits at 1e9 wei, so the suite never exercises the tiny-stake regime where the overflow lives.","treeHash":null,"usage":{"cachedInputTokens":1832251,"cost":{"source":"runtime","usdMicros":6516675},"inputTokens":450,"model":"claude-fable-5-1","outputTokens":55663,"runtime":"claude","turns":47,"wallClockMs":800022}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"62491201fe455e89","findings":[{"citation":"resolved","description":"CheeseFarm._deposit guards one of the two 'sync failed' branches but not the other. When emissions are paused the deposit reverts (src/CheeseFarm.sol:159) with the comment that catch-up emission must stay away from depositors who were not there when the blocks were certified. When _sync fails because squeekCore.state()/roundPage() revert (CoreUnavailable, src/CheeseEmissionController.sol:471-474 and 482-485) or because the reported height regressed (CoreRegression, lines 494-497), pullRewards swallows the failure (line 298: '_sync(); // best effort'), _accrue() receives 0, and the deposit proceeds and mints shares at the current accRewardPerShare. The blocks certified before that deposit are still unpriced; the next successful checkpoint prices them and spreads them over all shares that exist then, including the new depositor's. README.md:77-78 states 'Allocation changes and deposits require a successful sync'; the code enforces this only for allocation changes (_requireSync) and for the paused branch. This breaks the brief's rule that new depositors never receive rewards accrued before their eligibility, and it is asymmetric with withdrawals during the same outage, which forgo the unpriced blocks (harvested only through the last successful checkpoint), so value moves from stakers who stay or leave to whoever deposits while the Core is unreadable. Trigger: a Squeek Core outage or height regression (the brief requires handling 'temporarily unavailable Squeekchain data'); amplifier: any unprivileged depositor reacting to a CoreUnavailable/CoreRegression event or to pendingCheckpoint() reporting readable == false. Fix: in _deposit require a successful sync, e.g. call controller.checkpoint() (or a pullRewards variant that returns the sync flag) and revert with a SyncRequired-style error when it returns false, mirroring _requireSync. The existing suite never deposits while the Core is unavailable, so the gap is untested.","line":159,"path":"src/CheeseFarm.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {CheeseToken} from \"src/CheeseToken.sol\";\nimport {CheeseEmissionController} from \"src/CheeseEmissionController.sol\";\nimport {CheeseFarmETH} from \"src/farms/CheeseFarmETH.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\nimport {SqueekIssuance} from \"src/libraries/SqueekIssuance.sol\";\n\n/// @dev Minimal SqueekCore double: certifies rounds, completes blocks, and can be made unreadable.\ncontract OutageCore is ISqueekCore {\n    uint64 public genesisTimestamp;\n    uint64 public settledThroughRound;\n    uint96 public cumulativeBlockUnits;\n    bool public unavailable;\n    mapping(uint64 => uint96) public unitsAfterRound;\n\n    constructor(uint64 genesis) {\n        genesisTimestamp = genesis;\n    }\n\n    function setUnavailable(bool v) external {\n        unavailable = v;\n    }\n\n    function settle(uint64 round, uint64 newBlocks) external {\n        cumulativeBlockUnits += uint96(newBlocks) * 1_000_000_000;\n        for (uint64 r = settledThroughRound + 1; r <= round; ++r) {\n            unitsAfterRound[r] = cumulativeBlockUnits;\n        }\n        settledThroughRound = round;\n    }\n\n    function state() external view returns (CoreState memory r) {\n        require(!unavailable, \"core unavailable\");\n        r.settledThroughRound = settledThroughRound;\n        r.cumulativeBlockUnits = cumulativeBlockUnits;\n        r.blockHeight = uint64(cumulativeBlockUnits / 1_000_000_000);\n    }\n\n    function configuration() external view returns (CoreConfiguration memory r) {\n        require(!unavailable, \"core unavailable\");\n        r.genesisTimestamp = genesisTimestamp;\n        r.roundDuration = 600;\n        r.blockUnitsPerBlock = 1_000_000_000;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256) external view returns (RoundPage memory r) {\n        require(!unavailable, \"core unavailable\");\n        require(throughRound <= settledThroughRound, \"bounds\");\n        r.certifiedThroughRound = throughRound;\n        r.start.cumulativeBlockUnits = unitsAfterRound[afterRound];\n        r.end.cumulativeBlockUnits = unitsAfterRound[throughRound];\n    }\n\n    function blockInfo(uint64) external pure returns (BlockInfo memory) {\n        revert(\"BlockNotSettled\");\n    }\n\n    function paused() external pure returns (bool) {\n        return false;\n    }\n}\n\n/// @notice A depositor who joins while SqueekCore is unreadable receives emission for Squeek blocks that were\n///         certified before the deposit. Deposits are closed while emissions are paused (the same \"sync failed\"\n///         condition) but not when the sync fails because the Core is unreadable or regressed.\ncontract DepositDuringOutageTest is Test {\n    uint64 constant GENESIS = 1_793_735_400;\n\n    address owner = makeAddr(\"owner\");\n    address treasury = makeAddr(\"treasury\");\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n\n    OutageCore core;\n    CheeseEmissionController controller;\n    CheeseToken cheese;\n    CheeseFarmETH farm;\n\n    function setUp() public {\n        vm.warp(GENESIS + 100 * 600 + 17);\n        core = new OutageCore(GENESIS);\n        controller = new CheeseEmissionController(owner, address(core), treasury);\n        cheese = new CheeseToken(owner, owner, owner, address(controller));\n        farm = new CheeseFarmETH(owner, address(controller));\n        vm.startPrank(owner);\n        controller.bindToken(address(cheese));\n        controller.addPool(address(farm), 200);\n        controller.activate();\n        vm.stopPrank();\n        // certify through startRound - 1 with zero completed blocks so h0 == 0\n        core.settle(controller.startRound() - 1, 0);\n        controller.checkpoint();\n        assertTrue(controller.startCaptured());\n        vm.deal(alice, 10 ether);\n        vm.deal(bob, 10 ether);\n    }\n\n    function test_depositorJoiningDuringCoreOutageGetsNoEarlierEmission() public {\n        // alice is the only staker while 10 Squeek blocks are certified\n        vm.prank(alice);\n        farm.deposit{value: 1 ether}();\n        core.settle(core.settledThroughRound() + 1, 10);\n\n        // the Core becomes unreadable before anyone checkpoints; bob deposits during the outage\n        core.setUnavailable(true);\n        vm.prank(bob);\n        (bool ok,) = address(farm).call{value: 1 ether}(abi.encodeWithSignature(\"deposit()\"));\n        // whether the deposit was accepted or refused, bob must never earn from blocks 1..10\n\n        core.setUnavailable(false);\n        controller.checkpoint();\n\n        uint256 poolEmission = (SqueekIssuance.issued(10) * 200) / 1000;\n        uint256 alicePending = farm.pendingRewards(alice);\n        uint256 bobPending = farm.pendingRewards(bob);\n        emit log_named_uint(\"deposit accepted\", ok ? 1 : 0);\n        emit log_named_uint(\"alice pending\", alicePending);\n        emit log_named_uint(\"bob pending\", bobPending);\n        assertEq(bobPending, 0, \"bob earned emission for blocks certified before his deposit\");\n        assertEq(alicePending, poolEmission, \"alice should receive the whole pool emission for blocks 1..10\");\n    }\n}","reproduction":"State: controller activated, ETH pool weight 200, h0 captured at 0, alice is the only staker with 1 ether. 1) Core certifies one round completing 10 Squeek blocks; nobody checkpoints. 2) Core becomes unreadable (state() reverts). 3) bob calls CheeseFarmETH.deposit{value: 1 ether}(). Expected: revert (as on the paused branch) or zero share of blocks 1..10. Actual: deposit succeeds; bob receives 1e21 shares at an unchanged accRewardPerShare. 4) Core readable again; anyone calls controller.checkpoint(), pricing issued(10)*200/1000 = 7,349,141,400,022,026,245,116 wei CHEESE to the pool. 5) farm.pendingRewards(alice) == 3,674,570,700,011,013,122,558 and farm.pendingRewards(bob) == 3,674,570,700,011,013,122,558: bob takes half of the emission for blocks certified before his deposit. Expected alice 7,349,141,400,022,026,245,116 and bob 0. The same sequence with a Core height regression instead of a revert gives the same result. The proof test fails on the current code with 'bob earned emission for blocks certified before his deposit: 3674570700011013122558 != 0'.","severity":"medium","snippet":"        if (controller.emissionsPaused()) revert EmissionsPausedDepositsClosed();\n\n        _accrue();\n        _harvestDebt(msg.sender);","title":"Deposits stay open when the checkpoint fails for any reason other than pause, so a depositor who joins during a SqueekCore outage or regression receives emission for blocks certified before the deposi"},{"citation":"resolved","description":"_sync relies on try/catch to make Squeekchain failures non-blocking: pullRewards (src/CheeseEmissionController.sol:298) calls _sync() 'best effort: withdrawals must never be blocked by Squeekchain availability', and README.md:172-174 says a changed ABI would make checkpoint() return false. Solidity's try/catch only catches a revert of the external call; when the call succeeds but the returned bytes cannot be decoded as the declared struct (fewer words than CoreState's 8, empty return data, or a call to an address without code, since 0.8.10 no longer does an extcodesize check when return data is expected), the decoder raises a Panic in the calling contract, which no catch clause receives. The same applies to the roundPage() call at line 480 and to pendingCheckpoint() (lines 384 and 393), which is read by previewPoolEmission and pendingRewards. Because every CheeseFarm.withdraw and claim runs _accrue() -> controller.pullRewards -> _sync before paying, a SqueekCore upgrade that changes the CoreState layout, or an implementation removal behind the proxy ('testnet-only, in active development' per README), makes all user withdrawals and reward claims revert; deposits also revert. The owner can restore withdrawals by pauseEmissions(), because _sync returns at line 466 before reading the Core; nothing else works and the controller is immutable, so an unresponsive or renounced owner leaves principal locked. This defect is outside the permissions area but is reported because it blocks user access to principal. Fix: read the Core through a low-level staticcall (address(squeekCore).staticcall(abi.encodeCall(ISqueekCore.state, ()))) and check success and returndata.length (>= 8*32 for CoreState; roundPage is dynamic, so check the minimum length before abi.decode), treating any failure as 'unavailable'. The existing suite only simulates unavailability with a reverting mock (MockSqueekCore.setUnavailable), never with malformed return data or a code-less address.","line":469,"path":"src/CheeseEmissionController.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {CheeseToken} from \"src/CheeseToken.sol\";\nimport {CheeseEmissionController} from \"src/CheeseEmissionController.sol\";\nimport {CheeseFarmETH} from \"src/farms/CheeseFarmETH.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Core double whose state() can be switched to return data the controller's ABI cannot decode\n///      (an upgraded proxy with a changed CoreState layout, or a proxy whose implementation was removed).\ncontract ShapeShiftCore {\n    uint64 public genesisTimestamp;\n    uint64 public settledThroughRound;\n    uint96 public cumulativeBlockUnits;\n    uint8 public mode; // 0 = normal, 1 = short return data, 2 = empty return data\n    mapping(uint64 => uint96) public unitsAfterRound;\n\n    constructor(uint64 genesis) {\n        genesisTimestamp = genesis;\n    }\n\n    function setMode(uint8 m) external {\n        mode = m;\n    }\n\n    function settle(uint64 round, uint64 newBlocks) external {\n        cumulativeBlockUnits += uint96(newBlocks) * 1_000_000_000;\n        for (uint64 r = settledThroughRound + 1; r <= round; ++r) {\n            unitsAfterRound[r] = cumulativeBlockUnits;\n        }\n        settledThroughRound = round;\n    }\n\n    function configuration() external view returns (ISqueekCore.CoreConfiguration memory r) {\n        r.genesisTimestamp = genesisTimestamp;\n        r.roundDuration = 600;\n        r.blockUnitsPerBlock = 1_000_000_000;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256)\n        external\n        view\n        returns (ISqueekCore.RoundPage memory r)\n    {\n        r.certifiedThroughRound = throughRound;\n        r.start.cumulativeBlockUnits = unitsAfterRound[afterRound];\n        r.end.cumulativeBlockUnits = unitsAfterRound[throughRound];\n    }\n\n    // state() is served by the fallback so the return shape can change after \"an upgrade\"\n    fallback(bytes calldata) external returns (bytes memory) {\n        if (mode == 1) return abi.encode(uint256(1), uint256(2)); // 2 words, the decoder needs 8\n        if (mode == 2) return \"\";\n        ISqueekCore.CoreState memory s;\n        s.settledThroughRound = settledThroughRound;\n        s.cumulativeBlockUnits = cumulativeBlockUnits;\n        s.blockHeight = uint64(cumulativeBlockUnits / 1_000_000_000);\n        return abi.encode(s);\n    }\n}\n\n/// @notice `_sync` catches reverts from SqueekCore but not return-data decoding failures; those propagate out of\n///         pullRewards and block every farm withdraw and claim until the owner pauses emissions.\ncontract MalformedCoreTest is Test {\n    uint64 constant GENESIS = 1_793_735_400;\n    address owner = makeAddr(\"owner\");\n    address treasury = makeAddr(\"treasury\");\n    address alice = makeAddr(\"alice\");\n\n    ShapeShiftCore core;\n    CheeseEmissionController controller;\n    CheeseToken cheese;\n    CheeseFarmETH farm;\n\n    function setUp() public {\n        vm.warp(GENESIS + 100 * 600 + 17);\n        core = new ShapeShiftCore(GENESIS);\n        controller = new CheeseEmissionController(owner, address(core), treasury);\n        cheese = new CheeseToken(owner, owner, owner, address(controller));\n        farm = new CheeseFarmETH(owner, address(controller));\n        vm.startPrank(owner);\n        controller.bindToken(address(cheese));\n        controller.addPool(address(farm), 200);\n        controller.activate();\n        vm.stopPrank();\n        core.settle(controller.startRound() - 1, 0);\n        controller.checkpoint();\n        assertTrue(controller.startCaptured());\n        vm.deal(alice, 10 ether);\n        vm.prank(alice);\n        farm.deposit{value: 1 ether}();\n    }\n\n    function test_shortReturnData_withdrawStillWorks() public {\n        core.setMode(1);\n        uint256 half = farm.sharesOf(alice) / 2;\n        vm.prank(alice);\n        farm.withdraw(half);\n    }\n\n    function test_emptyReturnData_withdrawStillWorks() public {\n        core.setMode(2);\n        uint256 half = farm.sharesOf(alice) / 2;\n        vm.prank(alice);\n        farm.withdraw(half);\n    }\n\n    function test_coreWithoutCode_withdrawAndClaimStillWork() public {\n        vm.etch(address(core), \"\");\n        uint256 half = farm.sharesOf(alice) / 2;\n        vm.prank(alice);\n        farm.withdraw(half);\n        vm.prank(alice);\n        farm.claim(alice);\n    }\n}","reproduction":"State: controller activated with h0 captured, ETH pool weight 200, alice holds 1e21 shares (1 ether). 1) The SqueekCore proxy starts returning 2 ABI words from state() (or empty return data, or its code is removed; vm.etch(core, '') in the proof). 2) alice calls CheeseFarmETH.withdraw(5e20). Expected (per the pullRewards comment and the README): _sync returns (false, 0), CoreUnavailable is emitted, and alice is paid 0.425 ether minus the Hamster Bite split. Actual: the whole transaction reverts with an uncaught Panic from the return-data decoder inside _sync (trace: CheeseFarmETH::withdraw -> CheeseEmissionController::pullRewards -> EvmError: Revert). controller.checkpoint() and farm.claim(alice) revert the same way, and pendingRewards(alice) reverts. The three proof tests fail on the current code with 'EvmError: Revert' inside withdraw.","severity":"medium","snippet":"        try squeekCore.state() returns (ISqueekCore.CoreState memory result) {\n            s = result;\n        } catch (bytes memory reason) {\n            emit CoreUnavailable(reason);\n            return (false, 0);\n        }","title":"Return-data decoding failures from SqueekCore are not caught by try/catch, so a changed CoreState layout or a code-less proxy reverts pullRewards and blocks every farm withdraw and claim until the own"},{"citation":"resolved","description":"The brief requires 'no arbitrary owner minting' and the token enforces that only the controller mints (src/CheeseToken.sol:67). The controller, however, mints to whatever address a pool's farm is (line 307) and the farm chooses the hasStakers flag (line 295). addPool only checks that the candidate returns this controller from emissionController() and false from registered(); nothing checks that it is a CheeseFarm or holds any principal. The owner can therefore add a contract they control with weight up to the free reserve (200 points at launch) or, after setPoolWeight/batchSetPoolWeights lowers the real farms to 0, with all 1000 points, and have it call pullRewards(poolId, true) to receive every future CHEESE emission. This is within the owner powers the brief grants (add pools, adjust future allocation weights) and applies prospectively only, so it is not a permission bypass and user principal and already-accrued rewards are untouched; it is recorded as a trust assumption the README's role table ('the owner cannot mint') understates. Related owner powers worth documenting the same way: renounceOwnership() (inherited from Ownable, callable on the controller and each farm) while emissions are paused leaves emissions and deposits permanently closed since only the owner can unpause; the default launch arguments make the owner the Treasury and all three premint recipients (README.md:133-141). Mitigation if desired: a multisig/timelock owner, and optionally restricting addPool to farms whose code hash is one of the three farm implementations.","line":223,"path":"src/CheeseEmissionController.sol","reproduction":"State: launched system, owner key. 1) Owner deploys contract X with emissionController() returning the controller, registered() returning false, onPoolRegistered(uint256) storing the id, and pull() calling controller.pullRewards(id, true). 2) Owner calls controller.batchSetPoolWeights([0,1,2],[0,0,0]) then controller.addPool(X, 1000). 3) Any later checkpoint prices Squeek blocks and X.pull() mints 100% of the new emission to X (cheese.balanceOf(X) increases by issued(h)-issued(h_prev) per checkpoint). Expected by the brief's 'no arbitrary owner minting': owner cannot direct emission to an address outside the farms. Actual: owner can, prospectively, bounded by the SQK schedule and MINING_ALLOWANCE. No proof test is attached because this is an intended privilege, not a defect.","severity":"info","snippet":"        if (ICheeseFarm(farm).emissionController() != address(this) || ICheeseFarm(farm).registered()) {\n            revert FarmMismatch(farm);\n        }","title":"Trust assumption: the owner can register any contract that answers emissionController()/registered() as a pool and route emission to it, which is an indirect minting path bounded only by the SQK sched"}],"hash":"058b31f08aab1a2efcfa155ae356cf94e271e8b75669d6b188cd52b422097b9b","nodeId":"2c6d8d0a-e2b5-4cd2-811b-5d540e7da419","outcome":"completed","summary":"Findings file is complete and validated: all three snippets match the tree, both proof tests fail on the current code for the stated reason, and no repository file outside `.imd-findings.json` and `test/scratch/` was touched.\n\n## Summary\n\nI audited the permissions area (Access Control, Trust Gap, Asymmetry guides) of the CHEESE system and recorded three findings with a 54-row coverage record in `.imd-findings.json`.\n\n**Finding 1, medium, in area (asymmetry between the two \"sync failed\" branches).** `CheeseFarm._deposit` refuses deposits while emissions are paused so that catch-up emission cannot reach newcomers, but it accepts deposits when the checkpoint fails because SqueekCore is unreadable or its height regressed. A depositor who joins during such an outage then shares the emission for blocks certified before the deposit. In the proof, Bob deposits during the outage and ends with the same pending reward as Alice, who was the only staker when the ten blocks were certified. The README's claim that deposits require a successful sync is not enforced. Proof attached.\n\n**Finding 2, medium, outside strict area but blocks principal.** The Core reads rely on try/catch, which does not catch return-data decoding failures. If the SqueekCore proxy's state struct changes shape, returns empty data, or loses its code, `pullRewards` reverts, and with it every farm `withdraw`, `claim`, `checkpoint` and `pendingRewards`. The owner can unblock withdrawals by pausing emissions, which is why this is medium rather than high. Proof attached with three variants.\n\n**Finding 3, info, trust assumption.** The owner can register any contract that answers `emissionController()` and `registered()` as a pool and route up to all 1000 points of future emission to it. This is within the brief's owner powers and is prospective only, but the README's \"the owner cannot mint\" understates it. Also notes `renounceOwnership` while paused and the launch defaults that make the owner the Treasury and all premint recipients.\n\n**What holds.** Every owner, guardian, controller-only and token-authority guard was traced and behaves as intended. Two-step ownership, once-only binding and activation, pool weight bounds, settlement before every allocation change, prospective enable and add, the farm hook restricted to the controller, and the owner's inability to touch principal or accrued rewards all check out. Reentrancy via Core views is impossible since they are static calls.\n\n**Not reached.** I did not exhaust the economics or math lanes beyond what the trust-gap seams required, and I did not run the Sepolia verification script.","treeHash":null,"usage":{"cachedInputTokens":1418793,"cost":{"source":"runtime","usdMicros":5370252},"inputTokens":386,"model":"claude-fable-5-1","outputTokens":42838,"runtime":"claude","turns":46,"wallClockMs":678813}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"13603a08ada71730","findings":[{"citation":"resolved","description":"Merged from audit_flow, audit_economics and audit_math. ACC_PRECISION is 1e36 and _accrue adds amount * 1e36 / totalShares (line 281). A 1-wei deposit into an empty farm mints 1000 shares (virtual offset), so the accumulator gains about 2.9e55 per 40-block round on the ETH pool. Every later shares*acc product is a plain checked multiplication: _deposit line 171, withdraw line 193, pendingRewards line 232, _harvestDebt line 288. Any account whose shares exceed 2^256/acc reverts with panic 0x11. The accumulator never falls and no function resets it, so the per-account ceiling is permanent. It works at any moment a farm is empty while emission runs, on all three farms, and costs 1 wei plus gas; the griefer also collects the whole pool emission while alone. Scope note: the flow specialist's claim that existing depositors' principal freezes is not practically reachable. A staker's S*acc grows by at most emission*1e36 (about 3.8e62 for a 20% pool) because totalShares >= S, so only an account within about 3e-15 of the 2^256 bound at entry could overflow later. The harm is the permanent deposit ceiling. The invariant handlers swallow deposit reverts, which is why the suite never saw this.","line":171,"path":"src/CheeseFarm.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {CheeseToken} from \"src/CheeseToken.sol\";\nimport {CheeseEmissionController} from \"src/CheeseEmissionController.sol\";\nimport {CheeseFarmETH} from \"src/farms/CheeseFarmETH.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Minimal SqueekCore double: certifies rounds and completes blocks.\ncontract TinyCore is ISqueekCore {\n    uint64 public genesisTimestamp;\n    uint64 public settledThroughRound;\n    uint96 public cumulativeBlockUnits;\n    mapping(uint64 => uint96) public unitsAfterRound;\n\n    constructor(uint64 genesis) {\n        genesisTimestamp = genesis;\n    }\n\n    function settle(uint64 round, uint64 newBlocks) external {\n        cumulativeBlockUnits += uint96(newBlocks) * 1_000_000_000;\n        for (uint64 r = settledThroughRound + 1; r <= round; ++r) {\n            unitsAfterRound[r] = cumulativeBlockUnits;\n        }\n        settledThroughRound = round;\n    }\n\n    function state() external view returns (CoreState memory result) {\n        result.settledThroughRound = settledThroughRound;\n        result.cumulativeBlockUnits = cumulativeBlockUnits;\n        result.blockHeight = uint64(cumulativeBlockUnits / 1_000_000_000);\n    }\n\n    function configuration() external view returns (CoreConfiguration memory result) {\n        result.genesisTimestamp = genesisTimestamp;\n        result.roundDuration = 600;\n        result.blockUnitsPerBlock = 1_000_000_000;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256)\n        external\n        view\n        returns (RoundPage memory result)\n    {\n        result.certifiedThroughRound = throughRound;\n        result.start.cumulativeBlockUnits = unitsAfterRound[afterRound];\n        result.end.cumulativeBlockUnits = unitsAfterRound[throughRound];\n    }\n\n    function blockInfo(uint64) external pure returns (BlockInfo memory) {\n        revert(\"BlockNotSettled\");\n    }\n\n    function paused() external pure returns (bool) {\n        return false;\n    }\n}\n\n/// @notice A 1-wei first depositor makes `accRewardPerShare` grow by amount * 1e36 / 1000 per pull.\n///         After one Squeek round the ETH farm refuses every deposit of a few ETH: `sharesOf * accRewardPerShare`\n///         in CheeseFarm._deposit overflows uint256 and reverts. The accumulator never decreases, so the farm\n///         is permanently unusable for ordinary deposits. Fails on the current code; passes once the\n///         share * accumulator products use a 512-bit mulDiv (or ACC_PRECISION is lowered).\ncontract AccOverflowTest is Test {\n    uint64 internal constant GENESIS = 1_793_735_400;\n\n    address internal owner = makeAddr(\"owner\");\n    address internal treasury = makeAddr(\"treasury\");\n    address internal attacker = makeAddr(\"attacker\");\n    address internal victim = makeAddr(\"victim\");\n\n    TinyCore internal core;\n    CheeseEmissionController internal controller;\n    CheeseToken internal cheese;\n    CheeseFarmETH internal ethFarm;\n\n    function setUp() public {\n        vm.warp(GENESIS + 100 * 600 + 17);\n        core = new TinyCore(GENESIS);\n        controller = new CheeseEmissionController(owner, address(core), treasury);\n        cheese = new CheeseToken(owner, owner, owner, address(controller));\n        ethFarm = new CheeseFarmETH(owner, address(controller));\n        vm.startPrank(owner);\n        controller.bindToken(address(cheese));\n        controller.addPool(address(ethFarm), 200);\n        controller.activate();\n        vm.stopPrank();\n        // certify through startRound - 1 with zero blocks so h0 == 0\n        core.settle(controller.startRound() - 1, 0);\n        controller.checkpoint();\n        assertTrue(controller.startCaptured());\n        vm.deal(attacker, 1 ether);\n        vm.deal(victim, 100 ether);\n    }\n\n    function test_oneWeiFirstDepositorBricksDepositsAfterOneRound() public {\n        // 1. attacker is the first depositor with 1 wei -> 1000 shares\n        vm.prank(attacker);\n        ethFarm.deposit{value: 1}();\n        assertEq(ethFarm.totalShares(), 1000);\n\n        // 2. one Squeek round of 40 blocks is certified (about ten minutes)\n        core.settle(core.settledThroughRound() + 1, 40);\n\n        // 3. the victim deposits 10 ETH. Expected: shares minted, deposit accepted.\n        //    Actual: arithmetic overflow in `sharesOf[msg.sender] * accRewardPerShare` (panic 0x11).\n        vm.prank(victim);\n        uint256 shares = ethFarm.deposit{value: 10 ether}();\n        assertGt(shares, 0, \"victim should receive shares\");\n        assertEq(ethFarm.totalAssets(), 10 ether + 1);\n    }\n\n    function test_thresholdDropsWithEveryRound() public {\n        vm.prank(attacker);\n        ethFarm.deposit{value: 1}();\n        // 24 rounds (~4 hours) with the 1-wei staker alone\n        for (uint256 i; i < 24; ++i) {\n            core.settle(core.settledThroughRound() + 1, 40);\n            controller.checkpoint();\n        }\n        // even 0.5 ETH is refused now\n        vm.prank(victim);\n        uint256 shares = ethFarm.deposit{value: 0.5 ether}();\n        assertGt(shares, 0, \"0.5 ETH deposit should succeed\");\n    }\n}","reproduction":"ETH farm with 200 points, activated, h0 captured at 0. attacker: deposit{value:1}() gives totalShares 1000. Core certifies one round of 40 blocks. victim: deposit{value:10 ether}(). Expected: about 1e22 shares minted. Actual: panic 0x11 at line 171 (1e22 * 2.94e55 > 2^256). After 24 such rounds a 0.5 ETH deposit reverts the same way. Ran the proof: both tests fail with panic 0x11 on this tree, and both pass in a temporary copy with the products changed to Math.mulDiv(shares, acc, ACC_PRECISION).","severity":"high","snippet":"_rewardDebt[msg.sender] = (sharesOf[msg.sender] * accRewardPerShare) / ACC_PRECISION;","title":"1-wei first deposit lets accRewardPerShare grow until shares*acc overflows, so the farm permanently refuses ordinary deposits"},{"citation":"resolved","description":"Merged from audit_flow and write_foundry_tests, raised from medium to high. Reward debt is reset to floor(shares*acc/1e36) on every share change (lines 171, 193) and _harvestDebt credits floor(shares*acc/1e36) - debt. Each account's error is in (-1,+1) wei and the errors do not cancel across accounts, while the floor on acc itself leaves almost no slack at 1e36 precision. The sum of entitlements can therefore exceed rewardReserve by a wei or more. claim() pays all or nothing, so whoever is left short reverts with panic 0x11 and receives none of their reward. While emission runs a later accrual refills the gap; after emission completes nothing can, because donations count as surplus and never reach rewardReserve. The account's entire accrued reward is then frozen for good, over a 1-wei shortfall. This needs no attacker, no dust and no withdrawal: it happens with ordinary deposits. The schedule completes after 3H = 1,180,410 blocks, about 205 days at the documented 40 blocks per round. test/SystemInvariants.t.sol catches this revert with try/catch, so the suite tolerates it rather than testing it. The same state arises for a disabled pool until it is re-enabled.","line":214,"path":"src/CheeseFarm.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {CheeseToken} from \"src/CheeseToken.sol\";\nimport {CheeseEmissionController} from \"src/CheeseEmissionController.sol\";\nimport {CheeseFarmETH} from \"src/farms/CheeseFarmETH.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\ncontract OverdraftCore is ISqueekCore {\n    uint64 public genesisTimestamp;\n    uint64 public settledThroughRound;\n    uint96 public cumulativeBlockUnits;\n    mapping(uint64 => uint96) public unitsAfterRound;\n\n    constructor(uint64 genesis) {\n        genesisTimestamp = genesis;\n    }\n\n    function settle(uint64 round, uint64 newBlocks) external {\n        cumulativeBlockUnits += uint96(newBlocks) * 1_000_000_000;\n        for (uint64 r = settledThroughRound + 1; r <= round; ++r) {\n            unitsAfterRound[r] = cumulativeBlockUnits;\n        }\n        settledThroughRound = round;\n    }\n\n    function state() external view returns (CoreState memory result) {\n        result.settledThroughRound = settledThroughRound;\n        result.cumulativeBlockUnits = cumulativeBlockUnits;\n        result.blockHeight = uint64(cumulativeBlockUnits / 1_000_000_000);\n    }\n\n    function configuration() external view returns (CoreConfiguration memory result) {\n        result.genesisTimestamp = genesisTimestamp;\n        result.roundDuration = 600;\n        result.blockUnitsPerBlock = 1_000_000_000;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256)\n        external\n        view\n        returns (RoundPage memory result)\n    {\n        result.certifiedThroughRound = throughRound;\n        result.start.cumulativeBlockUnits = unitsAfterRound[afterRound];\n        result.end.cumulativeBlockUnits = unitsAfterRound[throughRound];\n    }\n\n    function blockInfo(uint64) external pure returns (BlockInfo memory) {\n        revert(\"BlockNotSettled\");\n    }\n\n    function paused() external pure returns (bool) {\n        return false;\n    }\n}\n\n/// @notice Floored per-account reward debts let accounts jointly earn more than the farm pulled. Once emission\n///         has completed nothing refills rewardReserve, so the last claimer's whole claim reverts forever.\n///         Fails on the current code with panic 0x11 at `rewardReserve -= amount`; passes once the sum of\n///         harvested rewards can no longer exceed the reserve (or the payout is clamped to it).\ncontract RewardOverdraftTest is Test {\n    uint64 internal constant GENESIS = 1_793_735_400;\n    address internal owner = makeAddr(\"owner\");\n    address internal treasury = makeAddr(\"treasury\");\n    OverdraftCore internal core;\n    CheeseEmissionController internal controller;\n    CheeseToken internal cheese;\n    CheeseFarmETH internal farm;\n    address[5] internal users;\n\n    function setUp() public {\n        vm.warp(GENESIS + 100 * 600 + 17);\n        core = new OverdraftCore(GENESIS);\n        controller = new CheeseEmissionController(owner, address(core), treasury);\n        cheese = new CheeseToken(owner, owner, owner, address(controller));\n        farm = new CheeseFarmETH(owner, address(controller));\n        vm.startPrank(owner);\n        controller.bindToken(address(cheese));\n        controller.addPool(address(farm), 200);\n        controller.activate();\n        vm.stopPrank();\n        core.settle(controller.startRound() - 1, 0);\n        controller.checkpoint();\n        users = [makeAddr(\"u0\"), makeAddr(\"u1\"), makeAddr(\"u2\"), makeAddr(\"u3\"), makeAddr(\"u4\")];\n        for (uint256 i; i < 5; ++i) {\n            vm.deal(users[i], 1000 ether);\n        }\n    }\n\n    /// Twelve ordinary deposits of 0.01 to 5 ETH by five accounts, 1 to 80 blocks apart, no dust and no withdrawal.\n    function test_everyAccountCanClaimAfterEmissionEnds() public {\n        for (uint256 k; k < 12; ++k) {\n            uint256 r = uint256(keccak256(abi.encode(SEED, k)));\n            vm.prank(users[r % 5]);\n            farm.deposit{value: 0.01 ether + (r >> 16) % 5 ether}();\n            core.settle(core.settledThroughRound() + 1, uint64(1 + (r >> 200) % 80));\n        }\n        core.settle(core.settledThroughRound() + 1, 3 * 393_470);\n        controller.checkpoint();\n        assertTrue(controller.completed(), \"emission completed\");\n        for (uint256 i; i < 5; ++i) {\n            uint256 pending = farm.pendingRewards(users[i]);\n            vm.prank(users[i]);\n            uint256 paid = farm.claim(users[i]); // reverts with panic 0x11 for the account left short\n            // A fix that clamps may leave the rounding dust unpaid, nothing more.\n            assertApproxEqAbs(paid, pending, 16, \"claim pays what pendingRewards reports\");\n        }\n    }\n\n    uint256 internal constant SEED = 13;\n}","reproduction":"ETH farm, 200 points, h0 = 0. Five accounts make 12 deposits of 0.01 to 5 ETH, 1 to 80 blocks apart, chosen by keccak256(abi.encode(13, k)) (see proof), with no withdrawals. Then 3*393470 blocks are certified and checkpoint() marks completed. Accounts u0 to u4 claim in order. Expected: every claim pays pendingRewards. Actual: u4's claim reverts with panic 0x11: pending 61664802992523187701075357 wei, rewardReserve 61664802992523187701075356, so 61.66M CHEESE is locked by 1 wei. In my 40-seed run of this pattern, 7 runs locked a claimer. The specialist's input also reproduces it: alice 12021 wei, 16 blocks, bob 2376452954 wei, 1 block, carol 2231 wei, alice withdraw(17), 44 blocks, checkpoint, 3*393470 blocks, checkpoint; alice is left 1 wei short (measured here: pending 13670619896979126701987, reserve 13670619896979126701986; the totals differ from the specialist's stated figures, the 1-wei shortfall and the revert are the same). Fix: round debt up on reset, or clamp the payout to rewardReserve. The proof passes in a temporary copy with the clamp.","severity":"high","snippet":"rewardReserve -= amount;","title":"Floored reward debts let accounts jointly earn more than the farm pulled: the last claimer's whole claim reverts, permanently once emission ends"},{"citation":"resolved","description":"Not reported by the specialists. withdraw() pushes the Treasury's share first and any failure reverts the whole withdrawal (CheeseFarmETH._push reverts with NativeTransferFailed). The Treasury is an owner-set address checked only for non-zero (CheeseEmissionController.setTreasury line 166). An owner who points it at a contract that refuses ETH blocks every ETH-farm withdrawal. The same setting also allows selective blocking, since such a contract can decide per call. Combined with the inherited renounceOwnership() on the controller, the freeze cannot be undone by anyone, and there is no other exit for principal. The brief requires that the owner must not be able to confiscate user principal and that withdrawals are immediately eligible, and the audit checklist names this pattern: a receiver that reverts must not block other users' funds. It also triggers by accident, for instance a Treasury contract without a receive function, or one that burns the forwarded gas. docs/SECURITY.md records only that a rejecting Treasury blocks withdrawals 'until the owner changes it' and test/Farm.t.sol line 294 asserts that revert; neither considers an uncooperative or absent owner. That line of the test needs to change with the fix. The sIMD farm has the same failure if its token refuses the transfer to the Treasury. The CHEESE farm cannot hit it, since OpenZeppelin ERC20 only refuses the zero address. Fix preserving the fee design: credit the Treasury share to a pull balance claimed by the Treasury, or fall back to that balance when a gas-bounded push fails, so the user's 85% never depends on the Treasury.","line":203,"path":"src/CheeseFarm.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {CheeseEmissionController} from \"src/CheeseEmissionController.sol\";\nimport {CheeseFarmETH} from \"src/farms/CheeseFarmETH.sol\";\n\ncontract RefusesEth {\n    receive() external payable {\n        revert(\"no ETH\");\n    }\n}\n\n/// @notice The owner can freeze all ETH-farm principal: withdraw pushes the Treasury's cut first and reverts the\n///         whole withdrawal when that push fails. With ownership renounced the freeze cannot be undone.\n///         Fails on the current code with NativeTransferFailed; passes once a failed Treasury payment no longer\n///         blocks the withdrawer (for example a pull balance for the Treasury).\ncontract TreasuryFreezeTest is Test {\n    address internal owner = makeAddr(\"owner\");\n    address internal alice = makeAddr(\"alice\");\n    CheeseEmissionController internal controller;\n    CheeseFarmETH internal farm;\n\n    function setUp() public {\n        // No Core read happens before activate(), so any non-zero address serves.\n        controller = new CheeseEmissionController(owner, address(0xC0DE), makeAddr(\"treasury\"));\n        farm = new CheeseFarmETH(owner, address(controller));\n        vm.deal(alice, 10 ether);\n    }\n\n    function test_ownerCannotFreezePrincipalThroughTreasury() public {\n        vm.prank(alice);\n        farm.deposit{value: 10 ether}();\n\n        RefusesEth bad = new RefusesEth();\n        vm.startPrank(owner);\n        controller.setTreasury(address(bad));\n        controller.renounceOwnership();\n        vm.stopPrank();\n\n        uint256 shares = farm.sharesOf(alice);\n        vm.prank(alice);\n        uint256 paid = farm.withdraw(shares);\n        assertEq(paid, 8.5 ether, \"85% paid\");\n        assertEq(alice.balance, 8.5 ether, \"alice received her 85%\");\n    }\n}","reproduction":"alice deposits 10 ETH into the ETH farm. Owner calls controller.setTreasury(address(contract whose receive reverts)), then controller.renounceOwnership(). alice calls withdraw(sharesOf(alice)). Expected: alice is paid 8.5 ETH. Actual: reverts NativeTransferFailed(treasury, 1.5e18), for every depositor and for good, since no address can call setTreasury any more. Ran the proof: fails with that error on this tree, and passes in a temporary copy where a failed Treasury push no longer reverts.","severity":"high","snippet":"if (toTreasury != 0) _push(treasury, toTreasury);","title":"Withdrawal reverts when the Treasury push fails, so the owner can freeze all ETH-farm principal, permanently if ownership is then renounced"},{"citation":"resolved","description":"Merged from all four audit areas and the tester. _deposit closes only the paused branch of a failed sync. When _sync fails because SqueekCore.state() or roundPage() reverted, or the height regressed, pullRewards ignores the result (CheeseEmissionController.sol line 298) and the deposit mints shares while blocks certified earlier remain unpriced. The next successful checkpoint spreads those blocks over all current shares. This breaks the brief's rule that new depositors never receive rewards accrued before their eligibility, and it contradicts README.md lines 77-78 ('Allocation changes and deposits require a successful sync'). A first depositor into an empty farm during an outage similarly receives emission that should have been forfeited. Fix: revert _deposit unless a sync succeeded, for example by calling controller.checkpoint() and requiring true. Withdraw and claim keep the best-effort path.","line":159,"path":"src/CheeseFarm.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {CheeseToken} from \"src/CheeseToken.sol\";\nimport {CheeseEmissionController} from \"src/CheeseEmissionController.sol\";\nimport {CheeseFarmETH} from \"src/farms/CheeseFarmETH.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\ncontract CoreStubA {\n    uint64 public genesis;\n    uint64 public settled;\n    uint96 public units;\n    bool public down;\n    mapping(uint64 => uint96) public after_;\n\n    constructor(uint64 g) { genesis = g; }\n    function setDown(bool d) external { down = d; }\n    function settle(uint64 round, uint64 blocks) external {\n        units += uint96(blocks) * 1e9;\n        for (uint64 r = settled + 1; r <= round; ++r) after_[r] = units;\n        settled = round;\n    }\n    function state() external view returns (ISqueekCore.CoreState memory s) {\n        require(!down, \"down\");\n        s.settledThroughRound = settled;\n        s.cumulativeBlockUnits = units;\n        s.blockHeight = uint64(units / 1e9);\n    }\n    function configuration() external view returns (ISqueekCore.CoreConfiguration memory c) {\n        require(!down, \"down\");\n        c.genesisTimestamp = genesis;\n        c.roundDuration = 600;\n        c.blockUnitsPerBlock = 1e9;\n    }\n    function roundPage(uint64 a, uint64 t, uint256) external view returns (ISqueekCore.RoundPage memory p) {\n        require(!down, \"down\");\n        require(a <= t && t <= settled, \"bounds\");\n        p.certifiedThroughRound = t;\n        p.end.cumulativeBlockUnits = after_[t];\n    }\n}\n\ncontract OutageDepositTest is Test {\n    uint64 constant GENESIS = 1_793_735_400;\n    address owner = makeAddr(\"owner\");\n    address treasury = makeAddr(\"treasury\");\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n    CoreStubA core;\n    CheeseEmissionController controller;\n    CheeseToken cheese;\n    CheeseFarmETH farm;\n\n    function setUp() public {\n        vm.warp(GENESIS + 100 * 600 + 17);\n        core = new CoreStubA(GENESIS);\n        controller = new CheeseEmissionController(owner, address(core), treasury);\n        cheese = new CheeseToken(owner, treasury, owner, address(controller));\n        farm = new CheeseFarmETH(owner, address(controller));\n        vm.startPrank(owner);\n        controller.bindToken(address(cheese));\n        controller.addPool(address(farm), 200);\n        controller.activate();\n        vm.stopPrank();\n        core.settle(controller.startRound() - 1, 0);\n        controller.checkpoint();\n        vm.deal(alice, 100 ether);\n        vm.deal(bob, 100 ether);\n    }\n\n    function test_outageDeposit() public {\n        vm.prank(alice);\n        farm.deposit{value: 10 ether}();\n        core.settle(core.settled() + 1, 1000); // 1000 blocks while only alice staked\n        core.setDown(true);\n        vm.prank(bob);\n        // Accepted today although the 1000 blocks were not priced; a fixed farm refuses it (or prices them first).\n        try farm.deposit{value: 10 ether}() {} catch {}\n        core.setDown(false);\n        vm.prank(alice); uint256 a = farm.claim(alice);\n        vm.prank(bob); uint256 b = farm.claim(bob);\n        emit log_named_uint(\"alice\", a);\n        emit log_named_uint(\"bob\", b);\n        assertEq(b, 0, \"bob earned emission of blocks certified before his deposit\");\n    }\n}","reproduction":"ETH farm, 200 points. alice deposits 10 ETH; Core certifies 1000 blocks; nobody checkpoints; state() starts reverting; bob deposits 10 ETH and it is accepted; Core recovers; both claim. Expected: alice 734914140002202624511650, bob 0. Actual: alice 367457070001101312255825, bob 367457070001101312255825. Ran the proof: it fails with 'bob earned emission of blocks certified before his deposit' on this tree and passes in a temporary copy where _deposit requires a successful checkpoint.","severity":"medium","snippet":"if (controller.emissionsPaused()) revert EmissionsPausedDepositsClosed();","title":"Deposits stay open when the checkpoint fails, so a depositor during a Core outage or regression takes emission for blocks certified before the deposit"},{"citation":"resolved","description":"From audit_flow. The function sets the flag without calling _sync, so blocks certified before the pause stay unpriced, and while paused _sync returns (false, 0) at line 466. A staker exiting during the pause settles against the stale accumulator. After unpause the catch-up goes to whoever is staked then; if the farm emptied, the next deposit calls pullRewards(hasStakers=false) and all of it is forfeited. So a guardian or owner action can take rewards earned before that action from the users who earned them, against the brief's rule on already accrued rewards. pendingRewards also shows 0 for those blocks during the pause, so the staker cannot see what exiting costs. docs/SECURITY.md residual risk 2 accepts forgoing the catch-up for blocks during a pause, but it does not address blocks certified before the pause. Fix: call _sync() best-effort at the start of pauseEmissions. Blocks certified during the pause keep the documented behaviour.","line":202,"path":"src/CheeseEmissionController.sol","reproduction":"ETH farm, 200 points. alice deposits 10 ETH as sole staker; Core certifies 1000 blocks; owner calls pauseEmissions(); alice calls withdraw(all) then claim(alice); owner calls unpauseEmissions(); bob deposits 1 ETH. Expected: alice receives 734914140002202624511650 CHEESE. Actual, from my test: alice claims 0 and controller.forfeitedEmission() == 734914140002202624511650.","severity":"medium","snippet":"function pauseEmissions() external {","title":"pauseEmissions does not checkpoint first: a staker who exits during the pause loses emission for blocks certified before it, and that emission can be forfeited"},{"citation":"resolved","description":"Merged from write_foundry_tests (low) and audit_permissions (medium). Solidity's try/catch does not catch a return-data decoding failure or a call to an address without code; the revert happens in the caller. The same applies to roundPage at line 480 and to pendingCheckpoint at lines 384 and 393, which pendingRewards reads. Every farm withdraw and claim runs _accrue, then pullRewards, then _sync, so a SqueekCore proxy upgrade that changes the CoreState layout blocks all user withdrawals and claims. This contradicts the pullRewards comment at line 298 ('withdrawals must never be blocked by Squeekchain availability') and README.md lines 172-174, which say a changed ABI makes checkpoint() return false. The proxy is upgradeable by its Timelock, so this is reachable outside this project's control. pauseEmissions restores withdrawals because _sync returns at line 466 before reading Core; without a responsive guardian or owner, principal stays locked. The live proxy currently decodes correctly (state, configuration and roundPage were all read from 0xFD6C166D4B8E2962E65746a13e5a0797CD4dE0c4), so this is a future-dependency defect, not a current failure. Fix: read Core through a low-level staticcall, check success and length, and treat any failure as unavailable.","line":469,"path":"src/CheeseEmissionController.sol","reproduction":"alice deposits 1 ETH with emission active. vm.mockCall(core, state.selector, abi.encode(uint256(1))). controller.checkpoint() reverts, where the README says it returns false. alice's withdraw(all) reverts with EvmError: Revert, where the expected result is payment of 85%. Same result after vm.etch(core, ''). After pauseEmissions() the same withdraw succeeds (existing test test_malformedCoreDataGuardianPauseRestoresWithdrawals). Run in my test; both variants fail as described.","severity":"medium","snippet":"try squeekCore.state() returns (ISqueekCore.CoreState memory result) {","title":"Undecodable SqueekCore return data is not caught by try/catch, so every withdraw and claim reverts until the owner or guardian pauses"},{"citation":"resolved","description":"From write_foundry_tests. The batch applies decreases in one pass and increases in a second, each compared with the weight at that moment. With a repeated id the result depends on the passes, not on the order given. The caller is the owner and the change is prospective, so the impact is a wrong allocation that the owner must notice and correct. Fix: reject duplicate ids.","line":254,"path":"src/CheeseEmissionController.sol","reproduction":"Initial allocation, pool 0 at 400 points. Owner calls batchSetPoolWeights([0,0],[500,100]). Expected with last-write-wins: 100, or a revert. Actual, from my test: getPoolAllocation(0).weight == 500.","severity":"low","snippet":"if (weights[i] <= _pool(poolIds[i]).weight) _setWeight(poolIds[i], weights[i]);","title":"batchSetPoolWeights with a repeated pool id ignores input order and can leave the higher weight"},{"citation":"resolved","description":"Merged from audit_flow and audit_math. Share value is (totalAssets+1)/(totalShares+1000). When remaining real shares are comparable to 1000, most of the retained 14% belongs to the virtual shares. When the last real shares leave, that portion remains in totalAssets with totalShares == 0. No withdrawal can reach it, skimSurplus cannot (_surplus excludes totalAssets), and the Treasury does not get it, though the brief sends the fee to the Treasury when no shares remain. withdraw's totalShares == 0 branch handles only the exiting withdrawal's own fee. A griefer can use the same path to raise the share price so small deposits revert ZeroShares, paying 15% to do it. docs/SECURITY.md item 5 notes the griefing but not the stranded funds. It needs dust-sized remaining shares, so low. Fix: when totalShares reaches 0, send residual totalAssets to the Treasury.","line":199,"path":"src/CheeseFarm.sol","reproduction":"ETH farm, no emission. alice deposits 1 ETH; bob deposits 1 wei (1000 shares); alice withdraws all (retained 0.14 ETH); bob's assetsOf is 70000000000000001, half of it; bob withdraws all. Expected: farm balance 0. Actual, from my test: totalShares 0, totalAssets 70000000000000000, balance 0.07 ETH, surplus() 0, so the 0.07 ETH is unrecoverable. Griefing variant, also run: alice deposits 1000 ETH and withdraws all but 1 share; a 0.1 ETH deposit then reverts because convertToShares(0.1 ether) == 0. She then withdraws the last share: totalShares 0, totalAssets 139860139860139860140 (139.86 ETH), surplus() 0, and convertToShares(0.1 ether) is still 0.","severity":"low","snippet":"totalAssets -= (paid + toTreasury); // `retained` stays in the principal reserve","title":"Retained Hamster Bite accrues partly to the 1000 virtual shares and is stranded forever once the farm empties"},{"citation":"resolved","description":"From audit_flow. The controller constructor sets guardian = owner_ (CheeseEmissionController.sol line 147), and the script constructs with the deployer as owner_. handOver never calls setGuardian, so the deployer keeps pauseEmissions power, which closes all deposits and can cause the forfeiture in the pause finding. The factory launch is unaffected, because launch.json passes $owner as owner_ and the guardian is therefore the owner; this affects the script's own Sepolia path only. Fix: call d.controller.setGuardian(owner) in handOver before transferOwnership.","line":73,"path":"script/Deploy.s.sol","reproduction":"deploy(cfg, deployer) with cfg.owner != deployer, as CheeseBase does, then owner.acceptOwnership(). Expected: controller.guardian() == owner. Actual, from my test: guardian equals the deployer; the deployer's pauseEmissions() succeeds and a following ETH-farm deposit reverts.","severity":"low","snippet":"d.controller.transferOwnership(owner);","title":"Deploy script leaves the deployer as emissions guardian after handing ownership to the owner"}],"hash":"15b36283a9bce9b88c254b3a6ca77560d39b9aa380edc1431cc3f518438d74ef","nodeId":"95b84426-94cc-4584-8468-10c3cd3ef206","outcome":"completed","summary":"The review is finished and this code shouldn't launch yet: I kept 9 findings (3 high, 3 medium, 3 low), and one of the highs is new. They're in `.imd-findings.json`, with coverage rows for all 51 entry points plus 3 invariants. No project files changed, and I deleted my scratch tests.\n\nThe existing suite passes (108 of 108), but it misses these defects, and its invariant handler catches the claim revert from finding 2 instead of failing on it.\n\n## Findings kept\n\n| # | Severity | Finding | How it was reproduced | Merged from |\n|---|---|---|---|---|\n| 1 | high | A 1-wei first deposit makes `shares * accRewardPerShare` overflow, so ordinary deposits revert for good | Specialist proof: a 10 ETH deposit panics after one round | flow, economics, math |\n| 2 | high | Rounded reward debts can exceed `rewardReserve`, so the last claimer's whole claim reverts, permanently once emission ends | My proof: 61.66M CHEESE locked over a 1-wei shortfall | flow, tester |\n| 3 | high | A Treasury that refuses ETH blocks every ETH-farm withdrawal; if ownership is then renounced, nobody can undo it | My proof, using `setTreasury` then `renounceOwnership` | new |\n| 4 | medium | Deposits stay open during a SqueekCore outage, so a late depositor takes rewards for earlier blocks | Specialist proof: bob takes half of alice's emission | all four areas, tester |\n| 5 | medium | `pauseEmissions` doesn't checkpoint first, so rewards for blocks certified before the pause can be lost | My test: alice claims 0 and 734,914 CHEESE is forfeited | flow |\n| 6 | medium | `try/catch` doesn't catch undecodable SqueekCore data, so withdrawals revert until someone pauses | My test, with malformed return data and with the code removed | tester, permissions |\n| 7 | low | `batchSetPoolWeights` with a repeated pool id ignores input order | My test: `[500,100]` leaves the weight at 500 | tester |\n| 8 | low | Part of the retained fee goes to the virtual shares and is stranded once the farm empties | My test: 0.07 ETH stranded, and 139.86 ETH in a griefing variant | flow, math |\n| 9 | low | The deploy script leaves the deployer as guardian after handing over ownership (the factory launch isn't affected) | My test | flow |\n\n- **Changes from the specialists:** I raised finding 2 from medium to high. With ordinary 0.01–5 ETH deposits and no withdrawals, 7 of 40 seeded runs locked a claimer, and no attacker is needed.\n- **Proofs:** findings 1–4 carry proofs. All four fail on the current code and pass in a temporary patched copy.\n- **Test to update:** `test/Farm.t.sol:294` asserts the revert in finding 3, so it has to change with the fix.\n\n## Dropped or narrowed\n- **Fee reduced by withdrawing in instalments:** the arithmetic is right, but the brief explicitly accepts that partial withdrawers benefit. A fix would change the agreed economics, so the owner should decide this as a product question.\n- **Rebasing sIMD:** this only reproduces against a mock, and nothing shows sIMD rebases. Check this before calling `setAsset`, because the asset can only be set once.\n- **Owner routing emissions to their own pool:** this is an intended power, limited to future emissions and bounded by the SQK schedule. It belongs in the trust assumptions, because the README's \"the owner cannot mint\" understates it.\n- **Frozen principal from the overflow (narrowed):** I removed this part of finding 1. An existing staker's overflow margin barely shrinks, so only an account already within about 3e-15 of the limit when it deposited could be frozen. The permanent deposit ceiling is the real harm.\n\n## Checked and holding\n- **Live integration:** on Sepolia, `state`, `configuration` and `roundPage(r,r,0)` all decode correctly and match the published docs.\n- **Schedule:** `script/VerifySqueekchain.s.sol` passes against the live chain, which the README says hadn't been run as a `forge script`.\n- **Deployment:** `launch.json` matches the schema and the constructors, the largest runtime is 12,816 bytes, and no constructor","treeHash":null,"usage":{"cachedInputTokens":1374051,"cost":{"source":"runtime","usdMicros":2385109},"inputTokens":22,"model":"claude-opus-5-5","outputTokens":45082,"runtime":"claude","turns":28,"wallClockMs":473545}},{"artifacts":[],"attempt":1,"bundleHash":"c6940507175b9329ad668187a9fa6f520c65f1b1c80a565c2798cf92e0095bb6","device":"aac006f7fc669006","findings":[],"hash":"24f8c58dcea5362b84b7a04d35b9bdbb944457b5787c557cf2fea34b03ea1089","nodeId":"5a771bd9-a897-4289-99a5-fdeef33dbd72","outcome":"completed","summary":"I checked `launch.json` against the accepted source and it was already correct, so I made one small addition to its notes. It still deploys five contracts in dependency order:\n\n1. **`CheeseEmissionController`**(`$owner`, `0xFD6C166D4B8E2962E65746a13e5a0797CD4dE0c4`, `$owner`): owner, SqueekCore proxy, Treasury.\n2. **`CheeseToken`**(`$owner`, `$owner`, `$owner`, `$contract:CheeseEmissionController`): the three premint recipients go to the owner because the brief names no wallets. The controller is the only emission authority, so there is no owner minting.\n3. **`CheeseFarmERC20`**(`$owner`, `$contract:CheeseEmissionController`, `$contract:CheeseToken`): the CHEESE farm.\n4. **`CheeseFarmETH`**(`$owner`, `$contract:CheeseEmissionController`): the native ETH farm.\n5. **`CheeseFarmSIMD`**(`$owner`, `$contract:CheeseEmissionController`): the sIMD farm. The owner sets the sIMD token once after launch.\n\nWhat I checked:\n- **Constructors:** every argument list matches the current source in number, order and type. The latest accepted round changed no constructors.\n- **SqueekCore address:** it matches the official `sepolia.json` (chain 11155111, release core-sepolia-20261103, proxy `0xfd6c…e0c4`).\n- **Manifest shape:** it has only the keys `kind`, `contracts` and `notes`, and the notes are 1,606 characters (the limit is 4,000).\n- **Build and tests:** `forge build` succeeds. `forge test` with the verifier's fuzz seed ran 91 tests and all passed. That run skipped the protected deployment and owner checks, because they need launch values only the verifier has.\n\n**The change:** the notes now say the sIMD asset the owner sets must be a non-rebasing ERC-20. The latest accepted source documents that requirement on `setAsset`.\n\n`launch.json` is the only file changed.","treeHash":"7b7439c9e9823d24896d14c51191a14d5f5d9d53","usage":{"cachedInputTokens":222604,"cost":{"source":"runtime","usdMicros":368776},"inputTokens":16,"model":"claude-opus-5-5","outputTokens":3064,"runtime":"claude","turns":9,"wallClockMs":72031}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"b8fd8054ea57c5d8","findings":[{"citation":"resolved","description":"accRewardPerShare is scaled by ACC_PRECISION = 1e36 and divided by totalShares. A deposit of 1 wei into an empty farm mints 1000 shares (VIRTUAL_SHARES offset), so each wei of pulled CHEESE adds 1e33 to the accumulator. The accumulator is monotonic. Every later share change computes `(sharesOf[msg.sender] * accRewardPerShare) / ACC_PRECISION` (CheeseFarm.sol:171 in _deposit, :193 in withdraw, and :288 in _harvestDebt), and that product overflows uint256 (1.16e77) once shares * acc exceeds it. With the ETH pool's 20% of SQK emission, one day of documented block production (144 rounds x 40 blocks) pulls 4.233e24 CHEESE, which makes acc 4.233e57. After that, any deposit that would mint more than about 2.7e19 shares (about 0.027 ETH at 1000 shares/wei) reverts with panic 0x11, forever. The same applies to the CHEESE and sIMD farms. It costs the attacker 1 wei plus gas, and the attacker also collects the whole pool emission while alone. A depositor who entered just under the bound can also have withdraw/claim overflow later as acc keeps growing, freezing principal.","line":281,"path":"src/CheeseFarm.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {CheeseToken} from \"src/CheeseToken.sol\";\nimport {CheeseEmissionController} from \"src/CheeseEmissionController.sol\";\nimport {CheeseFarmETH} from \"src/farms/CheeseFarmETH.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\ncontract CoreStubO {\n    uint64 public genesis;\n    uint64 public settled;\n    uint96 public units;\n    mapping(uint64 => uint96) public after_;\n\n    constructor(uint64 g) { genesis = g; }\n    function settle(uint64 round, uint64 blocks) external {\n        units += uint96(blocks) * 1e9;\n        for (uint64 r = settled + 1; r <= round; ++r) after_[r] = units;\n        settled = round;\n    }\n    function state() external view returns (ISqueekCore.CoreState memory s) {\n        s.settledThroughRound = settled;\n        s.cumulativeBlockUnits = units;\n        s.blockHeight = uint64(units / 1e9);\n    }\n    function configuration() external view returns (ISqueekCore.CoreConfiguration memory c) {\n        c.genesisTimestamp = genesis;\n        c.roundDuration = 600;\n        c.blockUnitsPerBlock = 1e9;\n    }\n    function roundPage(uint64 a, uint64 t, uint256) external view returns (ISqueekCore.RoundPage memory p) {\n        require(a <= t && t <= settled, \"bounds\");\n        p.certifiedThroughRound = t;\n        p.end.cumulativeBlockUnits = after_[t];\n    }\n}\n\n/// A 1-wei first deposit makes accRewardPerShare huge (1e36 / 1000 shares per reward wei); afterwards\n/// sharesOf * accRewardPerShare overflows for any ordinary deposit, so the farm is permanently unusable.\ncontract OverflowDoSTest is Test {\n    uint64 constant GENESIS = 1_793_735_400;\n    address owner = makeAddr(\"owner\");\n    address treasury = makeAddr(\"treasury\");\n    address attacker = makeAddr(\"attacker\");\n    address alice = makeAddr(\"alice\");\n    CoreStubO core;\n    CheeseEmissionController controller;\n    CheeseToken cheese;\n    CheeseFarmETH farm;\n\n    function setUp() public {\n        vm.warp(GENESIS + 100 * 600 + 17);\n        core = new CoreStubO(GENESIS);\n        controller = new CheeseEmissionController(owner, address(core), treasury);\n        cheese = new CheeseToken(owner, treasury, owner, address(controller));\n        farm = new CheeseFarmETH(owner, address(controller));\n        vm.startPrank(owner);\n        controller.bindToken(address(cheese));\n        controller.addPool(address(farm), 200); // the ETH Pool\n        controller.activate();\n        vm.stopPrank();\n        core.settle(controller.startRound() - 1, 0);\n        controller.checkpoint();\n        vm.deal(attacker, 1 ether);\n        vm.deal(alice, 100 ether);\n    }\n\n    function test_dustFirstDepositBricksFarm() public {\n        vm.prank(attacker);\n        farm.deposit{value: 1}(); // 1 wei -> 1000 shares\n        assertEq(farm.totalShares(), 1000);\n\n        // A day of SqueekCore rounds (144 rounds x 40 blocks, the documented target) while only the dust is staked.\n        core.settle(core.settled() + 144, 144 * 40);\n        vm.prank(attacker);\n        farm.claim(attacker); // pulls the pool's emission: accRewardPerShare += amount * 1e36 / 1000\n\n        // An honest 10 ETH deposit must succeed. It reverts with panic 0x11 (sharesOf * accRewardPerShare overflow).\n        vm.prank(alice);\n        uint256 shares = farm.deposit{value: 10 ether}();\n        assertGt(shares, 0);\n        assertEq(farm.sharesOf(alice), shares);\n    }\n}","reproduction":"ETH farm registered (200 points), emission active, farm empty. 1) attacker: ethFarm.deposit{value:1}() -> totalShares = 1000. 2) SqueekCore certifies 144 rounds / 5760 blocks. 3) attacker: ethFarm.claim(attacker): pullRewards mints 4.233e24 CHEESE, accRewardPerShare = 4.233e57. 4) alice: ethFarm.deposit{value:10 ether}(). Expected: about 1e22 shares minted. Actual: reverts with panic 0x11 at _rewardDebt = sharesOf*acc/1e36 (CheeseFarm.sol:171). Every later deposit above about 0.027 ETH reverts the same way. Fix: lower ACC_PRECISION (e.g. 1e18 with a mulDiv), use Math.mulDiv for shares*acc, and/or require a minimum first deposit / initial dead shares so totalShares cannot be dust.","severity":"high","snippet":"        accRewardPerShare += (amount * ACC_PRECISION) / totalShares;","title":"Dust first deposit inflates accRewardPerShare until sharesOf*accRewardPerShare overflows: farm permanently refuses ordinary deposits"},{"citation":"resolved","description":"_deposit closes deposits only when emissions are paused, because 'a paused emission cannot be checkpointed' (comment at CheeseFarm.sol:157-158). The same condition also arises when _sync returns (false, 0) because squeekCore.state() or roundPage reverts (CoreUnavailable) or the height regresses (CoreRegression; CheeseEmissionController.sol:471-497). pullRewards ignores that result (CheeseEmissionController.sol:298, `_sync(); // best effort`). The deposit then mints shares while the blocks certified since the last successful checkpoint are still unpriced. When SqueekCore becomes readable again, the next checkpoint prices those blocks into emissionPerPoint and the farm spreads them over all current shares, newcomers included. This breaks the brief's rule that new depositors never receive rewards accrued before their eligibility, and it dilutes the stakers who were actually present. The brief explicitly requires handling temporarily unavailable Squeekchain data.","line":159,"path":"src/CheeseFarm.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {CheeseToken} from \"src/CheeseToken.sol\";\nimport {CheeseEmissionController} from \"src/CheeseEmissionController.sol\";\nimport {CheeseFarmETH} from \"src/farms/CheeseFarmETH.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\ncontract CoreStubA {\n    uint64 public genesis;\n    uint64 public settled;\n    uint96 public units;\n    bool public down;\n    mapping(uint64 => uint96) public after_;\n\n    constructor(uint64 g) { genesis = g; }\n    function setDown(bool d) external { down = d; }\n    function settle(uint64 round, uint64 blocks) external {\n        units += uint96(blocks) * 1e9;\n        for (uint64 r = settled + 1; r <= round; ++r) after_[r] = units;\n        settled = round;\n    }\n    function state() external view returns (ISqueekCore.CoreState memory s) {\n        require(!down, \"down\");\n        s.settledThroughRound = settled;\n        s.cumulativeBlockUnits = units;\n        s.blockHeight = uint64(units / 1e9);\n    }\n    function configuration() external view returns (ISqueekCore.CoreConfiguration memory c) {\n        require(!down, \"down\");\n        c.genesisTimestamp = genesis;\n        c.roundDuration = 600;\n        c.blockUnitsPerBlock = 1e9;\n    }\n    function roundPage(uint64 a, uint64 t, uint256) external view returns (ISqueekCore.RoundPage memory p) {\n        require(!down, \"down\");\n        require(a <= t && t <= settled, \"bounds\");\n        p.certifiedThroughRound = t;\n        p.end.cumulativeBlockUnits = after_[t];\n    }\n}\n\ncontract OutageDepositTest is Test {\n    uint64 constant GENESIS = 1_793_735_400;\n    address owner = makeAddr(\"owner\");\n    address treasury = makeAddr(\"treasury\");\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n    CoreStubA core;\n    CheeseEmissionController controller;\n    CheeseToken cheese;\n    CheeseFarmETH farm;\n\n    function setUp() public {\n        vm.warp(GENESIS + 100 * 600 + 17);\n        core = new CoreStubA(GENESIS);\n        controller = new CheeseEmissionController(owner, address(core), treasury);\n        cheese = new CheeseToken(owner, treasury, owner, address(controller));\n        farm = new CheeseFarmETH(owner, address(controller));\n        vm.startPrank(owner);\n        controller.bindToken(address(cheese));\n        controller.addPool(address(farm), 200);\n        controller.activate();\n        vm.stopPrank();\n        core.settle(controller.startRound() - 1, 0);\n        controller.checkpoint();\n        vm.deal(alice, 100 ether);\n        vm.deal(bob, 100 ether);\n    }\n\n    function test_outageDeposit() public {\n        vm.prank(alice);\n        farm.deposit{value: 10 ether}();\n        core.settle(core.settled() + 1, 1000); // 1000 blocks while only alice staked\n        core.setDown(true);\n        vm.prank(bob);\n        // Accepted today although the 1000 blocks were not priced; a fixed farm refuses it (or prices them first).\n        try farm.deposit{value: 10 ether}() {} catch {}\n        core.setDown(false);\n        vm.prank(alice); uint256 a = farm.claim(alice);\n        vm.prank(bob); uint256 b = farm.claim(bob);\n        emit log_named_uint(\"alice\", a);\n        emit log_named_uint(\"bob\", b);\n        assertEq(b, 0, \"bob earned emission of blocks certified before his deposit\");\n    }\n}","reproduction":"ETH farm (200 pts), emission active. 1) alice deposits 10 ETH. 2) SqueekCore certifies 1000 blocks; nobody checkpoints. 3) SqueekCore.state() starts reverting (outage). 4) bob deposits 10 ETH: accepted, because _sync's failure is swallowed and only emissionsPaused is checked. 5) Core recovers; alice and bob call claim. Expected: alice 734914140002202624511650 (the pool's share of the 1000 blocks), bob 0. Actual: alice 367457070001101312255825, bob 367457070001101312255825. Fix: have _deposit revert unless the checkpoint succeeds (e.g. pullRewards returns or exposes the sync status, or the farm calls controller.checkpoint() and requires synced == true), mirroring the pause rule. Withdrawals keep the best-effort path.","severity":"medium","snippet":"        if (controller.emissionsPaused()) revert EmissionsPausedDepositsClosed();","title":"Deposits stay open when the checkpoint fails (SqueekCore unreadable or regressed), so new depositors take emission certified before they joined"},{"citation":"resolved","description":"pauseEmissions sets the flag without calling _sync. Blocks certified before the pause but not yet checkpointed stay unpriced. While paused, _sync returns (false, 0) immediately (line 466), so a staker who withdraws and claims during the pause settles against the stale emissionPerPoint. That staker never receives the pool's share of those pre-pause blocks. On unpause the catch-up emission goes to whoever is staked then; if the farm emptied, the first deposit after unpause calls pullRewards(hasStakers=false) and the whole amount is forfeited. A guardian or owner action can therefore take rewards earned before the action from the users who earned them. The brief says the owner must not be able to confiscate already accrued rewards, and the contract documents that withdrawals and claims keep working while paused and that nothing is lost.","line":202,"path":"src/CheeseEmissionController.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {CheeseToken} from \"src/CheeseToken.sol\";\nimport {CheeseEmissionController} from \"src/CheeseEmissionController.sol\";\nimport {CheeseFarmETH} from \"src/farms/CheeseFarmETH.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\ncontract CoreStubB {\n    uint64 public genesis;\n    uint64 public settled;\n    uint96 public units;\n    bool public down;\n    mapping(uint64 => uint96) public after_;\n\n    constructor(uint64 g) { genesis = g; }\n    function setDown(bool d) external { down = d; }\n    function settle(uint64 round, uint64 blocks) external {\n        units += uint96(blocks) * 1e9;\n        for (uint64 r = settled + 1; r <= round; ++r) after_[r] = units;\n        settled = round;\n    }\n    function state() external view returns (ISqueekCore.CoreState memory s) {\n        require(!down, \"down\");\n        s.settledThroughRound = settled;\n        s.cumulativeBlockUnits = units;\n        s.blockHeight = uint64(units / 1e9);\n    }\n    function configuration() external view returns (ISqueekCore.CoreConfiguration memory c) {\n        require(!down, \"down\");\n        c.genesisTimestamp = genesis;\n        c.roundDuration = 600;\n        c.blockUnitsPerBlock = 1e9;\n    }\n    function roundPage(uint64 a, uint64 t, uint256) external view returns (ISqueekCore.RoundPage memory p) {\n        require(!down, \"down\");\n        require(a <= t && t <= settled, \"bounds\");\n        p.certifiedThroughRound = t;\n        p.end.cumulativeBlockUnits = after_[t];\n    }\n}\n\ncontract PauseForfeitTest is Test {\n    uint64 constant GENESIS = 1_793_735_400;\n    address owner = makeAddr(\"owner\");\n    address treasury = makeAddr(\"treasury\");\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n    CoreStubB core;\n    CheeseEmissionController controller;\n    CheeseToken cheese;\n    CheeseFarmETH farm;\n\n    function setUp() public {\n        vm.warp(GENESIS + 100 * 600 + 17);\n        core = new CoreStubB(GENESIS);\n        controller = new CheeseEmissionController(owner, address(core), treasury);\n        cheese = new CheeseToken(owner, treasury, owner, address(controller));\n        farm = new CheeseFarmETH(owner, address(controller));\n        vm.startPrank(owner);\n        controller.bindToken(address(cheese));\n        controller.addPool(address(farm), 200);\n        controller.activate();\n        vm.stopPrank();\n        core.settle(controller.startRound() - 1, 0);\n        controller.checkpoint();\n        vm.deal(alice, 100 ether);\n        vm.deal(bob, 100 ether);\n    }\n\n    function test_pauseForfeit() public {\n        vm.prank(alice);\n        farm.deposit{value: 10 ether}();\n        core.settle(core.settled() + 1, 1000);\n        vm.prank(owner);\n        controller.pauseEmissions();\n        uint256 shares = farm.sharesOf(alice);\n        vm.startPrank(alice);\n        farm.withdraw(shares);\n        uint256 a = farm.claim(alice);\n        vm.stopPrank();\n        vm.prank(owner);\n        controller.unpauseEmissions();\n        vm.prank(bob);\n        farm.deposit{value: 1 ether}();\n        emit log_named_uint(\"alice claimed\", a);\n        emit log_named_uint(\"forfeited\", controller.forfeitedEmission());\n        assertGt(a, 0, \"alice lost rewards for 1000 blocks certified before the pause\");\n    }\n}","reproduction":"ETH farm (200 pts), emission active. 1) alice deposits 10 ETH (sole staker). 2) SqueekCore certifies 1000 blocks. 3) guardian calls pauseEmissions() (no checkpoint). 4) alice withdraw(all) then claim(alice) -> 0 CHEESE. 5) owner unpauseEmissions(); bob deposits 1 ETH -> _accrue -> pullRewards(0, false). Expected: alice receives 734914140002202624511650 CHEESE for the 1000 blocks she staked through. Actual: alice gets 0 and controller.forfeitedEmission() == 734914140002202624511650. Fix: call _sync() (best effort) at the start of pauseEmissions so everything certified before the pause is priced under the pre-pause staker set.","severity":"medium","snippet":"    function pauseEmissions() external {\n        if (msg.sender != guardian && msg.sender != owner()) revert NotGuardianOrOwner(msg.sender);\n        if (emissionsPaused) revert EmissionsArePaused();\n        emissionsPaused = true;","title":"pauseEmissions does not checkpoint first: stakers who exit during a pause lose emission for blocks certified before it, which is then forfeited"},{"citation":"resolved","description":"On every share change, _rewardDebt is reset to floor(shares * acc / 1e36) (CheeseFarm.sol:171 and :193). The fractional part dropped from the debt is reward the account never earned, and _harvestDebt later pays floor(shares * acc_new / 1e36) - debt. Each deposit or withdraw epoch can therefore overpay an account by 1 wei. accRewardPerShare is floored too, but at 1e36 precision that leaves almost no slack, so the sum of harvested rewards can exceed the CHEESE actually pulled into rewardReserve. claim() then subtracts the full pending amount from rewardReserve, and for whoever claims last it underflows: the claim reverts with panic 0x11 and returns nothing at all, not just the missing wei. While emission runs, a later accrual can refill the gap. After emission completes (or for a pool that is disabled), nothing adds to rewardReserve again, so that account's rewards are frozen permanently. pendingRewards keeps reporting the amount.","line":214,"path":"src/CheeseFarm.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {CheeseToken} from \"src/CheeseToken.sol\";\nimport {CheeseEmissionController} from \"src/CheeseEmissionController.sol\";\nimport {CheeseFarmETH} from \"src/farms/CheeseFarmETH.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\ncontract CoreStubR {\n    uint64 public genesis;\n    uint64 public settled;\n    uint96 public units;\n    mapping(uint64 => uint96) public after_;\n\n    constructor(uint64 g) { genesis = g; }\n    function settle(uint64 round, uint64 blocks) external {\n        units += uint96(blocks) * 1e9;\n        for (uint64 r = settled + 1; r <= round; ++r) after_[r] = units;\n        settled = round;\n    }\n    function state() external view returns (ISqueekCore.CoreState memory s) {\n        s.settledThroughRound = settled;\n        s.cumulativeBlockUnits = units;\n        s.blockHeight = uint64(units / 1e9);\n    }\n    function configuration() external view returns (ISqueekCore.CoreConfiguration memory c) {\n        c.genesisTimestamp = genesis;\n        c.roundDuration = 600;\n        c.blockUnitsPerBlock = 1e9;\n    }\n    function roundPage(uint64 a, uint64 t, uint256) external view returns (ISqueekCore.RoundPage memory p) {\n        require(a <= t && t <= settled, \"bounds\");\n        p.certifiedThroughRound = t;\n        p.end.cumulativeBlockUnits = after_[t];\n    }\n}\n\n/// MasterChef debt rounding overpays by up to 1 wei per share change; the last claimer underflows rewardReserve.\ncontract RewardReserveUnderflowTest is Test {\n    uint64 constant GENESIS = 1_793_735_400;\n    address owner = makeAddr(\"owner\");\n    address treasury = makeAddr(\"treasury\");\n    CoreStubR core;\n    CheeseEmissionController controller;\n    CheeseToken cheese;\n    CheeseFarmETH farm;\n\n    function setUp() public {\n        vm.warp(GENESIS + 100 * 600 + 17);\n        core = new CoreStubR(GENESIS);\n        controller = new CheeseEmissionController(owner, address(core), treasury);\n        cheese = new CheeseToken(owner, treasury, owner, address(controller));\n        farm = new CheeseFarmETH(owner, address(controller));\n        vm.startPrank(owner);\n        controller.bindToken(address(cheese));\n        controller.addPool(address(farm), 200);\n        controller.activate();\n        vm.stopPrank();\n        core.settle(controller.startRound() - 1, 0);\n        controller.checkpoint();\n    }\n\n    function test_lastClaimUnderflows() public {\n        address[3] memory u = [makeAddr(\"u0\"), makeAddr(\"u1\"), makeAddr(\"u2\")];\n        uint256[6] memory amt = [\n            uint256(10000000000000000000),\n            999999999000000000000413,\n            999999999000000000000601,\n            999999999000000000006234,\n            999999999000001793735401,\n            999999999000000000012429\n        ];\n        uint64[6] memory blocks = [uint64(3), 3, 3, 1, 2, 3];\n        for (uint256 i; i < 6; ++i) {\n            address who = u[i % 3];\n            vm.deal(who, amt[i]);\n            vm.prank(who);\n            farm.deposit{value: amt[i]}();\n            core.settle(core.settled() + 1, blocks[i]);\n        }\n        // Every staker must be able to claim what the farm reports; the third claim reverts with panic 0x11.\n        for (uint256 i; i < 3; ++i) {\n            vm.prank(u[i]);\n            farm.claim(u[i]);\n        }\n        assertEq(farm.rewardReserve() + farm.claimedOf(u[0]) + farm.claimedOf(u[1]) + farm.claimedOf(u[2]),\n            cheese.balanceOf(address(farm)) + farm.claimedOf(u[0]) + farm.claimedOf(u[1]) + farm.claimedOf(u[2]));\n    }\n}","reproduction":"ETH farm (200 pts), emission active, three accounts u0,u1,u2. Deposits in order u0,u1,u2,u0,u1,u2 of 10e18, 999999999000000000000413, 999999999000000000000601, 999999999000000000006234, 999999999000001793735401, 999999999000000000012429 wei, with SqueekCore certifying 3,3,3,1,2,3 blocks after each. Then u0.claim, u1.claim, u2.claim. Expected: all three claims succeed. Actual: u2.claim reverts with panic 0x11 at rewardReserve -= amount. Fix: clamp the payout to rewardReserve (amount = min(amount, rewardReserve)) and keep any remainder pending, or round the debt up when resetting it so the farm never pays more than it pulled.","severity":"medium","snippet":"        rewardReserve -= amount;","title":"Reward-debt rounding overpays: the last claimer's claim underflows rewardReserve and reverts (permanently once emission ends)"},{"citation":"resolved","description":"The retained 14% is added to totalAssets, but share value is (totalAssets + 1) / (totalShares + VIRTUAL_SHARES). When the shares left after a withdrawal are comparable to VIRTUAL_SHARES (1000), most of the retained fee belongs to the virtual shares, not to the remaining depositors. When those last real shares leave, the virtual portion stays in totalAssets with totalShares == 0. Nobody can withdraw it, skimSurplus cannot reach it (_surplus only counts balance above totalAssets), and the treasury does not receive it, even though the brief sends the full 15% to the Treasury when no shares remain. withdraw's `totalShares == 0` branch only handles the case where the exiting withdrawal itself empties the farm.","line":199,"path":"src/CheeseFarm.sol","reproduction":"ETH farm (controller stub, no emission). alice deposits 1 ether (1e21 shares). bob deposits 1 wei (1000 shares). alice withdraws all: retained 0.14 ether. bob.assetsOf = 70000000000000001 (only half). bob withdraws all 1000 shares (last out). Expected: farm balance 0 (bob plus treasury receive everything). Actual: totalShares 0, totalAssets 70000000000000000, farm balance 0.07 ETH, surplus() 0: 0.07 ETH permanently stranded. Fix: when totalShares becomes 0, send the residual totalAssets to the Treasury (or make it skimmable), and/or keep VIRTUAL_SHARES negligible relative to a required minimum deposit.","severity":"low","snippet":"        totalAssets -= (paid + toTreasury); // `retained` stays in the principal reserve","title":"Hamster Bite retained share is captured by the 1000 virtual shares when remaining real shares are dust, and is stranded forever once the farm empties"},{"citation":"resolved","description":"The CheeseEmissionController constructor sets guardian = owner_ (CheeseEmissionController.sol:147). The script constructs it with wiringOwner = CHEESE_DEPLOYER, and handOver() only calls transferOwnership. It never calls setGuardian(cfg.owner), so the deployer keeps pauseEmissions() power after the owner accepts. pauseEmissions closes all deposits, blocks every allocation change, and (finding 3) can make exiting stakers forfeit already-certified emission. That is a privileged power left with an account the brief never assigns it to.","line":53,"path":"script/Deploy.s.sol","reproduction":"Run DeployCheese.deploy(cfg, deployer) with cfg.owner != deployer (the test fixture CheeseBase does exactly this), then owner.acceptOwnership(). controller.guardian() == deployer, not owner. The deployer calls controller.pauseEmissions(): succeeds, and every farm deposit reverts with EmissionsPausedDepositsClosed until the owner unpauses. Expected: guardian is cfg.owner (or one the brief names) after handover. Fix: call d.controller.setGuardian(owner) in handOver before transferOwnership.","severity":"low","snippet":"        d.controller = new CheeseEmissionController(wiringOwner, cfg.squeekCore, cfg.treasury);","title":"Deploy script leaves the deployer EOA as emissions guardian after ownership is handed to the owner"}],"hash":"2fe060d345f278794deed43a81340d83611513024f3d6787e013be177f025fd0","nodeId":"5d07b6bd-19f8-4962-a49e-a7f3973cb223","outcome":"completed","summary":"I found six defects (1 high, 3 medium, 2 low) in the CHEESE contracts' control flow. The four most severe each have a Foundry test that fails on the code as it stands. Findings and a coverage row for all 51 entry points, plus three invariants, are in `.imd-findings.json`. I changed no source files and deleted the scratch tests afterwards.\n\n| # | Severity | Where | Defect |\n|---|---|---|---|\n| 1 | high | `src/CheeseFarm.sol:281` | **Any farm can be permanently locked for 1 wei.** A 1-wei first deposit gives the farm 1,000 shares, so the per-share reward counter grows enormously while only that dust is staked. After about one day of ETH-pool emission the counter is about 4.2e57. From then on, any deposit above roughly 0.027 ETH (including an ordinary 10 ETH one) reverts with an arithmetic overflow, forever. Applies to all three farms. |\n| 2 | medium | `src/CheeseFarm.sol:159` | **Newcomers can take rewards earned before they joined.** Deposits are only closed when emissions are paused, not when the checkpoint fails because SqueekCore is unreadable or reports a lower height. Alice stakes, 1,000 blocks are certified, Core goes down, Bob deposits; when Core recovers each gets 3.67e23 instead of Alice getting 7.35e23. |\n| 3 | medium | `src/CheeseEmissionController.sol:202` | **Pausing can wipe out rewards already earned.** `pauseEmissions` doesn't checkpoint first. If Alice withdraws and claims during the pause she gets 0. The 7.35e23 CHEESE she earned is then forfeited by the first deposit after unpausing. |\n| 4 | medium | `src/CheeseFarm.sol:214` | **The last claimer can be locked out of all their rewards.** Rounding when each account's reward baseline is reset overpays by up to 1 wei per deposit or withdrawal. The last claimer's `claim` then underflows `rewardReserve` and reverts in full. Once emission ends nothing refills the reserve, so it stays stuck. Reproduced with a concrete 6-deposit sequence. |\n| 5 | low | `src/CheeseFarm.sol:199` | **Part of the 14% retained fee can be stranded.** When the remaining real shares are dust, most of it goes to the 1,000 virtual shares, and it stays stuck once the farm empties. Example: 0.07 ETH left in `totalAssets` with zero shares, which neither withdraw nor `skimSurplus` can reach. |\n| 6 | low | `script/Deploy.s.sol:53` | **The deployer keeps pause power after handover.** The guardian is set to the deployer and `handOver` never calls `setGuardian`. |\n\n**Coverage:**\n- **Holds:** the emission schedule (the halving and launch-bonus arithmetic, the supply cap, no double counting, no minting before activation), the inactive Future Pool Reserve, allocation changes applying prospectively, single-use registration, `skimSurplus`, `receive`, `setAsset` and the token's functions.\n- **SqueekCore point read:** I checked against the live docs that the start height is read the documented way (`roundPage(r, r, 0)`).\n- **Reentrancy:** the leads from slither and aderyn did not reproduce. The farms update their state before making calls out, and are guarded against reentry.\n- **Trust assumptions, not findings:** the owner can renounce ownership while paused, after which nobody can unpause. A Treasury address that refuses ETH blocks ETH-farm withdrawals that pay a fee.\n- **Not done:** I did not check on-chain how SqueekCore behaves when it is itself paused, and I didn't fully trace the math-area rounding edges, such as fee-free withdrawals of 6 wei or less.","treeHash":null,"usage":{"cachedInputTokens":1930126,"cost":{"source":"runtime","usdMicros":2065590},"inputTokens":45,"model":"claude-opus-5-5","outputTokens":40930,"runtime":"claude","turns":26,"wallClockMs":438130}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"23617eab0c5242d6","findings":[],"hash":"3ce918eed7dd38dfa2bb1347569360f0a92e242cb04927dd89f684c9334201d3","nodeId":"1cb4d195-ab3c-45b3-ac90-52bdcd8e92b4","outcome":"failed","summary":"Credit balance is too low","treeHash":null,"usage":{"cachedInputTokens":546070,"cost":{"source":"runtime","usdMicros":799903},"inputTokens":17,"model":"claude-opus-5-5","outputTokens":16242,"runtime":"claude","turns":12,"wallClockMs":213634}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"ddfb1efa72fe9a94","findings":[{"citation":"resolved","description":"Reward accounting uses ACC_PRECISION = 1e36 and plain 256-bit multiplication in _deposit (line 171), withdraw (line 193), pendingRewards (line 232) and _harvestDebt (line 288). _accrue adds amount * 1e36 / totalShares (line 281). A deposit of 1 wei mints exactly 1000 shares (virtual offset 1e3), so while the farm holds only that dust every certified Squeek block adds about 5.2e53 to accRewardPerShare for the ETH pool (2.59e21 R0 * 20% * 1e36 / 1000) and about 1e54 for the CHEESE pool. After N dust-only blocks any account whose share balance S satisfies S * acc >= 2^256 reverts with panic 0x11: for the ETH farm that is a deposit above roughly 2.2e20 / N wei of ETH (about 158 ETH after one block during the launch bonus, 0.66 ETH after one hour, 0.027 ETH after one day). accRewardPerShare never decreases, so the ceiling is permanent: deposits above it revert forever, pendingRewards for such balances reverts, and the owner has no way to reset the accumulator. Cost to the griefer is 1 wei plus gas, at any moment a farm is empty while emission is active (launch, after the last staker leaves, when the sIMD asset is first set). Fix: compute these four products with Math.mulDiv (512-bit intermediate) or lower ACC_PRECISION to 1e12-1e18.","line":171,"path":"src/CheeseFarm.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {CheeseToken} from \"src/CheeseToken.sol\";\nimport {CheeseEmissionController} from \"src/CheeseEmissionController.sol\";\nimport {CheeseFarmETH} from \"src/farms/CheeseFarmETH.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Minimal SqueekCore double: settle() certifies rounds and completes blocks.\ncontract CoreStub is ISqueekCore {\n    uint64 public genesis;\n    uint64 public settledThroughRound;\n    uint96 public units;\n    mapping(uint64 => uint96) public unitsAfter;\n\n    constructor(uint64 g) {\n        genesis = g;\n    }\n\n    function settle(uint64 round, uint64 blocks) external {\n        units += uint96(blocks) * 1_000_000_000;\n        for (uint64 r = settledThroughRound + 1; r <= round; ++r) unitsAfter[r] = units;\n        settledThroughRound = round;\n    }\n\n    function state() external view returns (CoreState memory r) {\n        r.settledThroughRound = settledThroughRound;\n        r.cumulativeBlockUnits = units;\n        r.blockHeight = uint64(units / 1_000_000_000);\n    }\n\n    function configuration() external view returns (CoreConfiguration memory r) {\n        r.genesisTimestamp = genesis;\n        r.roundDuration = 600;\n        r.blockUnitsPerBlock = 1_000_000_000;\n    }\n\n    function roundPage(uint64 a, uint64 b, uint256) external view returns (RoundPage memory r) {\n        r.certifiedThroughRound = b;\n        r.start.cumulativeBlockUnits = unitsAfter[a];\n        r.end.cumulativeBlockUnits = unitsAfter[b];\n    }\n\n    function blockInfo(uint64) external pure returns (BlockInfo memory r) {}\n\n    function paused() external pure returns (bool) {\n        return false;\n    }\n}\n\n/// @notice A 1 wei first deposit followed by a few hours of emission pushes accRewardPerShare so high that\n///         `sharesOf * accRewardPerShare` overflows for any ordinary deposit; the farm then refuses every\n///         deposit above a shrinking ceiling forever (accRewardPerShare never decreases).\ncontract AccOverflowTest is Test {\n    uint64 constant GENESIS = 1_793_735_400;\n    address owner = makeAddr(\"owner\");\n    address treasury = makeAddr(\"treasury\");\n    address griefer = makeAddr(\"griefer\");\n    address alice = makeAddr(\"alice\");\n\n    CoreStub core;\n    CheeseEmissionController controller;\n    CheeseToken cheese;\n    CheeseFarmETH farm;\n\n    function setUp() public {\n        vm.warp(GENESIS + 100 * 600 + 17);\n        core = new CoreStub(GENESIS);\n        controller = new CheeseEmissionController(owner, address(core), treasury);\n        cheese = new CheeseToken(owner, owner, owner, address(controller));\n        farm = new CheeseFarmETH(owner, address(controller));\n        vm.startPrank(owner);\n        controller.bindToken(address(cheese));\n        controller.addPool(address(farm), 200);\n        controller.activate();\n        vm.stopPrank();\n        // certify through startRound - 1 with no blocks so h0 == 0\n        core.settle(controller.startRound() - 1, 0);\n        controller.checkpoint();\n        assertTrue(controller.startCaptured());\n        vm.deal(griefer, 1 ether);\n        vm.deal(alice, 1000 ether);\n    }\n\n    function test_oneWeiDepositThenEmissionBricksOrdinaryDeposits() public {\n        // Griefer is the first and only staker with 1 wei (1000 shares).\n        vm.prank(griefer);\n        farm.deposit{value: 1}();\n        assertEq(farm.totalShares(), 1000);\n\n        // 1000 Squeek blocks (about four hours) get certified; the pool's 20% share accrues to 1000 shares.\n        core.settle(core.settledThroughRound() + 1, 1000);\n\n        // Alice deposits 1 ETH: expected to succeed and mint ~1e21 shares.\n        // Actual: `sharesOf * accRewardPerShare` overflows (panic 0x11) at CheeseFarm._deposit.\n        vm.prank(alice);\n        farm.deposit{value: 1 ether}();\n\n        // Once fixed, the deposit lands and the view functions keep working.\n        assertGt(farm.sharesOf(alice), 0);\n        farm.pendingRewards(alice);\n    }\n}","reproduction":"Fresh ETH farm (200 points), emission active with h0 captured. 1) griefer: ethFarm.deposit{value: 1}() -> totalShares == 1000. 2) SqueekCore certifies 1000 more blocks (about 4 hours); no further action needed. 3) alice: ethFarm.deposit{value: 1 ether}(). Expected: alice receives ~1e21 shares. Actual: _accrue sets accRewardPerShare ~ 7.3e56, then line 171 computes 1e21 * 7.3e56 = 7.3e77 > 2^256 and the transaction reverts with panic 0x11. Every later deposit of 1 ether or more by any account reverts the same way.","severity":"high","snippet":"        _rewardDebt[msg.sender] = (sharesOf[msg.sender] * accRewardPerShare) / ACC_PRECISION;","title":"1 wei first deposit lets accRewardPerShare grow until sharesOf*accRewardPerShare overflows, permanently refusing ordinary deposits"},{"citation":"resolved","description":"The brief requires that new depositors never receive rewards accrued before their eligibility. The farm enforces this by calling _accrue (controller.pullRewards -> _sync) before minting shares, and by closing deposits while emissions are paused because a paused controller cannot price blocks. The same gap exists when _sync fails for any other reason: SqueekCore.state() reverting (CoreUnavailable, CheeseEmissionController.sol:469-474) or a height regression (CoreRegression, line 494-497). pullRewards swallows the failure, _settle runs on the stale emissionPerPoint, and _deposit mints shares. When Core becomes readable again, the next checkpoint prices every block certified during and before the outage and the farm spreads it over all current shares, including the depositor who was not staked when those blocks were certified. The controller already refuses allocation changes in that state (_requireSync reverts with SyncRequired); deposits should be refused the same way, e.g. have pullRewards return the synced flag and revert _deposit when it is false, or call controller.checkpoint() in _deposit and revert on false.","line":159,"path":"src/CheeseFarm.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {CheeseToken} from \"src/CheeseToken.sol\";\nimport {CheeseEmissionController} from \"src/CheeseEmissionController.sol\";\nimport {CheeseFarmETH} from \"src/farms/CheeseFarmETH.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Minimal SqueekCore double with an availability switch.\ncontract CoreStub is ISqueekCore {\n    uint64 public genesis;\n    uint64 public settledThroughRound;\n    uint96 public units;\n    bool public down;\n    mapping(uint64 => uint96) public unitsAfter;\n\n    constructor(uint64 g) {\n        genesis = g;\n    }\n\n    function setDown(bool v) external {\n        down = v;\n    }\n\n    function settle(uint64 round, uint64 blocks) external {\n        units += uint96(blocks) * 1_000_000_000;\n        for (uint64 r = settledThroughRound + 1; r <= round; ++r) unitsAfter[r] = units;\n        settledThroughRound = round;\n    }\n\n    function state() external view returns (CoreState memory r) {\n        require(!down, \"core unavailable\");\n        r.settledThroughRound = settledThroughRound;\n        r.cumulativeBlockUnits = units;\n        r.blockHeight = uint64(units / 1_000_000_000);\n    }\n\n    function configuration() external view returns (CoreConfiguration memory r) {\n        r.genesisTimestamp = genesis;\n        r.roundDuration = 600;\n        r.blockUnitsPerBlock = 1_000_000_000;\n    }\n\n    function roundPage(uint64 a, uint64 b, uint256) external view returns (RoundPage memory r) {\n        require(!down, \"core unavailable\");\n        r.certifiedThroughRound = b;\n        r.start.cumulativeBlockUnits = unitsAfter[a];\n        r.end.cumulativeBlockUnits = unitsAfter[b];\n    }\n\n    function blockInfo(uint64) external pure returns (BlockInfo memory r) {}\n\n    function paused() external pure returns (bool) {\n        return false;\n    }\n}\n\n/// @notice While SqueekCore cannot be read, deposits still go through. Blocks certified before the deposit\n///         are then priced at the next successful checkpoint and split with the new depositor, which the\n///         brief forbids (\"new depositors must never receive rewards accrued before their eligibility\").\ncontract UnavailableInheritTest is Test {\n    uint64 constant GENESIS = 1_793_735_400;\n    address owner = makeAddr(\"owner\");\n    address treasury = makeAddr(\"treasury\");\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n\n    CoreStub core;\n    CheeseEmissionController controller;\n    CheeseToken cheese;\n    CheeseFarmETH farm;\n\n    function setUp() public {\n        vm.warp(GENESIS + 100 * 600 + 17);\n        core = new CoreStub(GENESIS);\n        controller = new CheeseEmissionController(owner, address(core), treasury);\n        cheese = new CheeseToken(owner, owner, owner, address(controller));\n        farm = new CheeseFarmETH(owner, address(controller));\n        vm.startPrank(owner);\n        controller.bindToken(address(cheese));\n        controller.addPool(address(farm), 200);\n        controller.activate();\n        vm.stopPrank();\n        core.settle(controller.startRound() - 1, 0);\n        controller.checkpoint();\n        assertTrue(controller.startCaptured());\n        vm.deal(alice, 10 ether);\n        vm.deal(bob, 10 ether);\n    }\n\n    function test_depositDuringCoreOutageInheritsEarlierBlocks() public {\n        vm.prank(alice);\n        farm.deposit{value: 1 ether}();\n\n        // 100 blocks are certified while only Alice is staked, but nobody checkpoints them.\n        core.settle(core.settledThroughRound() + 1, 100);\n\n        // SqueekCore becomes unreadable; Bob deposits an equal amount during the outage.\n        core.setDown(true);\n        vm.prank(bob);\n        farm.deposit{value: 1 ether}();\n        core.setDown(false);\n\n        // The outage ends and the 100 earlier blocks are priced.\n        controller.checkpoint();\n\n        uint256 alicePending = farm.pendingRewards(alice);\n        uint256 bobPending = farm.pendingRewards(bob);\n        assertGt(alicePending, 0, \"alice earns the 100 blocks\");\n        // Expected: Bob was not staked when those blocks were certified, so he receives nothing from them.\n        // Actual: Bob receives half of the 100 blocks' emission.\n        assertEq(bobPending, 0, \"bob must not inherit emission from before his deposit\");\n    }\n}","reproduction":"ETH farm, 200 points, emission active. 1) alice deposits 1 ether. 2) SqueekCore certifies 100 blocks; nobody checkpoints. 3) SqueekCore.state() starts reverting (upgrade, pause, outage). 4) bob deposits 1 ether: _accrue emits CoreUnavailable, pullRewards returns 0, bob gets ~1e21 shares. 5) Core readable again; anyone calls controller.checkpoint(). Expected: alice's pendingRewards equals the full ETH-pool share of the 100 blocks (about 7.35e22) and bob's is 0. Actual: each shows about 3.67e22; bob takes half of emission for blocks certified before he was eligible.","severity":"medium","snippet":"        if (controller.emissionsPaused()) revert EmissionsPausedDepositsClosed();","title":"Deposits are accepted while SqueekCore is unreadable, so a new depositor inherits emission for blocks certified before their deposit"},{"citation":"resolved","description":"The 14% retained share is credited to all remaining shares, including the withdrawer's own. A depositor who holds most of the farm therefore recovers most of the retained 14% on every partial withdrawal, and only the final full withdrawal (or the 1% Treasury cut) is a real cost. The brief accepts that partial withdrawers may benefit, but the magnitude means the documented 15% fee is only paid by small holders exiting at once: for a sole depositor the effective fee converges to 1 - 0.85/0.86 = 1.16%, and for a holder with share fraction p the effective fee is roughly 1 - 0.85/(1 - 0.14 p). Whether this is acceptable is a product decision; if the 15% is meant to bind, the retained share must exclude the withdrawer's remaining shares (e.g. credit retained to totalAssets only in proportion to other holders' shares, or route the withdrawer's own portion to the Treasury).","line":199,"path":"src/CheeseFarm.sol","reproduction":"ETH farm with alice as sole depositor. 1) alice deposits 10 ether (1e22 shares). 2) alice calls withdraw(sharesOf/2) forty times, then withdraw(sharesOf) for the remainder. Expected under a 15% fee: alice receives 8.5 ether and the Treasury 1.5 ether in total. Actual: alice receives 9.8837 ether and the Treasury 0.1163 ether, an effective fee of 116 bps. Reproduced in test/scratch/FeeSplit.t.sol test_splitWithdrawReducesFee (logs: paid 9883720929994850543, treasury 116279070005149269).","severity":"low","snippet":"        totalAssets -= (paid + toTreasury); // `retained` stays in the principal reserve","title":"Hamster Bite shrinks to about 1.2% for a dominant depositor who withdraws in instalments"},{"citation":"resolved","description":"The farm tracks principal in totalAssets from the amounts received and never from balances, which is the right design for donation resistance but assumes the asset's balance is static. The sIMD token is unknown at launch and set once by the owner; a staked-IMD receipt token is plausibly rebasing. If balances shrink, totalAssets overstates holdings and the farm pays early withdrawers out of later depositors' tokens until the last withdrawals revert for lack of balance (withdraw -> _push -> safeTransfer fails). If balances grow, the extra is counted as surplus and the owner can move depositors' yield to the Treasury with skimSurplus. Neither case is documented in docs/SECURITY.md. Recommend documenting that sIMD must be a non-rebasing ERC-20, and checking that before setAsset, or redesigning the farm around balance-based accounting if sIMD rebases.","line":28,"path":"src/farms/CheeseFarmSIMD.sol","reproduction":"Mock rebasing ERC-20 set as sIMD asset. Case A: alice and bob each deposit 10e18; the token halves every balance (farm holds 10e18, totalAssets 20e18); alice withdraws all her shares and is paid 8.5e18 from the shared balance; bob's withdraw(sharesOf(bob)) reverts (insufficient balance) although assetsOf(bob) reports 11.4e18. Case B: alice deposits 10e18; the token doubles balances; surplus() returns 10e18 and the owner's skimSurplus() sends alice's 10e18 of yield to the Treasury while assetsOf(alice) stays 10e18. Both reproduced in test/scratch/FeeSplit.t.sol.","severity":"low","snippet":"        if (asset_.code.length == 0) revert AssetIsNotAContract();","title":"sIMD farm breaks if the owner-set sIMD asset rebases: negative rebases strand the last withdrawers, positive rebases are skimmed to the Treasury"}],"hash":"40cabcc8573212cb33e1bb7f4697ead09de3a9ed010055a7c3936460ecb68523","nodeId":"1cb4d195-ab3c-45b3-ac90-52bdcd8e92b4","outcome":"completed","summary":"Review complete. The findings file at `.imd-findings.json` holds four findings and a coverage row for all 51 entry points. No source files were changed; the only writes are the findings file and three scratch tests under `test/scratch/`.\n\n**Findings**\n\n1. **High: 1 wei deposit bricks ordinary deposits.** Reward accounting multiplies `sharesOf * accRewardPerShare` with a 1e36 precision constant in plain 256-bit arithmetic (`src/CheeseFarm.sol:171`, also lines 193, 232, 288). A 1 wei first deposit mints exactly 1000 shares, so every Squeek block certified while the farm holds only that dust adds roughly 5e53 to the accumulator. Any later deposit above a shrinking ceiling reverts with an overflow panic, forever, since the accumulator never decreases. The ceiling is about 158 ETH after one block, under 1 ETH after an hour, and 0.03 ETH after a day. Cost to the griefer is 1 wei plus gas, at any moment a farm is empty while emission runs. Proof: `test/scratch/AccOverflow.t.sol`, fails with panic 0x11. Fix is to use `Math.mulDiv` at the four sites or lower the precision constant.\n\n2. **Medium: deposits during a SqueekCore outage inherit earlier emission.** Deposits are closed while emissions are paused but not when the controller cannot read SqueekCore (`src/CheeseFarm.sol:159`). The farm mints shares on a failed sync, and the next successful checkpoint splits blocks certified before the deposit with the new depositor, which the brief forbids. The controller already refuses allocation changes in that state, so deposits should refuse the same way. Proof: `test/scratch/UnavailableInherit.t.sol`, fails with Bob holding half of Alice's rewards.\n\n3. **Low: Hamster Bite shrinks to about 1.2% for a dominant holder.** The retained 14% is credited to the withdrawer's own remaining shares (`src/CheeseFarm.sol:199`). A sole depositor withdrawing in halves pays an effective fee of 116 bps instead of 1500. The brief accepts that partial withdrawers benefit, so this is a product decision to confirm.\n\n4. **Low: sIMD farm is incompatible with a rebasing asset.** Internal reserve accounting means a negative rebase strands the last withdrawers and a positive rebase becomes owner-skimmable surplus (`src/farms/CheeseFarmSIMD.sol:28`). The sIMD token is unknown at launch and this is undocumented.\n\n**Coverage**\n\nAll three guides for the economics area were applied: dependency failure and token misbehaviour (Economic Security), conservation and cap invariants, round-trips, view and write divergence, emergency transitions (Invariant), and the execution by periphery by purpose seams (Flow Gap). The emission schedule, halving crossings, allowance clip, completion, weight changes, pool add and enable paths, forfeiture, and the fee split were traced and hold. The existing 70 tests pass. Static analysis leads were checked and none rose to a finding on their own. Items outside my area such as the real SqueekCore round numbering and ABI were not verified on-chain.","treeHash":null,"usage":{"cachedInputTokens":1447621,"cost":{"source":"runtime","usdMicros":5320390},"inputTokens":418,"model":"claude-fable-5-1","outputTokens":47490,"runtime":"claude","turns":36,"wallClockMs":674499}},{"artifacts":[],"attempt":1,"bundleHash":"565c4d24ec451ac2d6bc9df92bde20615e80484f7646f6b23f4255298d080c67","device":"7e64d4917fa814e6","findings":[{"description":"Reward debt is set as floor(shares * accRewardPerShare / 1e36) on every deposit and withdrawal (lines 171, 193), and _harvestDebt credits floor(shares*acc) - debt. Each account's floor error is in (-1, +1) wei, and they do not cancel across accounts, so the sum of claimable rewards can exceed the CHEESE the farm actually pulled (rewardReserve) by a wei or more. claim() pays the full pending amount in one go and does `rewardReserve -= amount`, which underflows (panic 0x11) for whoever claims last. While emission continues a later accrual can cover the shortfall; after the schedule completes nothing tops rewardReserve up (donations count as surplus, not reserve), so that account's whole accrued reward is locked forever. A stateless fuzz with three depositors found it in 5 runs.","line":214,"path":"src/CheeseFarm.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {CheeseToken} from \"src/CheeseToken.sol\";\nimport {CheeseEmissionController} from \"src/CheeseEmissionController.sol\";\nimport {CheeseFarmETH} from \"src/farms/CheeseFarmETH.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Minimal SqueekCore double: certifies rounds and completed blocks.\ncontract CoreDouble {\n    uint64 public genesis;\n    uint64 public settled;\n    uint96 public units;\n    mapping(uint64 => uint96) public after_;\n\n    constructor(uint64 g) {\n        genesis = g;\n    }\n\n    function settle(uint64 round, uint64 blocks) external {\n        units += uint96(blocks) * 1e9;\n        for (uint64 r = settled + 1; r <= round; ++r) {\n            after_[r] = units;\n        }\n        settled = round;\n    }\n\n    function state() external view returns (ISqueekCore.CoreState memory s) {\n        s.settledThroughRound = settled;\n        s.cumulativeBlockUnits = units;\n        s.blockHeight = uint64(units / 1e9);\n    }\n\n    function configuration() external view returns (ISqueekCore.CoreConfiguration memory c) {\n        c.genesisTimestamp = genesis;\n        c.roundDuration = 600;\n        c.blockUnitsPerBlock = 1e9;\n    }\n\n    function roundPage(uint64 a, uint64 t, uint256) external view returns (ISqueekCore.RoundPage memory p) {\n        p.certifiedThroughRound = t;\n        p.start.cumulativeBlockUnits = after_[a];\n        p.end.cumulativeBlockUnits = after_[t];\n    }\n}\n\n/// @notice Every depositor must be able to claim the rewards pendingRewards() reports. Per-account reward\n///         debt is floored (CheeseFarm._deposit / withdraw / _harvestDebt), so the sum of what accounts can\n///         claim exceeds rewardReserve by a wei; the last claimer's claim() underflows `rewardReserve -= amount`\n///         and reverts, permanently once emission has completed.\ncontract RoundingOverclaimProof is Test {\n    address owner = makeAddr(\"owner\");\n    address treasury = makeAddr(\"treasury\");\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n    address carol = makeAddr(\"carol\");\n    CoreDouble core;\n    CheeseEmissionController controller;\n    CheeseToken cheese;\n    CheeseFarmETH farm;\n\n    function setUp() public {\n        uint64 genesis = 1_793_735_400;\n        vm.warp(genesis + 100 * 600 + 17);\n        core = new CoreDouble(genesis);\n        controller = new CheeseEmissionController(owner, address(core), treasury);\n        cheese = new CheeseToken(owner, owner, owner, address(controller));\n        farm = new CheeseFarmETH(owner, address(controller));\n        vm.startPrank(owner);\n        controller.bindToken(address(cheese));\n        controller.addPool(address(farm), 200);\n        controller.activate();\n        vm.stopPrank();\n        core.settle(controller.startRound() - 1, 0);\n        controller.checkpoint();\n        vm.deal(alice, 1 ether);\n        vm.deal(bob, 10 ether);\n        vm.deal(carol, 1 ether);\n    }\n\n    function _advance(uint64 blocks) internal {\n        core.settle(core.settled() + 1, blocks);\n    }\n\n    function test_everyDepositorCanClaimAfterEmissionEnds() public {\n        vm.prank(alice);\n        farm.deposit{value: 12021}();\n        _advance(16);\n        vm.prank(bob);\n        farm.deposit{value: 2376452954}();\n        _advance(1);\n        vm.prank(carol);\n        farm.deposit{value: 2231}();\n        vm.prank(alice);\n        farm.withdraw(17);\n        _advance(44);\n        controller.checkpoint();\n        // Emission ends: no further CHEESE will ever reach the farm.\n        _advance(3 * 393_470);\n        controller.checkpoint();\n        assertTrue(controller.completed());\n\n        uint256 owedAlice = farm.pendingRewards(alice);\n        uint256 owedBob = farm.pendingRewards(bob);\n        uint256 owedCarol = farm.pendingRewards(carol);\n        vm.prank(carol);\n        assertEq(farm.claim(carol), owedCarol);\n        vm.prank(bob);\n        assertEq(farm.claim(bob), owedBob);\n        // Expected: alice receives what pendingRewards reported. Actual: panic 0x11 (rewardReserve underflow).\n        vm.prank(alice);\n        assertEq(farm.claim(alice), owedAlice);\n    }\n}","reproduction":"ETH farm (200 points), h0 = 0. alice deposits 12021 wei; 16 blocks certified; bob deposits 2376452954 wei; 1 block; carol deposits 2231 wei; alice withdraws 17 shares; 44 blocks; checkpoint; then 3*393470 blocks and checkpoint so emission is completed. carol claims, bob claims, then alice's claim(): expected it pays pendingRewards(alice) = 11758793525149482335180; actual it reverts with panic 0x11 because rewardReserve is 11758793525149482335179 (1 wei short). Fix suggestion: cap the payout at rewardReserve (pay min(amount, rewardReserve)), or round debt up / track per-account entitlement so the sum never exceeds what was pulled.","severity":"medium","title":"Floored per-account reward debt lets accounts jointly claim more than rewardReserve; the last claimer's claim() reverts, permanently after emission ends"},{"description":"_deposit only refuses deposits when emissionsPaused is set. When SqueekCore.state() reverts (or Core reports a height regression), the controller's _sync inside pullRewards returns false without pricing and the deposit goes ahead. Once Core is readable again the next checkpoint prices every block certified during the outage, including blocks certified before the deposit, and the pool's share is spread over all current shares. The late depositor gets rewards from before they were eligible, against the brief's rule that new depositors must never receive earlier emission. The same applies to a first depositor into an empty farm: emission that should have been forfeited is paid to them. The README/NatSpec say Core unavailability is tolerated without loss; it is not tolerated without misallocation.","line":159,"path":"src/CheeseFarm.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {CheeseToken} from \"src/CheeseToken.sol\";\nimport {CheeseEmissionController} from \"src/CheeseEmissionController.sol\";\nimport {CheeseFarmETH} from \"src/farms/CheeseFarmETH.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev Minimal SqueekCore double: certifies rounds and completed blocks.\ncontract CoreDouble {\n    uint64 public genesis;\n    bool public down;\n    uint64 public settled;\n    uint96 public units;\n    mapping(uint64 => uint96) public after_;\n\n    constructor(uint64 g) {\n        genesis = g;\n    }\n\n    function settle(uint64 round, uint64 blocks) external {\n        units += uint96(blocks) * 1e9;\n        for (uint64 r = settled + 1; r <= round; ++r) {\n            after_[r] = units;\n        }\n        settled = round;\n    }\n\n    function setDown(bool d) external {\n        down = d;\n    }\n\n    function state() external view returns (ISqueekCore.CoreState memory s) {\n        require(!down, \"core unavailable\");\n        s.settledThroughRound = settled;\n        s.cumulativeBlockUnits = units;\n        s.blockHeight = uint64(units / 1e9);\n    }\n\n    function configuration() external view returns (ISqueekCore.CoreConfiguration memory c) {\n        c.genesisTimestamp = genesis;\n        c.roundDuration = 600;\n        c.blockUnitsPerBlock = 1e9;\n    }\n\n    function roundPage(uint64 a, uint64 t, uint256) external view returns (ISqueekCore.RoundPage memory p) {\n        p.certifiedThroughRound = t;\n        p.start.cumulativeBlockUnits = after_[a];\n        p.end.cumulativeBlockUnits = after_[t];\n    }\n}\n\n/// @notice A depositor must never receive emission for Squeek blocks certified before their deposit. While\n///         SqueekCore.state() reverts, CheeseFarm._deposit still accepts deposits (only emissionsPaused closes\n///         them), pullRewards' best-effort _sync silently fails, and the blocks certified before the deposit are\n///         priced later and spread over the late depositor's shares too.\ncontract OutageRetroactiveRewardProof is Test {\n    address owner = makeAddr(\"owner\");\n    address treasury = makeAddr(\"treasury\");\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n    CoreDouble core;\n    CheeseEmissionController controller;\n    CheeseToken cheese;\n    CheeseFarmETH farm;\n\n    function setUp() public {\n        uint64 genesis = 1_793_735_400;\n        vm.warp(genesis + 100 * 600 + 17);\n        core = new CoreDouble(genesis);\n        controller = new CheeseEmissionController(owner, address(core), treasury);\n        cheese = new CheeseToken(owner, owner, owner, address(controller));\n        farm = new CheeseFarmETH(owner, address(controller));\n        vm.startPrank(owner);\n        controller.bindToken(address(cheese));\n        controller.addPool(address(farm), 200);\n        controller.activate();\n        vm.stopPrank();\n        core.settle(controller.startRound() - 1, 0);\n        controller.checkpoint();\n        vm.deal(alice, 1 ether);\n        vm.deal(bob, 100 ether);\n    }\n\n    function test_lateDepositorDuringOutageGetsNoEarlierEmission() public {\n        vm.prank(alice);\n        farm.deposit{value: 1 ether}();\n        core.settle(core.settled() + 1, 1000); // certified while alice is the only depositor\n        core.setDown(true);\n        // Expected: the deposit is refused (or the earlier blocks are attributed to alice alone).\n        vm.prank(bob);\n        try farm.deposit{value: 99 ether}() {} catch {}\n        core.setDown(false);\n        controller.checkpoint();\n        // Expected 0 for bob; actual ~99% of the 1000 blocks' pool emission.\n        assertEq(farm.pendingRewards(bob), 0, \"bob earned emission from blocks before their deposit\");\n    }\n}","reproduction":"alice deposits 1 ETH; 1000 blocks are certified; SqueekCore.state() starts reverting; bob deposits 99 ETH (succeeds); Core recovers; checkpoint. Expected pendingRewards(bob) == 0 and alice gets the whole issued(1000)*200/1000 = 734914140002202624511650. Actual: bob 727564998602180598266533, alice 7349141400022026245116. Fix suggestion: have _deposit require that the controller sync succeeded (e.g. revert when pullRewards could not bring accounting current), as it already does for emissionsPaused.","severity":"medium","title":"Deposits stay open while SqueekCore is unreadable, so late depositors receive emission for blocks certified before they deposited"},{"description":"_sync wraps squeekCore.state() in try/catch, but Solidity does not catch an ABI decoding failure of the return data (or a call to an address without code). If the upgradeable SqueekCore proxy ever returns data in a different shape, pullRewards reverts, and since every withdraw() and claim() calls pullRewards, principal cannot be withdrawn. The guardian or owner can restore withdrawals by calling pauseEmissions() (the paused branch returns before reading Core); that mitigation is covered by test_malformedCoreDataBlocksUntilPausedThenWithdrawalsWork in test/Adversarial.t.sol. While unpaused, user principal depends on an external contract's ABI.","line":469,"path":"src/CheeseEmissionController.sol","reproduction":"vm.mockCall(core, state.selector, abi.encode(uint256(1))); alice.withdraw(1000) reverts (expected: succeeds, as the NatSpec says withdrawals must never be blocked by Squeekchain availability). After controller.pauseEmissions() the same withdraw succeeds. Fix suggestion: make the Core read a low-level staticcall and check returndata length before decoding.","severity":"low","title":"Malformed SqueekCore return data blocks every withdrawal and claim until the guardian pauses emissions"},{"description":"The batch applies decreases in a first pass and increases in a second pass, comparing against the pool's weight at that moment. With a pool id repeated, the result depends on the pass ordering, not on input order, so an owner's batch can leave a higher weight than the one they listed last.","line":254,"path":"src/CheeseEmissionController.sol","reproduction":"Initial allocation (pool 0 = 400 points). batchSetPoolWeights([0,0],[500,100]). Expected (sequential semantics, last write wins) pool 0 = 100; actual pool 0 = 500 (900 of 1000 points assigned). Fix suggestion: reject duplicate ids in the batch.","severity":"low","title":"batchSetPoolWeights with a repeated pool id does not apply the last weight given"}],"hash":"544321344773be95daf0bd75a7b28f522f7fc7a25cc907813ee785b2ca5f1efc","nodeId":"24fa880d-8de5-47bc-854d-870c7e3ad465","outcome":"completed","summary":"I added two test files and reported four defects. With the verifier's fuzz seed, `forge build` and `forge test` both pass: 108 tests in 9 suites, 46 of them new. Nothing was committed.\n\n## Defects found (in `.imd-findings.json`)\n\nI wrote these up instead of writing passing tests that assert the wrong behaviour. Each was reproduced with a concrete input, and the two medium ones each have a standalone test that fails on the current code.\n\n1. **Medium: the last person to claim rewards can be locked out permanently** (`src/CheeseFarm.sol:214`). Each account's reward debt is rounded down, and the errors add up across accounts. Together, users can be owed 1 wei more than the farm holds (`rewardReserve`). The final `claim()` then reverts on underflow, and since a claim pays everything at once, that user gets nothing. Once the emission schedule ends, nothing tops the reserve back up, so the lock is permanent. Example: deposits of 12021, 2376452954 and 2231 wei, a partial withdrawal of 17 shares, then the schedule ends. Alice is owed …35180 but the reserve holds …35179, and her claim panics (0x11).\n2. **Medium: a late depositor can collect rewards from before they joined** (`src/CheeseFarm.sol:159`). Deposits are only closed while emissions are paused, not while SqueekCore can't be read. If someone deposits during an outage, the blocks certified before their deposit are paid out once Core recovers, partly to them. In the repro, Bob (99 ETH, deposited during the outage) got about 99% of the emission from blocks that Alice alone was staked for. This breaks the brief's \"no retroactive rewards\" rule.\n3. **Low: badly formatted SqueekCore data blocks all withdrawals and claims.** `try/catch` doesn't catch data that fails to decode, so withdrawals revert until the guardian calls `pauseEmissions()`. That workaround is covered by a test.\n4. **Low: `batchSetPoolWeights([0,0],[500,100])` leaves pool 0 at 500, not 100**, because increases are applied after decreases, whatever order they were given in.\n\n## Tests added\n\n- **`test/Adversarial.t.sol` (37 tests)**, mostly failure paths:\n  - **Wrong callers and bad arguments:** zero addresses, unknown pool ids, mismatched or empty batches, an over-weight batch that applies nothing, double disable or enable, the pause rules, and two-step ownership.\n  - **Emission edges:** the exact emission of each single block either side of the launch-bonus end and every halving, partial blocks never emitting, a Core regression and recovery without double counting, and completion never reopening.\n  - **Allocation:** disabled and re-enabled pools, and weight-0 pools.\n  - **Farms:** an unconfigured sIMD farm forfeiting its emission, and the exact 85/14/1 split of the 15% fee (fuzzed).\n  - **Last depositor out:** they pay the full 15% to the Treasury.\n- **`test/SystemInvariants.t.sol`**: random call sequences across the CHEESE and ETH farms, including allocation changes, pauses, Core outages, donations, skims and blocks run past the end of the schedule. It checks that:\n  - principal is conserved in both assets and reserves are fully backed;\n  - shares add up and the supply never exceeds the cap;\n  - emission equals the SQK schedule exactly;\n  - what was minted equals what was claimed plus what the farms hold;\n  - emission never decreases and completion never reopens.\n\n  The check that rewards owed never exceed the reserve allows 1 wei per account, because of finding 1; any bigger gap still fails.","treeHash":"237d1756a1e49e3dd12dbf473eb2fe192651217a","usage":{"cachedInputTokens":2016601,"cost":{"source":"runtime","usdMicros":2184497},"inputTokens":47,"model":"claude-opus-5-5","outputTokens":46275,"runtime":"claude","turns":28,"wallClockMs":501241}},{"artifacts":[],"attempt":1,"bundleHash":"698551577c2a14991e67b21b997e70c16621b851c2567fdbbf21b4f5f7db2a28","device":"bc39660800b5f882","findings":[],"hash":"58e33fbc7d4c3166dd705b3e579d83c7434ef91588c1f376ce57a0352e6a4a35","nodeId":"ef45bb39-6188-4c7b-8ef1-9e056dd24c04","outcome":"completed","summary":"The CHEESE Vault V1 contract system is complete and verified locally. Final state: `forge build`, `forge fmt --check`, and `forge test` with the verifier's fuzz seed all pass.\n\n| Check | Result |\n|---|---|\n| forge build (solc 0.8.26) | success |\n| forge test (seed 0xdf41…3568) | 70 passed, 0 failed |\n| forge fmt --check | clean |\n\n**Squeekchain verification.** I read the official docs, the `sepolia.json` deployment record and the published ABIs, then read the live Sepolia contracts. Docs and chain agree on everything CHEESE depends on: proxy implementation slots and runtime hashes, chain id, the 600-second round and 1e9 block units, cap, premint, allowance, launch bonus, halving length, and the first-period rate. The transcribed `issued()` formula reproduces the live `SqueekMining.issued()` at every boundary probed, and the live minted total equals `issued(fundedBlocks)` exactly. No discrepancy was found. Details are in `docs/SQUEEKCHAIN_VERIFICATION.md`.\n\n**What was built** (all under `src/`, launch order): `CheeseEmissionController`, `CheeseToken`, `CheeseFarmERC20` (CHEESE pool), `CheeseFarmETH`, `CheeseFarmSIMD`. The controller reads SqueekCore's block counter, anchors at the next Squeek round like SqueekMining does, prices new blocks in O(1) with SQK's exact schedule, and splits them by points with a single lazy accumulator. The Future Pool Reserve is never minted. Farms use internal 18-decimal shares, the 85/14/1 Hamster Bite with full 15% to Treasury on last exit, separate principal and reward reserves, and claim independent of principal. Tests cover emission matching against live vectors, halving crossings, cap, no double minting, allocation changes, inactive reserve, no retroactive rewards, fee split, last depositor, reward independence, emission end, reentrancy, access control, gas scalability, plus fuzz and invariant suites.\n\n**Bug caught by tests during development.** `claim()` did not clear the harvested pending bucket, so a second claim would have paid again. Fixed and covered by a test and an invariant. Two other issues (constructor reading the token before binding, and pending-reward preview ignoring unsynced blocks) were also fixed. All three are recorded in `docs/SECURITY.md`.\n\n**Decisions you should know about.**\n- Emission accrued to a farm with no stakers is forfeited, never minted, so a first depositor cannot inherit it. Routing it to the Treasury instead is a one-line change and a product decision.\n- The sIMD token address is not in the brief, so `CheeseFarmSIMD.setAsset` is a write-once owner setter. Set it before `activate()` or the sIMD pool's emission is forfeited.\n- Unnamed wallets (Treasury, team, liquidity premint recipients) resolve to the launch owner; the owner can move the Treasury later.\n- Pausing emissions closes deposits and catches up on unpause, so the schedule still matches SQK.\n\n**Not done, by design or constraint.** No contracts were deployed and no gas was spent. The IMD launch deploys the five contracts; the README lists the exact constructor arguments, the six after-launch owner calls, and the deployment approval items. The Sepolia verification script `script/VerifySqueekchain.s.sol` was exercised through equivalent `cast` reads but not run as a forge script here, and Slither was not available in this environment. The job id is in the README; no explorer URL exists yet and none was invented.","treeHash":"0877357879c037f7b7526a62533e7eb9ba6db3ee","usage":{"cachedInputTokens":5077077,"cost":{"source":"runtime","usdMicros":12135913},"inputTokens":1058,"model":"claude-fable-5-1","outputTokens":126748,"runtime":"claude","turns":84,"wallClockMs":2229163}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"d7e6877fd9e388bb","findings":[{"citation":"resolved","description":"New this round, from write_foundry_tests; reproduced. _readState (line 488), _readConfiguration (line 499) and _readHeightAtRound (line 512) use the high-level `(bool, bytes memory) = addr.staticcall(...)` form, which copies the callee's entire return data into memory before the `data.length != CORE_STATE_BYTES` check on line 489 runs. The caller pays that copy out of what it kept after the call: with EIP-150 that is 1/64 of its gas plus whatever the callee returns unused. A SqueekCore implementation that expands memory to 2 MiB, burns the forwarded gas down to a few tens of thousands and then returns the 2 MiB forces the caller to pay about 8.6M gas of memory expansion from roughly 470k (at a 30M transaction), so the read reverts out of gas however much gas the user supplies. Every farm withdraw and claim runs _accrue -> pullRewards -> _sync -> _readState (CheeseFarm.sol lines 200, 229, 317), and since this round pauseEmissions also calls _sync() before setting the flag (line 206), so the guardian's pause, which was the escape hatch for a misbehaving Core in the previous design and in the fixed-reverting case, now fails the same way. squeekCore is immutable, lastHeight and completed cannot be set by anyone, and withdraw has no path that skips the controller, so all ETH, CHEESE and sIMD principal and accrued rewards stay locked for as long as the Core behaves this way. Reachability: the SqueekCore proxy is upgradeable by its Timelock (docs/SECURITY.md item 8), so this needs a malicious or badly broken upstream upgrade, not a plain outage; a Core that merely reverts or burns all gas (INVALID) is survivable, as test_gasBurningCoreDoesNotBlockExitsOrPause shows, which confirms the return-data copy is the cause. docs/SECURITY.md item 8 states that a breaking Core change 'leaves withdrawals, claims and views working'; this case does not. Fix: perform the three reads with a fixed-size output buffer in assembly (`staticcall(gas(), target, in, insize, out, CORE_STATE_BYTES)`) and check `returndatasize()` against the expected size (and a sane upper bound for roundPage) before any copy, so the caller never touches an oversized payload; verified that this change makes the attached proof pass (gas 116.6M in the test harness, but withdraw/claim/pause each succeed within their 30M caps).","line":488,"path":"src/CheeseEmissionController.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {CheeseToken} from \"src/CheeseToken.sol\";\nimport {CheeseEmissionController} from \"src/CheeseEmissionController.sol\";\nimport {CheeseFarmETH} from \"src/farms/CheeseFarmETH.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @notice Minimal SqueekCore double: certifies rounds and completes blocks.\ncontract CoreDouble is ISqueekCore {\n    uint64 public genesisTimestamp;\n    uint64 public settledThroughRound;\n    uint96 public cumulativeBlockUnits;\n    mapping(uint64 => uint96) public unitsAfterRound;\n\n    constructor(uint64 genesis) {\n        genesisTimestamp = genesis;\n    }\n\n    function settle(uint64 round, uint64 newBlocks) external {\n        cumulativeBlockUnits += uint96(newBlocks) * 1_000_000_000;\n        for (uint64 r = settledThroughRound + 1; r <= round; ++r) {\n            unitsAfterRound[r] = cumulativeBlockUnits;\n        }\n        settledThroughRound = round;\n    }\n\n    function state() external view returns (CoreState memory result) {\n        result.settledThroughRound = settledThroughRound;\n        result.cumulativeBlockUnits = cumulativeBlockUnits;\n        result.blockHeight = uint64(cumulativeBlockUnits / 1_000_000_000);\n    }\n\n    function configuration() external view returns (CoreConfiguration memory result) {\n        result.genesisTimestamp = genesisTimestamp;\n        result.roundDuration = 600;\n        result.blockUnitsPerBlock = 1_000_000_000;\n    }\n\n    function roundPage(uint64 afterRound, uint64 throughRound, uint256) external view returns (RoundPage memory result) {\n        result.certifiedThroughRound = throughRound;\n        result.start.cumulativeBlockUnits = unitsAfterRound[afterRound];\n        result.end.cumulativeBlockUnits = unitsAfterRound[throughRound];\n    }\n\n    function blockInfo(uint64) external pure returns (BlockInfo memory) {\n        revert();\n    }\n\n    function paused() external pure returns (bool) {\n        return false;\n    }\n}\n\n/// @notice A SqueekCore whose every call expands memory to 2 MiB, burns the forwarded gas down to ~65k and then\n///         returns the whole 2 MiB. The caller keeps 1/64 of its gas and must copy 2 MiB of return data (~8.6M\n///         gas), so it runs out of gas however much the transaction supplies.\n///\n///         Bytecode: PUSH1 0 PUSH3 0x1FFFE0 MSTORE | JUMPDEST GAS PUSH3 0xFFFF LT PUSH1 7 JUMPI |\n///                   PUSH3 0x200000 PUSH1 0 RETURN\ncontract ReturnBombCoreTest is Test {\n    uint64 internal constant GENESIS = 1_793_735_400;\n    bytes internal constant BOMB = hex\"6000621fffe0525b5a6200ffff10600757622000006000f3\";\n\n    address internal owner = makeAddr(\"owner\");\n    address internal treasury = makeAddr(\"treasury\");\n    address internal alice = makeAddr(\"alice\");\n\n    CoreDouble internal core;\n    CheeseEmissionController internal controller;\n    CheeseToken internal cheese;\n    CheeseFarmETH internal farm;\n\n    function setUp() public {\n        vm.warp(GENESIS + 100 * 600 + 17);\n        core = new CoreDouble(GENESIS);\n        controller = new CheeseEmissionController(owner, address(core), treasury);\n        cheese = new CheeseToken(owner, owner, owner, address(controller));\n        farm = new CheeseFarmETH(owner, address(controller));\n        vm.startPrank(owner);\n        controller.bindToken(address(cheese));\n        controller.addPool(address(farm), 200);\n        controller.activate();\n        vm.stopPrank();\n        core.settle(controller.startRound() - 1, 0);\n        controller.checkpoint();\n        assertTrue(controller.startCaptured());\n\n        vm.deal(alice, 10 ether);\n        vm.prank(alice);\n        farm.deposit{value: 1 ether}();\n        core.settle(core.settledThroughRound() + 1, 10);\n        controller.checkpoint();\n        assertGt(farm.pendingRewards(alice), 0);\n    }\n\n    function test_returnBombCoreDoesNotBlockWithdrawClaimOrPause() public {\n        vm.etch(address(core), BOMB);\n\n        // Sanity: a bare call to the bomb succeeds when the caller can afford the copy but burns the gas.\n        (bool ok, bytes memory data) = address(core).staticcall{gas: 20_000_000}(abi.encodeCall(ISqueekCore.state, ()));\n        assertTrue(ok);\n        assertEq(data.length, 0x200000);\n\n        // The brief: Squeekchain unavailability must never block withdrawals; owner emergency procedures work.\n        uint256 shares = farm.sharesOf(alice);\n        vm.prank(alice);\n        uint256 paid = farm.withdraw{gas: 30_000_000}(shares);\n        assertEq(paid, 0.85 ether, \"85% of principal paid\");\n\n        vm.prank(alice);\n        uint256 claimed = farm.claim{gas: 30_000_000}(alice);\n        assertGt(claimed, 0, \"accrued rewards claimable\");\n\n        vm.prank(owner);\n        controller.pauseEmissions{gas: 30_000_000}();\n        assertTrue(controller.emissionsPaused(), \"emergency pause works\");\n    }\n}","reproduction":"Deploy CheeseEmissionController, CheeseToken, CheeseFarmETH against a SqueekCore double; bindToken, addPool(ETH farm, 200), activate, certify round startRound-1 with 0 blocks and checkpoint (h0 = 0). alice deposits 1 ETH; one round completing 10 blocks is certified and checkpointed, so alice has pending rewards. Then vm.etch(core, 6000621fffe0525b5a6200ffff10600757622000006000f3) (PUSH1 0 PUSH3 0x1FFFE0 MSTORE; JUMPDEST GAS PUSH3 0xFFFF LT PUSH1 7 JUMPI; PUSH3 0x200000 PUSH1 0 RETURN). alice calls ethFarm.withdraw{gas: 30_000_000}(1e21 shares). Expected (brief: withdrawals immediately eligible, Squeekchain unavailability must not block them; SECURITY.md item 8): paid 0.85 ETH. Actual: the call consumes 29,552,823 gas and reverts with EvmError: Revert at CheeseEmissionController.pullRewards (out of gas copying the 2 MiB payload in _readState). claim{gas: 30M} and owner pauseEmissions{gas: 30M} fail the same way. Proof: test/scratch/Proof_ReturnBomb.t.sol fails on this tree; it passes with _readState changed to a fixed-size assembly staticcall plus a returndatasize() check (patched locally and reverted).","severity":"medium","snippet":"        (bool success, bytes memory data) = address(squeekCore).staticcall(abi.encodeCall(ISqueekCore.state, ()));","title":"SqueekCore return data is copied unbounded, so a Core that returns a large payload after burning gas makes every withdraw, claim and the emergency pause run out of gas, locking all principal with no o"},{"citation":"resolved","description":"New this round, found by running the delivered suite: test/Invariants.t.sol invariant_userActionsNeverRevertUnexpectedly fails ('claim reverted or paid less than pendingRewards') on this tree, 129 other tests pass. Cause: previewPoolEmission adds the settled-but-unpulled part floor(weight * (emissionPerPoint - last) / 1e18) (line 366) and the unsynced part floor(weight * floor(unsynced*1e18/1000) / 1e18) (line 369) as two separate floors, while the real pull adds the unsynced increment to emissionPerPoint first and then floors once in _settle (line 471). floor(a)+floor(b) <= floor(a+b), so the view is up to 1 wei below what claim pays whenever both parts are non-zero. The handler in test/Invariants.t.sol line 89 flags `paid != pending` strictly, so the suite's own invariant fails (the SystemInvariants handler tolerates +-2 and documents the reason at its line 142, so the author knows the view rounds). No funds are at risk: the claim pays more than promised, never less, the sum of entitlements still fits the reserve and rewardReserve keeps 1 wei of dust. It matters because the brief's deliverable is 'full automated tests and results' and because the website reads pendingRewards. Fix: compute the preview the way _settle will, e.g. total += weight * ((emissionPerPoint - last) + unsynced*1e18/1000) / 1e18, or make the handler tolerate view rounding like the system handler does; either makes the invariant pass. Also clear cache/invariant/failures before re-running, or the replay keeps failing.","line":369,"path":"src/CheeseEmissionController.sol","reproduction":"activateWithHistory(0) from test/CheeseBase.t.sol. Account 0xA11CF deposits 7314 wei into the ETH farm (7,314,000 shares). advanceBlocks(4989); controller.checkpoint(); advanceBlocks(41) with no checkpoint. pendingRewards(0xA11CF) returns 3696618124211079201293598; claim(0xA11CF) pays 3696618124211079201293599. Expected: equal. Same with ordinary sizes: alice deposits 10 ETH, then for i in 1..59: advance 30+i blocks, checkpoint, advance 41+(i%7) blocks, compare pendingRewards and claim: 23 of 59 cycles pay 1 wei more than the view (e.g. i=3: view 56588388780169602087396, paid 56588388780169602087397). The failing invariant run: forge test --match-path test/Invariants.t.sol, sequence deposit(301,7314), advance(60000), checkpoint(), advance(41), claim(0x5ef...333) with fuzz seed 0x147ed9b9642aec2df099056188202e919eb6d5395346ebd91b4274845f71ec58.","severity":"low","snippet":"            total += (uint256(pool.weight) * ((unsynced * PRECISION) / TOTAL_POINTS)) / PRECISION;","title":"pendingRewards under-reports claim by 1 wei when settled and unsynced emission are floored separately, and the shipped invariant suite fails on it"},{"citation":"resolved","description":"From audit_economics; reproduced. The farm tracks principal in totalAssets from amounts received and never from balances (CheeseFarm.sol lines 186, 219), which is the right design against donations but assumes a static-balance token. The sIMD asset is unknown at launch and set once, irreversibly, by the owner (setAsset checks only non-zero and code). A staked-IMD receipt token may well rebase. If balances shrink, totalAssets overstates holdings, early withdrawers are paid from later depositors' tokens and the last withdrawals revert in _push for lack of balance, so principal is lost for whoever exits last with no admin recovery. If balances grow, the extra counts as surplus and the owner's skimSurplus moves depositors' yield to the Treasury while assetsOf is unchanged. docs/SECURITY.md item 6 notes only that the address is checked for code and cannot be corrected; nothing says sIMD must be non-rebasing. Severity low because it depends on an owner configuration choice about an asset the brief does not specify; it becomes a loss of funds if sIMD rebases. Fix: document the non-rebasing requirement next to setAsset and verify it against the real sIMD before calling; if sIMD does rebase, the farm needs balance-based (or wrapper) accounting, which is a scope decision.","line":28,"path":"src/farms/CheeseFarmSIMD.sol","reproduction":"CheeseFarmSIMD with a controller stub (no emission) and a mock ERC-20 whose balanceOf scales by num/den and whose transfers convert amounts back. Case A: alice and bob each deposit 10e18; the token halves balances (farm balance 10e18, totalAssets 20e18); alice withdraws all her shares and is paid 8.5e18; bob's assetsOf shows 11399999999999999999 but bob.withdraw(sharesOf(bob)) reverts (insufficient balance). Expected: bob can withdraw what the farm reports as his. Case B: alice deposits 10e18; the token doubles balances; surplus() returns 10e18 and the owner's skimSurplus() sends 10e18 to the Treasury while assetsOf(alice) stays 10e18. Both run in test/scratch/Probe_Rebase.t.sol on this tree.","severity":"low","snippet":"        if (asset_.code.length == 0) revert AssetIsNotAContract();","title":"sIMD farm assumes a non-rebasing asset: a negative rebase strands the last withdrawers and a positive rebase lets skimSurplus send depositors' yield to the Treasury; neither the code nor the docs stat"},{"citation":"resolved","description":"From audit_permissions; traced, not a defect. addPool checks only that the candidate returns this controller and false from registered(); pullRewards (line 305) mints to pool.farm whatever hasStakers the farm passes. An owner-controlled contract added with the free 200 points, or with all 1000 after setting the real farms to 0, receives every later emission. This is prospective, bounded by the SQK schedule and MINING_ALLOWANCE, touches no user principal or accrued rewards, and is inside the owner powers the brief grants (add pools, adjust future weights), so it is a trust assumption, not a bypass. README.md line 140 ('The owner cannot mint') and docs/SECURITY.md line 36 should say the owner cannot mint directly but can direct future emission to any pool it registers; a multisig or timelock owner is the mitigation.","line":225,"path":"src/CheeseEmissionController.sol","reproduction":"Owner deploys X with emissionController() returning the controller, registered() returning false, onPoolRegistered(uint256) storing the id and pull() calling controller.pullRewards(id, true). Owner calls batchSetPoolWeights([0,1,2],[0,0,0]) then addPool(X, 1000). After the next certified blocks, X.pull() mints the full issued(h)-issued(h_prev) to X. Expected by the README wording: impossible; actual: allowed, prospectively.","severity":"info","snippet":"        if (ICheeseFarm(farm).emissionController() != address(this) || ICheeseFarm(farm).registered()) {","title":"Trust assumption: the owner can register any contract that answers emissionController() and registered() as a pool and route future emission to it, so 'the owner cannot mint' in the README overstates "}],"hash":"5fff20e6862a66e73b7261d04b2a70208fe64d107d4d89c8eb64b71e0f000845","nodeId":"95b84426-94cc-4584-8468-10c3cd3ef206","outcome":"completed","summary":"Review complete. `.imd-findings.json` holds 4 findings and 56 coverage rows, and no tracked file was changed.\n\n**All nine earlier findings are fixed.** The four attached proofs (accumulator overflow, floored reward debts, Treasury-blocked withdrawals, deposits during a Core outage) pass on the revised tree, and I traced the five others in the code: `pauseEmissions` syncs first, Core reads are raw staticcalls with size checks and guarded decodes, duplicate batch ids revert, the last withdrawer sweeps the residual to the Treasury, and the deploy script moves the guardian.\n\n**New findings this round**\n\n- **Medium: return-data bomb from SqueekCore locks all principal.** The high-level `staticcall` in `_readState` copies the full payload before the size check. A Core that expands memory to 2 MiB, burns gas to about 65k and returns it forces the caller to pay ~8.6M gas from its 1/64 remainder. Withdraw, claim and now `pauseEmissions` (which syncs first since this round) all run out of gas at any transaction gas. Reproduced with a 30M-gas withdraw reverting inside `pullRewards`. Proof attached; it passes with a fixed-size assembly staticcall plus `returndatasize()` check, which I patched and reverted locally. Needs a malicious or broken upstream upgrade, hence medium.\n- **Low: shipped invariant fails.** `invariant_userActionsNeverRevertUnexpectedly` fails on this tree because `previewPoolEmission` floors the settled and unsynced parts separately, so `pendingRewards` is 1 wei below what `claim` pays (23 of 59 ordinary cycles in my scan). No funds at risk.\n- **Low: sIMD farm assumes a non-rebasing asset.** Reproduced with a rebasing mock: a halving strands the last withdrawer, a doubling lets `skimSurplus` take depositors' yield. Not documented or checked at `setAsset`.\n- **Info: owner can register any conforming contract as a pool** and route future emission to it. Intended privilege, but the README's \"owner cannot mint\" overstates it.\n\n**Dropped:** the Hamster Bite instalment finding (the brief explicitly accepts that partial withdrawers benefit).","treeHash":null,"usage":{"cachedInputTokens":1257934,"cost":{"source":"runtime","usdMicros":5203445},"inputTokens":322,"model":"claude-fable-5-1","outputTokens":37234,"runtime":"claude","turns":36,"wallClockMs":572146}},{"artifacts":[],"attempt":1,"bundleHash":"d764532c84c569e28e82d4da4dadd0aa0e4d02a4752aa1ecefe2375ee19aa724","device":"a315ee4cb81c640c","findings":[],"hash":"6761efc4875b38d87e66f31e76773c689ec194d87983ba9781a35223c6e20a02","nodeId":"5a771bd9-a897-4289-99a5-fdeef33dbd72","outcome":"completed","summary":"I've written `launch.json`. It lists the five accepted CHEESE contracts in the order their constructors depend on each other. `forge build` succeeds, and `forge test` with the verifier's fuzz seed passes all 70 tests. `launch.json` is the only file changed.\n\n| # | Contract | Constructor arguments |\n|---|---|---|\n| 0 | `CheeseEmissionController` | `$owner`, `0xFD6C166D4B8E2962E65746a13e5a0797CD4dE0c4` (the SqueekCore proxy), `$owner` (Treasury) |\n| 1 | `CheeseToken` | `$owner`, `$owner`, `$owner` (team, treasury and liquidity premint), `$contract:CheeseEmissionController` (the only account that can mint) |\n| 2 | `CheeseFarmERC20` | `$owner`, `$contract:CheeseEmissionController`, `$contract:CheeseToken` (its deposit asset) |\n| 3 | `CheeseFarmETH` | `$owner`, `$contract:CheeseEmissionController` |\n| 4 | `CheeseFarmSIMD` | `$owner`, `$contract:CheeseEmissionController` |\n\n- **Matches the source:** each argument list has the same count and order as the constructor in `src/`, and agrees with the README's deployment table and `script/Deploy.s.sol`. No owner role goes to the factory or to IMD. The file has only `kind`, `contracts` and `notes`, and the notes are 1,551 characters, under the 4,000 limit.\n- **Wallets set to `$owner`:** the brief names no Treasury, team or liquidity wallet, so those arguments are `$owner`. The owner can move the Treasury later with `setTreasury`.\n- **SqueekCore address:** the official docs site returned a Cloudflare challenge (HTTP 403), so I couldn't re-read `sepolia.json` myself. Instead I checked Sepolia directly. The proxy at that address has code, and its implementation slot points to `0x6869d610…22ad3`, the implementation the repo's verification notes record. The rest of the comparison with the docs comes from the earlier contributor's notes, not from my own check.\n\nThe notes in the manifest also list the owner's steps after launch: link the token to the controller, add the three pools at 400/200/200 points (the remaining 200 is the inactive Future Pool Reserve), set the sIMD token address, then turn on emission.","treeHash":"448b24fa4d80ed18d4decc17764bfba3a158fb1d","usage":{"cachedInputTokens":331448,"cost":{"source":"runtime","usdMicros":441166},"inputTokens":21,"model":"claude-opus-5-5","outputTokens":4241,"runtime":"claude","turns":11,"wallClockMs":59831}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"0ef0cddb99617930","findings":[],"hash":"83ca2779c8e7c4ab9eb0019e4ece91207cd3603ab9c38eabd6b2b9eefc593254","nodeId":"95b84426-94cc-4584-8468-10c3cd3ef206","outcome":"completed","summary":"The review is complete. The findings file holds an empty findings list and a 59-row coverage record (all 53 entry points plus the settled items), and the working tree is unchanged apart from that file.\n\n**Settlement of my earlier findings**\n\n- **Return-bomb Core locking principal (medium): fixed.** The three Core reads now go through a bounded assembly staticcall that checks `returndatasize()` before copying anything. My proof passes on this tree, and withdraw, claim and the guardian pause each succeed under a 30M cap against a 2 MiB-returning Core.\n- **pendingRewards 1 wei under claim (low): fixed.** The preview now adds the unsynced increment to the per-point delta and floors once, as a real pull does. The author's new equality test and the full suite pass with the invariant cache cleared.\n- **Rebasing sIMD (low): fixed as agreed.** The non-rebasing requirement is stated at the setter, in the README after-launch step and in the security doc. The accounting is unchanged, which was the scope decision.\n- **Owner can route emission wording (info): fixed.** README and security doc now say the owner cannot mint directly but can direct future emission to any registered pool.\n\n**Specialist findings** all describe the pre-fix code and are resolved in this tree. Their four attached proofs pass here. I confirmed each mechanism in the source: 512-bit mulDiv on every share-times-accumulator product, deposits reverting with SyncRequired when the Core is unreadable or regressed, pause syncing first, claim clamping to the reserve with ceiled debts, the last withdrawer sweeping the virtual-offset residual to the Treasury, and the deploy script moving the guardian on hand-over. The partial-withdrawal fee effect is accepted by the brief and documented in the README. The slither and aderyn leads are the deliberate internal-reserve equalities, the OZ two-step ownership and the schedule constant, none of which reproduce as defects.\n\n**Fresh pass on the changed code** found nothing new. Deposits before activation and after completion still succeed, the last-out branch cannot underflow, and the manifest's constructor arguments match each constructor in order with contract sizes well inside the deploy limits.\n\nResult: 138 delivered tests and all 5 proofs pass. No open findings, so nothing blocks this launch from my side.","treeHash":null,"usage":{"cachedInputTokens":574861,"cost":{"source":"runtime","usdMicros":2994204},"inputTokens":194,"model":"claude-fable-5-1","outputTokens":14926,"runtime":"claude","turns":16,"wallClockMs":231426}},{"artifacts":[],"attempt":1,"bundleHash":"33503576da6ec5329f75274fbcf28b1b085a91be0ba1dfcb1b489802c16e6123","device":"a11137177fc72d8b","findings":[],"hash":"b29b2150bffba095b6456e83862ea93076cf8c5f60cf02fc3e64df6f2bc6efc7","nodeId":"ef45bb39-6188-4c7b-8ef1-9e056dd24c04","outcome":"completed","summary":"All nine findings reproduced on the starting tree and are fixed. The four reviewer proofs now pass, the project suite passes with the verifier's fuzz seed and with a random seed at higher depth, and `forge build` and `forge fmt --check` are clean. Answers are in `.imd-responses.json` (nine entries, all `fixed`).\n\n**What changed in the contracts**\n\n- **Accumulator overflow (high):** every share × accumulator product in the farm is now a 512-bit `Math.mulDiv`, so a 1-wei first depositor can no longer make later deposits revert.\n- **Reward overdraft (high):** reward debts round up and harvests floor, so entitlements never sum above the reserve. As defence in depth, a claim pays what the reserve holds and keeps any shortfall pending instead of reverting.\n- **Treasury freeze (high):** the Treasury's Hamster Bite share is pushed with a 100k gas stipend without reverting. On failure it is credited to a new `treasuryOwed` reserve and anyone can call `payTreasury()` later. The withdrawer's 85% never depends on the Treasury, even after ownership is renounced.\n- **Deposit during outage (medium):** the controller's pull hook now reports whether the sync succeeded, and a deposit reverts with `SyncRequired` when it did not. Withdrawals and claims stay best-effort.\n- **Pause without checkpoint (medium):** pausing first prices the blocks certified before it, so an exiting staker keeps what they earned.\n- **Undecodable Core data (medium):** SqueekCore is read through raw staticcalls with exact size checks and guarded decoding, so a proxy upgrade that changes a struct reads as \"unavailable\" rather than reverting user transactions.\n- **Low findings:** duplicate ids in a weight batch revert, an emptied farm sends its stranded residual to the Treasury and restarts at the initial price, and the deploy script moves the guardian role with ownership.\n\n**Tests and docs**\n\n- The old test asserting the Treasury revert was replaced. Seventeen regression tests were added across the farm, emission, allocation and deploy suites, covering each finding's reproduction plus gas-burning, receiver-less and token-blocking Treasuries.\n- The invariant handler now records unexpected deposit, withdraw or claim reverts and two new invariants fail on them and on entitlements exceeding the reserve.\n- README, `docs/SECURITY.md` (new \"fixed after review\" table and updated residual risks) and `docs/ARCHITECTURE.md` describe the new behaviour, and the four changed ABIs under `web/abi` were regenerated.\n\nResults with the verifier's seed:\n\n| Scope | Result |\n|---|---|\n| Project tests (`test/`) | 87 passed, 0 failed |\n| Reviewer proofs (copied under `test/scratch/`) | 5 passed, 0 failed |\n| `forge fmt --check` | clean |\n\nOne note for the reviewer: `.imd-responses.json` is listed in the repository's `.git/info/exclude`, which I cannot edit, so it sits in the working tree root as instructed but is not a tracked file. Nothing was committed, since `.git/` is off limits for this task.","treeHash":"cae0958e103420e28c4baaf89ea7b04c63750ca4","usage":{"cachedInputTokens":4157272,"cost":{"source":"runtime","usdMicros":9418532},"inputTokens":834,"model":"claude-fable-5-1","outputTokens":85485,"runtime":"claude","turns":108,"wallClockMs":1053472}},{"artifacts":[],"attempt":1,"bundleHash":"b4f21160123f81f848121d83080b9908886822f81c3c5191a90d1179635315ce","device":"1a7606be97f168d4","findings":[{"description":"_readState (and _readHeightAtRound, _readConfiguration) use `(bool success, bytes memory data) = address(squeekCore).staticcall(...)`, which copies the callee's entire return data into memory before the `data.length != CORE_STATE_BYTES` check. A SqueekCore (an upgradeable third-party proxy) whose state() returns a large payload makes the caller pay quadratic memory expansion out of the 1/64 of gas it kept, so the call runs out of gas regardless of the gas the user supplies. Every user exit syncs first (withdraw/claim -> _accrue -> controller.pullRewards -> _sync -> _readState), so principal and accrued rewards become unwithdrawable. The revision that made pauseEmissions() call _sync() before setting the flag removes the escape hatch: before it, the guardian's pause made _sync return early without touching Core and exits worked; now the pause itself runs out of gas. The owner cannot repoint squeekCore (immutable). The brief requires that Squeekchain unavailability never blocks withdrawals and that the owner/emergency procedures keep principal safe. Fix: read with assembly, check returndatasize() == CORE_STATE_BYTES (and a sane upper bound for roundPage) and returndatacopy only that many bytes; optionally make pauseEmissions' pre-pause sync gas-capped or skip it. A Core that merely burns all gas (INVALID) is already survivable and is covered by test_gasBurningCoreDoesNotBlockExitsOrPause.","line":488,"path":"src/CheeseEmissionController.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {CheeseEmissionController} from \"src/CheeseEmissionController.sol\";\nimport {CheeseToken} from \"src/CheeseToken.sol\";\nimport {CheeseFarmETH} from \"src/farms/CheeseFarmETH.sol\";\nimport {ISqueekCore} from \"src/interfaces/ISqueekCore.sol\";\n\n/// @dev SqueekCore stand-in with the live ABI. `bombBytes != 0` simulates a broken or compromised Core upgrade\n///      whose state() returns that many bytes of zeroes.\ncontract CoreDouble {\n    uint64 internal genesis;\n    uint64 internal settled;\n    uint96 internal units;\n    uint256 internal bombBytes;\n\n    constructor(uint64 genesis_) {\n        genesis = genesis_;\n    }\n\n    function settle(uint64 round, uint64 blocks) external {\n        settled = round;\n        units = uint96(blocks) * 1e9;\n    }\n\n    function bomb(uint256 bytes_) external {\n        bombBytes = bytes_;\n    }\n\n    function state() external view returns (ISqueekCore.CoreState memory s) {\n        uint256 b = bombBytes;\n        if (b != 0) {\n            assembly {\n                return(0, b)\n            }\n        }\n        s.settledThroughRound = settled;\n        s.cumulativeBlockUnits = units;\n        s.blockHeight = uint64(units / 1e9);\n    }\n\n    function configuration() external view returns (ISqueekCore.CoreConfiguration memory c) {\n        c.genesisTimestamp = genesis;\n        c.roundDuration = 600;\n        c.blockUnitsPerBlock = 1e9;\n    }\n\n    function roundPage(uint64, uint64 through, uint256) external view returns (ISqueekCore.RoundPage memory p) {\n        p.certifiedThroughRound = through;\n        p.end.cumulativeBlockUnits = units;\n    }\n}\n\n/// @notice A SqueekCore whose state() returns a very large payload makes the controller's raw staticcall copy\n///         all of it into memory, and the quadratic memory cost exhausts the 1/64 of gas the caller kept. Every\n///         path that syncs then runs out of gas: withdraw, claim, checkpoint and, since the revision that syncs\n///         first, pauseEmissions too, so the guardian's emergency pause no longer frees principal.\ncontract ReturnBombTest is Test {\n    address owner = makeAddr(\"owner\");\n    address treasury = makeAddr(\"treasury\");\n    address alice = makeAddr(\"alice\");\n    CoreDouble core;\n    CheeseEmissionController controller;\n    CheeseFarmETH ethFarm;\n\n    function setUp() public {\n        uint64 genesis = 1_793_735_400;\n        vm.warp(genesis + 100 * 600 + 17);\n        core = new CoreDouble(genesis);\n        vm.startPrank(owner);\n        controller = new CheeseEmissionController(owner, address(core), treasury);\n        CheeseToken token = new CheeseToken(owner, treasury, owner, address(controller));\n        ethFarm = new CheeseFarmETH(owner, address(controller));\n        controller.bindToken(address(token));\n        controller.addPool(address(ethFarm), 200);\n        controller.activate();\n        vm.stopPrank();\n        uint64 start = controller.startRound();\n        core.settle(start - 1, 0);\n        controller.checkpoint();\n        vm.deal(alice, 10 ether);\n        vm.prank(alice);\n        ethFarm.deposit{value: 1 ether}();\n        core.settle(start, 10);\n        controller.checkpoint();\n        // Core now answers state() with 2 MiB of zeroes.\n        core.bomb(2 << 20);\n    }\n\n    /// @dev Fails today (OutOfGas inside the withdrawal); passes once the Core read copies at most the expected\n    ///      CORE_STATE_BYTES of return data (bounded returndatacopy) and treats any other size as unavailable.\n    function test_withdrawSurvivesReturnBombingCore() public {\n        uint256 shares = ethFarm.sharesOf(alice);\n        uint256 before = alice.balance;\n        vm.prank(alice);\n        ethFarm.withdraw{gas: 30_000_000}(shares);\n        assertEq(alice.balance - before, 0.85 ether, \"85% of principal returned\");\n    }\n\n    /// @dev The emergency stop must not depend on SqueekCore behaving.\n    function test_guardianPauseSurvivesReturnBombingCore() public {\n        vm.prank(owner);\n        controller.pauseEmissions{gas: 30_000_000}();\n        assertTrue(controller.emissionsPaused());\n    }\n}","reproduction":"Deploy controller + ETH farm against a Core double, activate, alice deposits 1 ether, 10 blocks certified and checkpointed. Switch Core.state() to `return(0, 2 MiB)`. Expected: alice.withdraw{gas: 30_000_000}(allShares) pays 0.85 ether and owner pauseEmissions{gas: 30_000_000}() sets emissionsPaused. Actual: both revert out of gas inside CheeseEmissionController (_readState via pullRewards / pauseEmissions). Same setup with a Core that executes INVALID instead passes, confirming the return-data copy is the cause.","severity":"medium","title":"Unbounded return-data copy from SqueekCore lets a misbehaving Core freeze withdrawals, claims and the emergency pause"}],"hash":"b3a13cdff0669c04f9f01878994106da7145803872c2198bb4917b005d26af09","nodeId":"24fa880d-8de5-47bc-854d-870c7e3ad465","outcome":"completed","summary":"I've updated my two test files to match the revised implementation. `forge build` and `forge test` both pass with the verifier seed: 130 tests, 0 failures. I also found one new medium-severity defect, which I reported in `.imd-findings.json` with a test that proves it rather than writing a test that accepts it.\n\n**Why a test was failing.** The implementation now sends the last withdrawer's whole remainder to the Treasury: the 15% fee plus a small leftover that no shares can claim. My old test expected exactly 15%, so it failed. That was my test being out of date, not a bug.\n\n**Changes in `test/Adversarial.t.sol`**\n- **Last withdrawer:** the test now checks the withdrawer still gets exactly 85%, the Treasury gets the fee plus at most 1,000 wei of leftover, and the farm ends with zero assets and zero ETH balance.\n- **New tests:**\n  - Someone depositing after everyone has left gets the same price as the very first depositor and inherits nothing.\n  - When SqueekCore returns malformed data, withdrawals and claims still work without pausing, deposits are refused with `SyncRequired`, and the guardian can still pause. This replaces my old test, which needed a pause first.\n  - Pausing records the blocks certified before the pause for whoever was staked then, and the blocks certified during the pause are caught up exactly after unpausing.\n  - Pausing works even while SqueekCore is unreachable.\n  - Deposits wait while SqueekCore is down, and the blocks certified during the outage go only to the depositors who were already in.\n  - A SqueekCore that burns all the gas it's given doesn't block withdrawals, claims or the pause within a 30M-gas transaction.\n\n**Changes in `test/SystemInvariants.t.sol`**\n- Last round I loosened two checks because of a rounding defect and a claim that could revert. Both are fixed, so I made the checks strict again:\n  - What depositors are owed in rewards must not exceed what the farm holds plus what it can pull, with no tolerance.\n  - A claim or a withdrawal of shares someone actually holds must never revert. A claim must pay what `pendingRewards` showed just before (within 2 wei of rounding in that view) and leave nothing pending.\n- Also new: an empty farm must hold no principal, and nothing is ever owed to the Treasury when it's an ordinary wallet.\n- These passed with the verifier seed and with seeds 1, 2 and 3.\n\n**The defect (medium): an oversized SqueekCore response can freeze all withdrawals and the emergency pause**\n- **Where:** `src/CheeseEmissionController.sol:488`. The same pattern is used for the other two SqueekCore reads.\n- **What happens:** the controller copies SqueekCore's entire response into memory before checking its size. If SqueekCore, which is a third-party upgradeable contract, returns a very large response, the copy runs out of gas no matter how much gas the user sends. Every withdrawal and claim reads SqueekCore first, so all user funds become stuck.\n- **Why the revision made it worse:** `pauseEmissions()` now reads SqueekCore before setting the pause. Before, the guardian could pause and exits would skip SqueekCore entirely; now the pause itself runs out of gas. The owner can't point the controller at a different SqueekCore because that address is fixed at deployment.\n- **Proof:** a SqueekCore that returns 2 MiB makes both `withdraw{gas: 30M}` and `pauseEmissions{gas: 30M}` fail. The same setup with a SqueekCore that just burns all its gas passes, which shows the copy is the cause. `forge build`'s own linter also flags this pattern.\n- **Fix:** copy at most the expected response size, and treat any other size as \"SqueekCore unavailable\".\n\nThe proof file in the findings is self-contained and fails on the current code. I didn't change anything outside `test/` apart from `.imd-findings.json`.","treeHash":"d7ce3436b7a4d7ec673b730111399ef9b05dc29b","usage":{"cachedInputTokens":1783538,"cost":{"source":"runtime","usdMicros":1595964},"inputTokens":53,"model":"claude-opus-5-5","outputTokens":24513,"runtime":"claude","turns":32,"wallClockMs":317929}},{"artifacts":[],"attempt":1,"bundleHash":"e4b7da033d69048ceedd080e5d44a2036653975fbdb24f7360b971a9e70b3aa3","device":"fa4b91aed45c94fa","findings":[],"hash":"d251f89f27d35127eb0e4d5d81a21b5a32d94d7185a8112e2bacf2ddd617bea7","nodeId":"5a771bd9-a897-4289-99a5-fdeef33dbd72","outcome":"completed","summary":"I left `launch.json` unchanged. It already matches the accepted source, and I found nothing that needed changing.\n\n**What I checked**\n- **Constructors:** the source commit that came after the manifest (`5e4371f`) changed no constructor. The five contracts and their parameters are still the same:\n  - `CheeseEmissionController(owner_, squeekCore_, treasury_)` gets `$owner`, the SqueekCore proxy `0xFD6C166D4B8E2962E65746a13e5a0797CD4dE0c4`, and `$owner`.\n  - `CheeseToken(teamRecipient_, treasuryRecipient_, liquidityRecipient_, emissionAuthority_)` gets `$owner` three times, then `$contract:CheeseEmissionController`.\n  - `CheeseFarmERC20(owner_, controller_, asset_)` gets `$owner`, the controller and `$contract:CheeseToken`.\n  - `CheeseFarmETH(owner_, controller_)` and `CheeseFarmSIMD(owner_, controller_)` each get `$owner` and the controller.\n- **Manifest shape:** it has only the `kind`, `contracts` and `notes` keys. The notes are 1,551 characters, and every reference points back to an earlier contract. The SqueekCore address in the manifest is the same one the source and scripts use.\n- **Deployment rehearsal:** a scratch test deployed all five contracts in one call, in manifest order, with these arguments. It passed. Every owner, plus the controller's guardian and Treasury, comes out as the launch owner `0xdea1…4449`, and none is the deploying factory. The token's only minter is the controller, the 210M premint goes to the owner, and the CHEESE farm's asset is CheeseToken.\n- **Build and tests:** `forge build` succeeds and all five contracts are under the 24,576-byte size limit. `forge test` with the verifier's fuzz seed passed all 88 tests in 8 suites.\n- **Not run:** the two pinned protected checks need the launch's environment variables, which aren't set on this machine, so they will first run at the verifier.\n\nThe notes don't mention one feature added after the manifest was written: if a Treasury fee payment fails, it is held and paid later through `payTreasury()`. The notes are still accurate without it. No file is changed; the only new file is the scratch test, which gets deleted before submission.","treeHash":"86a49e48161f553ddedd2b0d830c9b6bb428ca5a","usage":{"cachedInputTokens":232533,"cost":{"source":"runtime","usdMicros":347601},"inputTokens":16,"model":"claude-opus-5-5","outputTokens":3839,"runtime":"claude","turns":8,"wallClockMs":61294}},{"artifacts":[],"attempt":1,"bundleHash":"4ae77a00c7c78d5a87b37f66a9dfc14e7258c14fae9c250931195d1652ad3632","device":"c4617da5f1fd5ce8","findings":[],"hash":"e15b2b328157bffd1feaf53e67a52a406432c87fc22c2df6510e16414d949343","nodeId":"ef45bb39-6188-4c7b-8ef1-9e056dd24c04","outcome":"completed","summary":"All four findings reproduced and are answered as fixed in `.imd-responses.json`. The reviewer's proof now passes, and the full suite passes with the verifier's fuzz seed.\n\n**What reproduced**\n\n- **Return bomb (medium).** The proof failed on the starting tree exactly as reported: a 2 MiB payload from the Core made withdraw revert out of gas after ~29.5M gas.\n- **View rounding (low).** The invariant suite passed here with both the verifier's seed and the reviewer's, so the replay was machine-dependent. The arithmetic reproduced directly: the 7314-wei scenario read 1 wei low, and 23 of 59 ordinary cycles did too.\n- **sIMD rebasing (low) and owner-can-route-emission (info)** are correct readings of the code. Both are documentation gaps, not code defects, and the code is unchanged for them.\n\n**What changed**\n\n- `src/CheeseEmissionController.sol`: a new internal `_boundedStaticcall` makes the SqueekCore call with no output buffer, checks `returndatasize()` against the bound, and only then copies. State and configuration keep their exact sizes; roundPage gets a 4 KiB bound. Oversized payloads read as unavailable with an `OversizedReturnData` reason. `previewPoolEmission` now adds the unsynced increment to the per-point delta and floors once, matching the pull.\n- `src/farms/CheeseFarmSIMD.sol`: a note on the write-once setter that the asset must be non-rebasing, with the two failure modes.\n- Tests added: return-bomb Core (withdraw, claim, pause and views all succeed within 30M gas, then catch-up), oversized roundPage and configuration, and view-equals-claim across the reviewer's scenario plus 59 cycles.\n- Docs: README roles wording, after-launch sIMD step, read-path description and test results; SECURITY revision 2 table (items 13 to 16), trust assumptions, residual risks 6 and 8; ARCHITECTURE read-path paragraph. The controller ABI in `web/abi` was regenerated; the other four ABIs were unchanged.\n\n**Verification**\n\n| Check | Result |\n|---|---|\n| forge build | clean, only pre-existing lints |\n| forge test with verifier seed | 91 tests pass (94 with the scratch proof and probe) |\n| forge test, random seed, 512 fuzz / 128×64 invariant | 91 pass |\n| forge fmt --check | pass |\n\nNothing was committed, per the task's harness convention. The scratch copies of the proof and probe live under `test/scratch/` and are deleted before submission.","treeHash":"47e6ab719a10118d951f7a2c0d6eed9c28e980ae","usage":{"cachedInputTokens":1786046,"cost":{"source":"runtime","usdMicros":4425441},"inputTokens":610,"model":"claude-fable-5-1","outputTokens":34837,"runtime":"claude","turns":65,"wallClockMs":621919}},{"artifacts":[],"attempt":1,"bundleHash":"1c8268056513d702a4c5775824a09ce57b7cc9f65e009d3d2080ec62401c2cd6","device":"af4be7a5238dbcbf","findings":[],"hash":"f2f3661f7c702c35b05243debedb8027a2a8db3d2273e74aeacf529c1d201dfd","nodeId":"24fa880d-8de5-47bc-854d-870c7e3ad465","outcome":"completed","summary":"I've added tests for the implementer's latest fixes, and the full suite passes with the verifier's seed: `forge test` ran 138 tests in 9 suites with none failing or skipped.\n\nThis round's brief listed no findings to address. The implementer's last commit fixed the two defects my previous round's tests had pointed to:\n- **Large Core reply:** a SqueekCore that returned a huge payload could make withdraw, claim and pause run out of gas. The controller now checks the reply's size before copying it.\n- **Preview off by 1 wei:** `pendingRewards` could read up to 1 wei below what `claim` paid.\n\nI found no new defects in the revised code, so I wrote no `.imd-findings.json`.\n\n**Changes in `test/Adversarial.t.sol`:**\n- **Updated comment:** one test comment still said the large-reply problem was open and reported. It now points to the implementer's test for it.\n- **`testFuzz_pendingRewardsEqualsClaimWithUnsyncedBlocks` (512 runs):** checks that `pendingRewards` equals what `claim` pays, to the wei, for two depositors. It varies deposit sizes, the pool's weight, a partial withdrawal, and up to 3 halving periods of blocks certified on SqueekCore but not yet checkpointed.\n- **`test_stateReadAcceptsOnlyExactSize`:** a `state()` reply counts as unavailable at 255 bytes, 257 bytes, a valid reply plus a trailing word, or a 1000-byte revert. Nothing is priced from a bad read, and the exact 256 bytes is accepted.\n- **`test_roundPageBoundaryAcceptsAtBoundRejectsAbove`:** a well-formed round page of 23 records (4000 bytes) sets the start height; 24 records (4160 bytes, over the 4096 limit) is rejected and the start waits.\n- **`test_gasBurningCoreExitsFitUnderTxGasCap`:** with a SqueekCore that burns all the gas it gets, claim, a last-depositor withdraw, a partial CHEESE withdraw and the emergency pause each still finish inside Sepolia's per-transaction gas cap of 2^24 (EIP-7825). This checks the claim in `docs/SECURITY.md` item 8 that such a Core slows exits but doesn't block them.\n\n**Change in `test/SystemInvariants.t.sol`:** the random-sequence handler's outage action now has four modes: healthy, Core reverts, an oversized `state()` reply, and a reply one byte short. All 7 invariants still hold, including \"claim pays what `pendingRewards` promised\" and \"withdrawing held shares never reverts\".\n\nNothing is committed. The working tree holds only these two test files.","treeHash":"9e88fba2a07198d84c63c69735b6a79f8bf37a6f","usage":{"cachedInputTokens":1041202,"cost":{"source":"runtime","usdMicros":991543},"inputTokens":39,"model":"claude-opus-5-5","outputTokens":14016,"runtime":"claude","turns":22,"wallClockMs":190718}}],"verification":[{"checks":[{"durationMs":6044,"exitCode":0,"name":"build","output":"Compiling 72 files with Solc 0.8.26\nSolc 0.8.26 finished in 5.89s\nCompiler run successful!\n","passed":true},{"durationMs":1072,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 5 tests for test/Token.t.sol:TokenTest\n[PASS] testFuzz_mintNeverExceedsCap(uint256,uint256) (runs: 256, μ: 140236, ~: 142578)\n[PASS] test_capBoundsMintedTotalNotSupply() (gas: 237213)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 10284)\n[PASS] test_metadataAndPremint() (gas: 96648)\n[PASS] test_onlyEmissionAuthorityMints() (gas: 139970)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 17.26ms (10.92ms CPU time)\n\nRan 15 tests for test/Allocation.t.sol:AllocationTest\n[PASS] test_addPoolRejectsFarmOfAnotherController() (gas: 73769)\n[PASS] test_addPoolRejectsForeignOrDuplicateFarms() (gas: 41070)\n[PASS] test_allocationChangesRevertWhilePausedOrCoreUnavailable() (gas: 795463)\n[PASS] test_batchRejectsDuplicatePoolIds() (gas: 52423)\n[PASS] test_batchSetPoolWeightsReallocatesAtomically() (gas: 1253851)\n[PASS] test_disableAndEnablePool() (gas: 1421653)\n[PASS] test_emissionSplitsByWeightAndReserveStaysUnminted() (gas: 2285460)\n[PASS] test_gasScalabilityDoesNotGrowWithPoolCount() (gas: 11970744)\n[PASS] test_initialAllocationIs400_200_200_200() (gas: 86004)\n[PASS] test_onlyOwnerAdministers() (gas: 190854)\n[PASS] test_onlyPoolFarmPullsRewards() (gas: 581283)\n[PASS] test_reserveAssignedLaterIsNeverRetroactive() (gas: 997081)\n[PASS] test_setPoolWeightAppliesProspectivelyAfterCheckpoint() (gas: 1022608)\n[PASS] test_treasuryAndGuardianSetters() (gas: 105151)\n[PASS] test_weightsCannotExceedTotalPoints() (gas: 235936)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 41.01ms (35.67ms CPU time)\n\nRan 2 tests for test/Deploy.t.sol:DeployTest\n[PASS] test_constructorsNeedNoLiveContracts() (gas: 20234299)\n[PASS] test_wireAndHandOver() (gas: 21824523)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 75.87ms (2.69ms CPU time)\n\nRan 4 tests for test/FarmFuzz.t.sol:FarmFuzzTest\n[PASS] testFuzz_checkpointGroupingIsIrrelevant(uint16,uint16,uint16) (runs: 256, μ: 2840248, ~: 2841907)\n[PASS] testFuzz_depositWithdrawConservesAssets(uint96,uint96,uint96) (runs: 256, μ: 424120, ~: 429335)\n[PASS] testFuzz_lateDepositorNeverInheritsRewards(uint96,uint96,uint32) (runs: 256, μ: 1334808, ~: 1335103)\n[PASS] testFuzz_rewardsSplitProRata(uint96,uint96,uint32) (runs: 256, μ: 1364399, ~: 1364534)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 76.60ms (235.00ms CPU time)\n\nRan 32 tests for test/Farm.t.sol:FarmTest\n[PASS] test_cheeseFarmDeferredTreasuryShareIsReservedAgainstSkim() (gas: 1785805)\n[PASS] test_cheeseFarmKeepsPrincipalAndRewardReservesApart() (gas: 1658684)\n[PASS] test_claimNeverRevertsOnReserveShortfall() (gas: 1633340)\n[PASS] test_deferredTreasuryShareIsPaidOnceTheTreasuryCanReceive() (gas: 842038)\n[PASS] test_depositMintsSharesOneToOneInitially() (gas: 171642)\n[PASS] test_depositRejectsZeroAndPausedAndUnconfigured() (gas: 287163)\n[PASS] test_depositWaitsForSyncWhileWithdrawAndClaimDoNot() (gas: 1613657)\n[PASS] test_depositsCloseWhileEmissionsPausedWithdrawalsStayOpen() (gas: 816612)\n[PASS] test_directEthTransferRejected() (gas: 32983)\n[PASS] test_donationsDoNotMoveSharePriceAndAreSkimmableOnly() (gas: 704273)\n[PASS] test_emptyFarmEmissionIsForfeitedNotInherited() (gas: 1201451)\n[PASS] test_emptyFarmSweepsResidualRetainedFeeToTreasury() (gas: 662652)\n[PASS] test_erc20FarmDefersTreasuryShareTheTokenRefusesToDeliver() (gas: 1267605)\n[PASS] test_everyAccountCanClaimAfterEmissionEnds() (gas: 4753139)\n[PASS] test_feeOnTransferAssetCreditsReceivedAmount() (gas: 861595)\n[PASS] test_feeWorksAfterEmissionEnds() (gas: 1357986)\n[PASS] test_firstDepositDuringOutageCannotInheritForfeitableEmission() (gas: 978831)\n[PASS] test_gasBurningOrReceiverlessTreasuryIsDeferredToo() (gas: 904656)\n[PASS] test_hamsterBiteSplit85_14_1() (gas: 425636)\n[PASS] test_inflationAttackIsUneconomic() (gas: 1219626)\n[PASS] test_lastDepositorWithdrawalSendsFull15ToTreasury() (gas: 291417)\n[PASS] test_newDepositorGetsNothingFromEarlierBlocks() (gas: 1268602)\n[PASS] test_noCooldownWithdrawSameBlockAsDeposit() (gas: 255347)\n[PASS] test_onPoolRegisteredOnlyController() (gas: 341811)\n[PASS] test_oneWeiFirstDepositorCannotBrickLaterDeposits() (gas: 4764167)\n[PASS] test_ownerCannotTouchPrincipalOrRewards() (gas: 1050621)\n[PASS] test_partialWithdrawerBenefitsFromOwnRetainedShare() (gas: 441691)\n[PASS] test_pendingRewardsMatchesClaimExactlyWithUnsyncedBlocks() (gas: 26569510)\n[PASS] test_reentrancyBlockedOnWithdrawDepositAndClaim() (gas: 1190044)\n[PASS] test_revertingTreasuryNeverBlocksWithdrawalEvenWithoutAnOwner() (gas: 765990)\n[PASS] test_rewardsAccrueProRataAndClaimIsIndependentOfPrincipal() (gas: 1790710)\n[PASS] test_withdrawRejectsZeroAndTooMany() (gas: 212948)\nSuite result: ok. 32 passed; 0 failed; 0 skipped; finished in 79.40ms (56.30ms CPU time)\n\nRan 32 tests for test/Emission.t.sol:EmissionTest\n[PASS] testFuzz_issuedMatchesReferenceAndIsMonotone(uint64,uint64) (runs: 256, μ: 6160, ~: 6059)\n[PASS] test_activateRevertsOnClockMismatch() (gas: 84746)\n[PASS] test_activateRevertsTwiceAndForNonOwner() (gas: 135549)\n[PASS] test_activateRevertsWhenCoreUnavailable() (gas: 76134)\n[PASS] test_activateSchedulesNextRound() (gas: 97555)\n[PASS] test_activateTreatsUndecodableConfigurationAsUnavailable() (gas: 118586)\n[PASS] test_bindTokenOnceAndOnlyMatchingAuthority() (gas: 103028)\n[PASS] test_blocksBeforeActivationNeverEmit() (gas: 2653933)\n[PASS] test_checkpointBeforeActivationIsNoop() (gas: 90644)\n[PASS] test_completionEmitsEventAndMintsAtMostAllowance() (gas: 1451547)\n[PASS] test_coreRegressionIsIgnored() (gas: 708494)\n[PASS] test_coreUnavailableIsTolerated() (gas: 742666)\n[PASS] test_coreWithoutCodeIsUnavailableNotARevert() (gas: 972987)\n[PASS] test_delayedCheckpointEqualsFrequentCheckpoints() (gas: 2456864)\n[PASS] test_fullScheduleCompletesAndStaysCapped() (gas: 745512)\n[PASS] test_guardianCanPauseOnlyOwnerUnpauses() (gas: 171320)\n[PASS] test_halvingCrossingInsideOneCheckpoint() (gas: 770863)\n[PASS] test_issuedMatchesLiveSqueekMiningVectors() (gas: 8033)\n[PASS] test_noDoubleMintingOnRepeatedCheckpoints() (gas: 833773)\n[PASS] test_noEmissionBeforeStartRoundCertified() (gas: 2423072)\n[PASS] test_oversizedConfigurationIsUnavailableAtActivation() (gas: 29535105)\n[PASS] test_oversizedRoundPageDelaysAnchorWithoutRevert() (gas: 2513958)\n[PASS] test_pausePricesBlocksCertifiedBeforeItForThoseStakedThen() (gas: 1524942)\n[PASS] test_pauseStillWorksWhenCoreIsUnavailable() (gas: 791011)\n[PASS] test_pauseStopsEmissionAndUnpauseCatchesUp() (gas: 934343)\n[PASS] test_pendingCheckpointPreview() (gas: 2668431)\n[PASS] test_perBlockRatesAcrossHalvings() (gas: 12379)\n[PASS] test_returnBombCoreDoesNotBlockExitsOrPause() (gas: 176435117)\n[PASS] test_scheduleConstantsMatchSqueekMining() (gas: 1716)\n[PASS] test_undecodableCoreStateIsUnavailableNotARevert() (gas: 1585116)\n[PASS] test_undecodableRoundPageDelaysAnchorWithoutRevert() (gas: 2534024)\n[PASS] test_zeroBlockMovementEmitsNothing() (gas: 742414)\nSuite result: ok. 32 passed; 0 failed; 0 skipped; finished in 247.77ms (357.27ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_entitlementsNeverExceedReserve\n[PASS] invariant_ethConservation\n[PASS] invariant_farmBalanceCoversPrincipal\n[PASS] invariant_sharesSumAndBacking\n[PASS] invariant_supplyAccounting\n[PASS] invariant_userActionsNeverRevertUnexpectedly\n[PASS] invariant_weightsWithinTotal\n InvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭---------------+------------+-------+---------+----------╮\n| Contract      | Selector   | Calls | Reverts | Discards |\n+=========================================================+\n| CheeseHandler | advance    | 298   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | checkpoint | 290   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | claim      | 289   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | deposit    | 287   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | setWeight  | 292   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | toggle     | 299   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | withdraw   | 293   | 0       | 0        |\n╰---------------+------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 919.21ms (907.21ms CPU time)\n\nRan 7 test suites in 921.63ms (1.46s CPU time): 91 tests passed, 0 failed, 0 skipped (91 total tests)\n","passed":true},{"durationMs":86,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CheeseEmissionController.acceptOwnership()\",\"CheeseEmissionController.activate()\",\"CheeseEmissionController.addPool(address,uint16)\",\"CheeseEmissionController.batchSetPoolWeights(uint256[],uint16[])\",\"CheeseEmissionController.bindToken(address)\",\"CheeseEmissionController.checkpoint()\",\"CheeseEmissionController.disablePool(uint256)\",\"CheeseEmissionController.enablePool(uint256)\",\"CheeseEmissionController.pauseEmissions()\",\"CheeseEmissionController.pullRewards(uint256,bool)\",\"CheeseEmissionController.renounceOwnership()\",\"CheeseEmissionController.setGuardian(address)\",\"CheeseEmissionController.setPoolWeight(uint256,uint16)\",\"CheeseEmissionController.setTreasury(address)\",\"CheeseEmissionController.transferOwnership(address)\",\"CheeseEmissionController.unpauseEmissions()\",\"CheeseFarmERC20.acceptOwnership()\",\"CheeseFarmERC20.claim(address)\",\"CheeseFarmERC20.deposit(uint256)\",\"CheeseFarmERC20.onPoolRegistered(uint256)\",\"CheeseFarmERC20.payTreasury()\",\"CheeseFarmERC20.renounceOwnership()\",\"CheeseFarmERC20.setDepositsPaused(bool)\",\"CheeseFarmERC20.skimSurplus()\",\"CheeseFarmERC20.transferOwnership(address)\",\"CheeseFarmERC20.withdraw(uint256)\",\"CheeseFarmETH.acceptOwnership()\",\"CheeseFarmETH.claim(address)\",\"CheeseFarmETH.deposit()\",\"CheeseFarmETH.onPoolRegistered(uint256)\",\"CheeseFarmETH.payTreasury()\",\"CheeseFarmETH.receive()\",\"CheeseFarmETH.renounceOwnership()\",\"CheeseFarmETH.setDepositsPaused(bool)\",\"CheeseFarmETH.skimSurplus()\",\"CheeseFarmETH.transferOwnership(address)\",\"CheeseFarmETH.withdraw(uint256)\",\"CheeseFarmSIMD.acceptOwnership()\",\"CheeseFarmSIMD.claim(address)\",\"CheeseFarmSIMD.deposit(uint256)\",\"CheeseFarmSIMD.onPoolRegistered(uint256)\",\"CheeseFarmSIMD.payTreasury()\",\"CheeseFarmSIMD.renounceOwnership()\",\"CheeseFarmSIMD.setAsset(address)\",\"CheeseFarmSIMD.setDepositsPaused(bool)\",\"CheeseFarmSIMD.skimSurplus()\",\"CheeseFarmSIMD.transferOwnership(address)\",\"CheeseFarmSIMD.withdraw(uint256)\",\"CheeseToken.approve(address,uint256)\",\"CheeseToken.burn(uint256)\",\"CheeseToken.mintEmission(address,uint256)\",\"CheeseToken.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"CheeseToken.transfer(address,uint256)\",\"CheeseToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"README.md\":256,\"docs/ARCHITECTURE.md\":87,\"docs/SECURITY.md\":129,\"docs/SQUEEKCHAIN_VERIFICATION.md\":85,\"foundry.toml\":28,\"launch.json\":45,\"remappings.txt\":2,\"script/Deploy.s.sol\":112,\"script/VerifySqueekchain.s.sol\":70,\"src/CheeseEmissionController.sol\":608,\"src/CheeseFarm.sol\":375,\"src/CheeseToken.sol\":81,\"src/farms/CheeseFarmERC20.sol\":47,\"src/farms/CheeseFarmETH.sol\":46,\"src/farms/CheeseFarmSIMD.sol\":71,\"src/interfaces/ICheeseEmissionController.sol\":25,\"src/interfaces/ICheeseFarm.sol\":9,\"src/interfaces/ICheeseToken.sol\":13,\"src/interfaces/ISqueekCore.sol\":68,\"src/interfaces/ISqueekMining.sol\":36,\"src/libraries/SqueekIssuance.sol\":70,\"test/Allocation.t.sol\":319,\"test/CheeseBase.t.sol\":142,\"test/Deploy.t.sol\":76,\"test/Emission.t.sol\":509,\"test/Farm.t.sol\":629,\"test/FarmFuzz.t.sol\":80,\"test/Invariants.t.sol\":188,\"test/Token.t.sol\":75,\"test/mocks/Attackers.sol\":71,\"test/mocks/MockERC20.sol\":51,\"test/mocks/MockSqueekCore.sol\":100,\"web/abi/CheeseEmissionController.json\":1671,\"web/abi/CheeseFarmERC20.json\":969,\"web/abi/CheeseFarmETH.json\":970,\"web/abi/CheeseFarmSIMD.json\":1000,\"web/abi/CheeseToken.json\":750,\"web/config.sepolia.json\":49},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"24f8c58dcea5362b84b7a04d35b9bdbb944457b5787c557cf2fea34b03ea1089","verifiedTreeHash":"7b7439c9e9823d24896d14c51191a14d5f5d9d53","verifierVersion":"0.1.0+18543036"},{"checks":[{"durationMs":5981,"exitCode":0,"name":"build","output":"Compiling 74 files with Solc 0.8.26\nSolc 0.8.26 finished in 5.85s\nCompiler run successful!\n","passed":true},{"durationMs":2287,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/Deploy.t.sol:DeployTest\n[PASS] test_constructorsNeedNoLiveContracts() (gas: 18159387)\n[PASS] test_wireAndHandOver() (gas: 19702567)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 2.83ms (2.06ms CPU time)\n\nRan 5 tests for test/Token.t.sol:TokenTest\n[PASS] testFuzz_mintNeverExceedsCap(uint256,uint256) (runs: 256, μ: 140360, ~: 142584)\n[PASS] test_capBoundsMintedTotalNotSupply() (gas: 237213)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 10284)\n[PASS] test_metadataAndPremint() (gas: 96648)\n[PASS] test_onlyEmissionAuthorityMints() (gas: 139970)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 8.37ms (9.89ms CPU time)\n\nRan 23 tests for test/Emission.t.sol:EmissionTest\n[PASS] testFuzz_issuedMatchesReferenceAndIsMonotone(uint64,uint64) (runs: 256, μ: 6052, ~: 5901)\n[PASS] test_activateRevertsOnClockMismatch() (gas: 80602)\n[PASS] test_activateRevertsTwiceAndForNonOwner() (gas: 131446)\n[PASS] test_activateRevertsWhenCoreUnavailable() (gas: 75677)\n[PASS] test_activateSchedulesNextRound() (gas: 93496)\n[PASS] test_bindTokenOnceAndOnlyMatchingAuthority() (gas: 102940)\n[PASS] test_blocksBeforeActivationNeverEmit() (gas: 2639273)\n[PASS] test_checkpointBeforeActivationIsNoop() (gas: 90579)\n[PASS] test_completionEmitsEventAndMintsAtMostAllowance() (gas: 1429808)\n[PASS] test_coreRegressionIsIgnored() (gas: 690511)\n[PASS] test_coreUnavailableIsTolerated() (gas: 727194)\n[PASS] test_delayedCheckpointEqualsFrequentCheckpoints() (gas: 2408506)\n[PASS] test_fullScheduleCompletesAndStaysCapped() (gas: 730815)\n[PASS] test_guardianCanPauseOnlyOwnerUnpauses() (gas: 171190)\n[PASS] test_halvingCrossingInsideOneCheckpoint() (gas: 752848)\n[PASS] test_issuedMatchesLiveSqueekMiningVectors() (gas: 8033)\n[PASS] test_noDoubleMintingOnRepeatedCheckpoints() (gas: 802328)\n[PASS] test_noEmissionBeforeStartRoundCertified() (gas: 2415656)\n[PASS] test_pauseStopsEmissionAndUnpauseCatchesUp() (gas: 904567)\n[PASS] test_pendingCheckpointPreview() (gas: 2650457)\n[PASS] test_perBlockRatesAcrossHalvings() (gas: 12379)\n[PASS] test_scheduleConstantsMatchSqueekMining() (gas: 1694)\n[PASS] test_zeroBlockMovementEmitsNothing() (gas: 724419)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 25.14ms (42.84ms CPU time)\n\nRan 4 tests for test/FarmFuzz.t.sol:FarmFuzzTest\n[PASS] testFuzz_checkpointGroupingIsIrrelevant(uint16,uint16,uint16) (runs: 256, μ: 2822190, ~: 2823864)\n[PASS] testFuzz_depositWithdrawConservesAssets(uint96,uint96,uint96) (runs: 256, μ: 420305, ~: 425266)\n[PASS] testFuzz_lateDepositorNeverInheritsRewards(uint96,uint96,uint32) (runs: 256, μ: 1300047, ~: 1300295)\n[PASS] testFuzz_rewardsSplitProRata(uint96,uint96,uint32) (runs: 256, μ: 1335109, ~: 1335206)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 58.88ms (190.68ms CPU time)\n\nRan 14 tests for test/Allocation.t.sol:AllocationTest\n[PASS] test_addPoolRejectsFarmOfAnotherController() (gas: 73703)\n[PASS] test_addPoolRejectsForeignOrDuplicateFarms() (gas: 41070)\n[PASS] test_allocationChangesRevertWhilePausedOrCoreUnavailable() (gas: 694910)\n[PASS] test_batchSetPoolWeightsReallocatesAtomically() (gas: 1229299)\n[PASS] test_disableAndEnablePool() (gas: 1392568)\n[PASS] test_emissionSplitsByWeightAndReserveStaysUnminted() (gas: 2246727)\n[PASS] test_gasScalabilityDoesNotGrowWithPoolCount() (gas: 11738473)\n[PASS] test_initialAllocationIs400_200_200_200() (gas: 86203)\n[PASS] test_onlyOwnerAdministers() (gas: 190832)\n[PASS] test_onlyPoolFarmPullsRewards() (gas: 569874)\n[PASS] test_reserveAssignedLaterIsNeverRetroactive() (gas: 975884)\n[PASS] test_setPoolWeightAppliesProspectivelyAfterCheckpoint() (gas: 1000166)\n[PASS] test_treasuryAndGuardianSetters() (gas: 105196)\n[PASS] test_weightsCannotExceedTotalPoints() (gas: 235783)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 67.59ms (19.22ms CPU time)\n\nRan 21 tests for test/Farm.t.sol:FarmTest\n[PASS] test_blockedTreasuryCannotBeSetByNonOwnerAndOwnerCanFix() (gas: 530175)\n[PASS] test_cheeseFarmKeepsPrincipalAndRewardReservesApart() (gas: 1621658)\n[PASS] test_depositMintsSharesOneToOneInitially() (gas: 170360)\n[PASS] test_depositRejectsZeroAndPausedAndUnconfigured() (gas: 287276)\n[PASS] test_depositsCloseWhileEmissionsPausedWithdrawalsStayOpen() (gas: 787184)\n[PASS] test_directEthTransferRejected() (gas: 33005)\n[PASS] test_donationsDoNotMoveSharePriceAndAreSkimmableOnly() (gas: 694851)\n[PASS] test_emptyFarmEmissionIsForfeitedNotInherited() (gas: 1178200)\n[PASS] test_feeOnTransferAssetCreditsReceivedAmount() (gas: 860345)\n[PASS] test_feeWorksAfterEmissionEnds() (gas: 1334833)\n[PASS] test_hamsterBiteSplit85_14_1() (gas: 421677)\n[PASS] test_inflationAttackIsUneconomic() (gas: 1203589)\n[PASS] test_lastDepositorWithdrawalSendsFull15ToTreasury() (gas: 283364)\n[PASS] test_newDepositorGetsNothingFromEarlierBlocks() (gas: 1237339)\n[PASS] test_noCooldownWithdrawSameBlockAsDeposit() (gas: 252423)\n[PASS] test_onPoolRegisteredOnlyController() (gas: 339141)\n[PASS] test_ownerCannotTouchPrincipalOrRewards() (gas: 1028309)\n[PASS] test_partialWithdrawerBenefitsFromOwnRetainedShare() (gas: 438514)\n[PASS] test_reentrancyBlockedOnWithdrawDepositAndClaim() (gas: 1183112)\n[PASS] test_rewardsAccrueProRataAndClaimIsIndependentOfPrincipal() (gas: 1740173)\n[PASS] test_withdrawRejectsZeroAndTooMany() (gas: 211556)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 71.28ms (13.86ms CPU time)\n\nRan 37 tests for test/Adversarial.t.sol:AdversarialTest\n[PASS] testFuzz_anyCheckpointSplitMatchesSchedule(uint32[6]) (runs: 512, μ: 1256209, ~: 1262521)\n[PASS] testFuzz_hamsterBiteExactSplit(uint96,uint96,uint16) (runs: 512, μ: 449114, ~: 449129)\n[PASS] test_activateRequiresBoundToken() (gas: 67844)\n[PASS] test_addPoolZeroAddressAndOverweight() (gas: 232353)\n[PASS] test_allocationChangeRevertsDuringRegression() (gas: 700962)\n[PASS] test_allocationChangesAllowedAfterCompletion() (gas: 965985)\n[PASS] test_batchRejectsLengthMismatchEmptyAndOverweight() (gas: 153148)\n[PASS] test_bindTokenZeroAndNonOwner() (gas: 67244)\n[PASS] test_burnMoreThanBalanceReverts() (gas: 59644)\n[PASS] test_cannotWithdrawSomeoneElsesShares() (gas: 173202)\n[PASS] test_claimToZeroAddressReverts() (gas: 37289)\n[PASS] test_claimWithNothingReturnsZeroAndMovesNothing() (gas: 525433)\n[PASS] test_constructorRejectsZeroCoreOrTreasury() (gas: 8281)\n[PASS] test_depositsPausedStillAllowsWithdrawAndClaim() (gas: 1342919)\n[PASS] test_disableTwiceAndEnableEnabledRevert() (gas: 117579)\n[PASS] test_disabledPoolKeepsAccruedButEarnsNothingMore() (gas: 1222957)\n[PASS] test_lastWithdrawerPaysFullFifteenPercentToTreasuryAfterFees() (gas: 501304)\n[PASS] test_malformedCoreDataGuardianPauseRestoresWithdrawals() (gas: 1146582)\n[PASS] test_mintEmissionToZeroAddressReverts() (gas: 32496)\n[PASS] test_mintExactlyRemainingThenOneMoreReverts() (gas: 117802)\n[PASS] test_onlyOwnerPausesDepositsAndSkims() (gas: 111828)\n[PASS] test_ownerAndTreasuryCannotMint() (gas: 93564)\n[PASS] test_ownershipIsTwoStep() (gas: 136485)\n[PASS] test_partialBlockProgressNeverEmits() (gas: 716741)\n[PASS] test_pauseStateMachine() (gas: 149449)\n[PASS] test_reenabledPoolGetsNothingForDisabledPeriod() (gas: 1268533)\n[PASS] test_regressionThenRecoveryNeverDoubleCounts() (gas: 861458)\n[PASS] test_settersRejectZeroAndStrangers() (gas: 163427)\n[PASS] test_simdSetAssetGuards() (gas: 180086)\n[PASS] test_singleBlockEmissionAtEveryBoundary() (gas: 2151811)\n[PASS] test_startHeightIgnoresPartialProgressAtH0() (gas: 2620773)\n[PASS] test_unavailableCoreDoesNotBlockWithdrawOrClaim() (gas: 1197652)\n[PASS] test_unconfiguredSimdFarmForfeitsItsEmission() (gas: 921164)\n[PASS] test_unknownPoolIdsRevert() (gas: 145260)\n[PASS] test_weightChangeOnDisabledPoolAppliesOnEnable() (gas: 1159579)\n[PASS] test_weightZeroPoolEarnsNothing() (gas: 889481)\n[PASS] test_withdrawingDustSharesBurnsThemForNothing() (gas: 233031)\nSuite result: ok. 37 passed; 0 failed; 0 skipped; finished in 77.65ms (157.71ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_ethConservation\n[PASS] invariant_farmBalanceCoversPrincipal\n[PASS] invariant_sharesSumAndBacking\n[PASS] invariant_supplyAccounting\n[PASS] invariant_weightsWithinTotal\n InvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭---------------+------------+-------+---------+----------╮\n| Contract      | Selector   | Calls | Reverts | Discards |\n+=========================================================+\n| CheeseHandler | advance    | 298   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | checkpoint | 290   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | claim      | 289   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | deposit    | 287   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | setWeight  | 292   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | toggle     | 299   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | withdraw   | 293   | 0       | 0        |\n╰---------------+------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 452.79ms (448.15ms CPU time)\n\nRan 1 test for test/SystemInvariants.t.sol:SystemInvariantTest\n[PASS]\nSystemInvariantTest invariants:\n[PASS] invariant_monotoneAndFinal\n[PASS] invariant_principalConservation\n[PASS] invariant_reservesBacked\n[PASS] invariant_rewardsOwedBoundedByReserve\n[PASS] invariant_sharesFullySubscribed\n[PASS] invariant_supplyAndMintAccounting\n SystemInvariantTest invariants (runs: 128, calls: 6144, reverts: 0)\n\n╭---------------+----------------+-------+---------+----------╮\n| Contract      | Selector       | Calls | Reverts | Discards |\n+=============================================================+\n| SystemHandler | advance        | 462   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | checkpoint     | 470   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | claim          | 484   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | depositCheese  | 459   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | depositEth     | 475   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | donateCheese   | 488   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | setWeight      | 496   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | skim           | 481   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | toggleOutage   | 455   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | togglePause    | 463   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | togglePool     | 478   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | withdrawCheese | 495   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | withdrawEth    | 438   | 0       | 0        |\n╰---------------+----------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.13s (2.13s CPU time)\n\nRan 9 test suites in 2.14s (2.90s CPU time): 108 tests passed, 0 failed, 0 skipped (108 total tests)\n","passed":true},{"durationMs":66,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CheeseEmissionController.acceptOwnership()\",\"CheeseEmissionController.activate()\",\"CheeseEmissionController.addPool(address,uint16)\",\"CheeseEmissionController.batchSetPoolWeights(uint256[],uint16[])\",\"CheeseEmissionController.bindToken(address)\",\"CheeseEmissionController.checkpoint()\",\"CheeseEmissionController.disablePool(uint256)\",\"CheeseEmissionController.enablePool(uint256)\",\"CheeseEmissionController.pauseEmissions()\",\"CheeseEmissionController.pullRewards(uint256,bool)\",\"CheeseEmissionController.renounceOwnership()\",\"CheeseEmissionController.setGuardian(address)\",\"CheeseEmissionController.setPoolWeight(uint256,uint16)\",\"CheeseEmissionController.setTreasury(address)\",\"CheeseEmissionController.transferOwnership(address)\",\"CheeseEmissionController.unpauseEmissions()\",\"CheeseFarmERC20.acceptOwnership()\",\"CheeseFarmERC20.claim(address)\",\"CheeseFarmERC20.deposit(uint256)\",\"CheeseFarmERC20.onPoolRegistered(uint256)\",\"CheeseFarmERC20.renounceOwnership()\",\"CheeseFarmERC20.setDepositsPaused(bool)\",\"CheeseFarmERC20.skimSurplus()\",\"CheeseFarmERC20.transferOwnership(address)\",\"CheeseFarmERC20.withdraw(uint256)\",\"CheeseFarmETH.acceptOwnership()\",\"CheeseFarmETH.claim(address)\",\"CheeseFarmETH.deposit()\",\"CheeseFarmETH.onPoolRegistered(uint256)\",\"CheeseFarmETH.receive()\",\"CheeseFarmETH.renounceOwnership()\",\"CheeseFarmETH.setDepositsPaused(bool)\",\"CheeseFarmETH.skimSurplus()\",\"CheeseFarmETH.transferOwnership(address)\",\"CheeseFarmETH.withdraw(uint256)\",\"CheeseFarmSIMD.acceptOwnership()\",\"CheeseFarmSIMD.claim(address)\",\"CheeseFarmSIMD.deposit(uint256)\",\"CheeseFarmSIMD.onPoolRegistered(uint256)\",\"CheeseFarmSIMD.renounceOwnership()\",\"CheeseFarmSIMD.setAsset(address)\",\"CheeseFarmSIMD.setDepositsPaused(bool)\",\"CheeseFarmSIMD.skimSurplus()\",\"CheeseFarmSIMD.transferOwnership(address)\",\"CheeseFarmSIMD.withdraw(uint256)\",\"CheeseToken.approve(address,uint256)\",\"CheeseToken.burn(uint256)\",\"CheeseToken.mintEmission(address,uint256)\",\"CheeseToken.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"CheeseToken.transfer(address,uint256)\",\"CheeseToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"README.md\":217,\"docs/ARCHITECTURE.md\":71,\"docs/SECURITY.md\":76,\"docs/SQUEEKCHAIN_VERIFICATION.md\":85,\"foundry.toml\":28,\"remappings.txt\":2,\"script/Deploy.s.sol\":110,\"script/VerifySqueekchain.s.sol\":70,\"src/CheeseEmissionController.sol\":515,\"src/CheeseFarm.sol\":308,\"src/CheeseToken.sol\":81,\"src/farms/CheeseFarmERC20.sol\":43,\"src/farms/CheeseFarmETH.sol\":41,\"src/farms/CheeseFarmSIMD.sol\":61,\"src/interfaces/ICheeseEmissionController.sol\":25,\"src/interfaces/ICheeseFarm.sol\":9,\"src/interfaces/ICheeseToken.sol\":13,\"src/interfaces/ISqueekCore.sol\":68,\"src/interfaces/ISqueekMining.sol\":36,\"src/libraries/SqueekIssuance.sol\":70,\"test/Adversarial.t.sol\":567,\"test/Allocation.t.sol\":305,\"test/CheeseBase.t.sol\":142,\"test/Deploy.t.sol\":70,\"test/Emission.t.sol\":310,\"test/Farm.t.sol\":340,\"test/FarmFuzz.t.sol\":80,\"test/Invariants.t.sol\":154,\"test/SystemInvariants.t.sol\":305,\"test/Token.t.sol\":75,\"test/mocks/Attackers.sol\":52,\"test/mocks/MockERC20.sol\":30,\"test/mocks/MockSqueekCore.sol\":100,\"web/abi/CheeseEmissionController.json\":1400,\"web/abi/CheeseFarmERC20.json\":882,\"web/abi/CheeseFarmETH.json\":883,\"web/abi/CheeseFarmSIMD.json\":913,\"web/abi/CheeseToken.json\":750,\"web/config.sepolia.json\":49},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"544321344773be95daf0bd75a7b28f522f7fc7a25cc907813ee785b2ca5f1efc","verifiedTreeHash":"237d1756a1e49e3dd12dbf473eb2fe192651217a","verifierVersion":"0.1.0+18543036"},{"checks":[{"durationMs":3998,"exitCode":0,"name":"build","output":"Compiling 72 files with Solc 0.8.26\nSolc 0.8.26 finished in 3.89s\nCompiler run successful!\n","passed":true},{"durationMs":604,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 23 tests for test/Emission.t.sol:EmissionTest\n[PASS] testFuzz_issuedMatchesReferenceAndIsMonotone(uint64,uint64) (runs: 256, μ: 6087, ~: 5961)\n[PASS] test_activateRevertsOnClockMismatch() (gas: 80602)\n[PASS] test_activateRevertsTwiceAndForNonOwner() (gas: 131446)\n[PASS] test_activateRevertsWhenCoreUnavailable() (gas: 75677)\n[PASS] test_activateSchedulesNextRound() (gas: 93496)\n[PASS] test_bindTokenOnceAndOnlyMatchingAuthority() (gas: 102940)\n[PASS] test_blocksBeforeActivationNeverEmit() (gas: 2639273)\n[PASS] test_checkpointBeforeActivationIsNoop() (gas: 90579)\n[PASS] test_completionEmitsEventAndMintsAtMostAllowance() (gas: 1429808)\n[PASS] test_coreRegressionIsIgnored() (gas: 690511)\n[PASS] test_coreUnavailableIsTolerated() (gas: 727194)\n[PASS] test_delayedCheckpointEqualsFrequentCheckpoints() (gas: 2408506)\n[PASS] test_fullScheduleCompletesAndStaysCapped() (gas: 730815)\n[PASS] test_guardianCanPauseOnlyOwnerUnpauses() (gas: 171190)\n[PASS] test_halvingCrossingInsideOneCheckpoint() (gas: 752848)\n[PASS] test_issuedMatchesLiveSqueekMiningVectors() (gas: 8033)\n[PASS] test_noDoubleMintingOnRepeatedCheckpoints() (gas: 802328)\n[PASS] test_noEmissionBeforeStartRoundCertified() (gas: 2415656)\n[PASS] test_pauseStopsEmissionAndUnpauseCatchesUp() (gas: 904567)\n[PASS] test_pendingCheckpointPreview() (gas: 2650457)\n[PASS] test_perBlockRatesAcrossHalvings() (gas: 12379)\n[PASS] test_scheduleConstantsMatchSqueekMining() (gas: 1694)\n[PASS] test_zeroBlockMovementEmitsNothing() (gas: 724419)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 48.88ms (16.94ms CPU time)\n\nRan 21 tests for test/Farm.t.sol:FarmTest\n[PASS] test_blockedTreasuryCannotBeSetByNonOwnerAndOwnerCanFix() (gas: 530175)\n[PASS] test_cheeseFarmKeepsPrincipalAndRewardReservesApart() (gas: 1621658)\n[PASS] test_depositMintsSharesOneToOneInitially() (gas: 170360)\n[PASS] test_depositRejectsZeroAndPausedAndUnconfigured() (gas: 287276)\n[PASS] test_depositsCloseWhileEmissionsPausedWithdrawalsStayOpen() (gas: 787184)\n[PASS] test_directEthTransferRejected() (gas: 33005)\n[PASS] test_donationsDoNotMoveSharePriceAndAreSkimmableOnly() (gas: 694851)\n[PASS] test_emptyFarmEmissionIsForfeitedNotInherited() (gas: 1178200)\n[PASS] test_feeOnTransferAssetCreditsReceivedAmount() (gas: 860345)\n[PASS] test_feeWorksAfterEmissionEnds() (gas: 1334833)\n[PASS] test_hamsterBiteSplit85_14_1() (gas: 421677)\n[PASS] test_inflationAttackIsUneconomic() (gas: 1203589)\n[PASS] test_lastDepositorWithdrawalSendsFull15ToTreasury() (gas: 283364)\n[PASS] test_newDepositorGetsNothingFromEarlierBlocks() (gas: 1237339)\n[PASS] test_noCooldownWithdrawSameBlockAsDeposit() (gas: 252423)\n[PASS] test_onPoolRegisteredOnlyController() (gas: 339141)\n[PASS] test_ownerCannotTouchPrincipalOrRewards() (gas: 1028309)\n[PASS] test_partialWithdrawerBenefitsFromOwnRetainedShare() (gas: 438514)\n[PASS] test_reentrancyBlockedOnWithdrawDepositAndClaim() (gas: 1183112)\n[PASS] test_rewardsAccrueProRataAndClaimIsIndependentOfPrincipal() (gas: 1740173)\n[PASS] test_withdrawRejectsZeroAndTooMany() (gas: 211556)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 48.97ms (13.44ms CPU time)\n\nRan 2 tests for test/Deploy.t.sol:DeployTest\n[PASS] test_constructorsNeedNoLiveContracts() (gas: 18159387)\n[PASS] test_wireAndHandOver() (gas: 19702567)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 53.85ms (7.04ms CPU time)\n\nRan 5 tests for test/Token.t.sol:TokenTest\n[PASS] testFuzz_mintNeverExceedsCap(uint256,uint256) (runs: 256, μ: 140364, ~: 142590)\n[PASS] test_capBoundsMintedTotalNotSupply() (gas: 237213)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 10284)\n[PASS] test_metadataAndPremint() (gas: 96648)\n[PASS] test_onlyEmissionAuthorityMints() (gas: 139970)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 53.87ms (10.47ms CPU time)\n\nRan 14 tests for test/Allocation.t.sol:AllocationTest\n[PASS] test_addPoolRejectsFarmOfAnotherController() (gas: 73703)\n[PASS] test_addPoolRejectsForeignOrDuplicateFarms() (gas: 41070)\n[PASS] test_allocationChangesRevertWhilePausedOrCoreUnavailable() (gas: 694910)\n[PASS] test_batchSetPoolWeightsReallocatesAtomically() (gas: 1229299)\n[PASS] test_disableAndEnablePool() (gas: 1392568)\n[PASS] test_emissionSplitsByWeightAndReserveStaysUnminted() (gas: 2246727)\n[PASS] test_gasScalabilityDoesNotGrowWithPoolCount() (gas: 11738473)\n[PASS] test_initialAllocationIs400_200_200_200() (gas: 86203)\n[PASS] test_onlyOwnerAdministers() (gas: 190832)\n[PASS] test_onlyPoolFarmPullsRewards() (gas: 569874)\n[PASS] test_reserveAssignedLaterIsNeverRetroactive() (gas: 975884)\n[PASS] test_setPoolWeightAppliesProspectivelyAfterCheckpoint() (gas: 1000166)\n[PASS] test_treasuryAndGuardianSetters() (gas: 105196)\n[PASS] test_weightsCannotExceedTotalPoints() (gas: 235783)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 72.60ms (23.69ms CPU time)\n\nRan 4 tests for test/FarmFuzz.t.sol:FarmFuzzTest\n[PASS] testFuzz_checkpointGroupingIsIrrelevant(uint16,uint16,uint16) (runs: 256, μ: 2822185, ~: 2823864)\n[PASS] testFuzz_depositWithdrawConservesAssets(uint96,uint96,uint96) (runs: 256, μ: 420305, ~: 425266)\n[PASS] testFuzz_lateDepositorNeverInheritsRewards(uint96,uint96,uint32) (runs: 256, μ: 1300033, ~: 1300295)\n[PASS] testFuzz_rewardsSplitProRata(uint96,uint96,uint32) (runs: 256, μ: 1335099, ~: 1335206)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 72.55ms (195.85ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_ethConservation\n[PASS] invariant_farmBalanceCoversPrincipal\n[PASS] invariant_sharesSumAndBacking\n[PASS] invariant_supplyAccounting\n[PASS] invariant_weightsWithinTotal\n InvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭---------------+------------+-------+---------+----------╮\n| Contract      | Selector   | Calls | Reverts | Discards |\n+=========================================================+\n| CheeseHandler | advance    | 298   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | checkpoint | 290   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | claim      | 289   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | deposit    | 287   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | setWeight  | 292   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | toggle     | 299   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | withdraw   | 293   | 0       | 0        |\n╰---------------+------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 458.09ms (454.31ms CPU time)\n\nRan 7 test suites in 459.75ms (808.79ms CPU time): 70 tests passed, 0 failed, 0 skipped (70 total tests)\n","passed":true},{"durationMs":64,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CheeseEmissionController.acceptOwnership()\",\"CheeseEmissionController.activate()\",\"CheeseEmissionController.addPool(address,uint16)\",\"CheeseEmissionController.batchSetPoolWeights(uint256[],uint16[])\",\"CheeseEmissionController.bindToken(address)\",\"CheeseEmissionController.checkpoint()\",\"CheeseEmissionController.disablePool(uint256)\",\"CheeseEmissionController.enablePool(uint256)\",\"CheeseEmissionController.pauseEmissions()\",\"CheeseEmissionController.pullRewards(uint256,bool)\",\"CheeseEmissionController.renounceOwnership()\",\"CheeseEmissionController.setGuardian(address)\",\"CheeseEmissionController.setPoolWeight(uint256,uint16)\",\"CheeseEmissionController.setTreasury(address)\",\"CheeseEmissionController.transferOwnership(address)\",\"CheeseEmissionController.unpauseEmissions()\",\"CheeseFarmERC20.acceptOwnership()\",\"CheeseFarmERC20.claim(address)\",\"CheeseFarmERC20.deposit(uint256)\",\"CheeseFarmERC20.onPoolRegistered(uint256)\",\"CheeseFarmERC20.renounceOwnership()\",\"CheeseFarmERC20.setDepositsPaused(bool)\",\"CheeseFarmERC20.skimSurplus()\",\"CheeseFarmERC20.transferOwnership(address)\",\"CheeseFarmERC20.withdraw(uint256)\",\"CheeseFarmETH.acceptOwnership()\",\"CheeseFarmETH.claim(address)\",\"CheeseFarmETH.deposit()\",\"CheeseFarmETH.onPoolRegistered(uint256)\",\"CheeseFarmETH.receive()\",\"CheeseFarmETH.renounceOwnership()\",\"CheeseFarmETH.setDepositsPaused(bool)\",\"CheeseFarmETH.skimSurplus()\",\"CheeseFarmETH.transferOwnership(address)\",\"CheeseFarmETH.withdraw(uint256)\",\"CheeseFarmSIMD.acceptOwnership()\",\"CheeseFarmSIMD.claim(address)\",\"CheeseFarmSIMD.deposit(uint256)\",\"CheeseFarmSIMD.onPoolRegistered(uint256)\",\"CheeseFarmSIMD.renounceOwnership()\",\"CheeseFarmSIMD.setAsset(address)\",\"CheeseFarmSIMD.setDepositsPaused(bool)\",\"CheeseFarmSIMD.skimSurplus()\",\"CheeseFarmSIMD.transferOwnership(address)\",\"CheeseFarmSIMD.withdraw(uint256)\",\"CheeseToken.approve(address,uint256)\",\"CheeseToken.burn(uint256)\",\"CheeseToken.mintEmission(address,uint256)\",\"CheeseToken.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"CheeseToken.transfer(address,uint256)\",\"CheeseToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"README.md\":217,\"docs/ARCHITECTURE.md\":71,\"docs/SECURITY.md\":76,\"docs/SQUEEKCHAIN_VERIFICATION.md\":85,\"foundry.toml\":28,\"remappings.txt\":2,\"script/Deploy.s.sol\":110,\"script/VerifySqueekchain.s.sol\":70,\"src/CheeseEmissionController.sol\":515,\"src/CheeseFarm.sol\":308,\"src/CheeseToken.sol\":81,\"src/farms/CheeseFarmERC20.sol\":43,\"src/farms/CheeseFarmETH.sol\":41,\"src/farms/CheeseFarmSIMD.sol\":61,\"src/interfaces/ICheeseEmissionController.sol\":25,\"src/interfaces/ICheeseFarm.sol\":9,\"src/interfaces/ICheeseToken.sol\":13,\"src/interfaces/ISqueekCore.sol\":68,\"src/interfaces/ISqueekMining.sol\":36,\"src/libraries/SqueekIssuance.sol\":70,\"test/Allocation.t.sol\":305,\"test/CheeseBase.t.sol\":142,\"test/Deploy.t.sol\":70,\"test/Emission.t.sol\":310,\"test/Farm.t.sol\":340,\"test/FarmFuzz.t.sol\":80,\"test/Invariants.t.sol\":154,\"test/Token.t.sol\":75,\"test/mocks/Attackers.sol\":52,\"test/mocks/MockERC20.sol\":30,\"test/mocks/MockSqueekCore.sol\":100,\"web/abi/CheeseEmissionController.json\":1400,\"web/abi/CheeseFarmERC20.json\":882,\"web/abi/CheeseFarmETH.json\":883,\"web/abi/CheeseFarmSIMD.json\":913,\"web/abi/CheeseToken.json\":750,\"web/config.sepolia.json\":49},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":3481,"exitCode":0,"name":"slither","output":"[medium/medium] divide-before-multiply at src/libraries/SqueekIssuance.sol:47: SqueekIssuance.issued(uint64) (src/libraries/SqueekIssuance.sol#47-57) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/libraries/SqueekIssuance.sol:47: SqueekIssuance.issued(uint64) (src/libraries/SqueekIssuance.sol#47-57) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/libraries/SqueekIssuance.sol:47: SqueekIssuance.issued(uint64) (src/libraries/SqueekIssuance.sol#47-57) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/CheeseEmissionController.sol:352: CheeseEmissionController.previewPoolEmission(uint256) (src/CheeseEmissionController.sol#352-361) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/libraries/SqueekIssuance.sol:47: SqueekIssuance.issued(uint64) (src/libraries/SqueekIssuance.sol#47-57) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/libraries/SqueekIssuance.sol:21: SqueekIssuance.slitherConstructorConstantVariables() (src/libraries/SqueekIssuance.sol#21-70) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/CheeseFarm.sol:140: CheeseFarm.skimSurplus() (src/CheeseFarm.sol#140-146) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CheeseFarm.sol:153: CheeseFarm._deposit(uint256,uint256) (src/CheeseFarm.sol#153-173) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CheeseEmissionController.sol:464: CheeseEmissionController._sync() (src/CheeseEmissionController.sol#464-514) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CheeseEmissionController.sol:447: CheeseEmissionController._settle(uint256,ICheeseEmissionController.Pool) (src/CheeseEmissionController.sol#447-455) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CheeseEmissionController.sol:464: CheeseEmissionController._sync() (src/CheeseEmissionController.sol#464-514) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CheeseFarm.sol:276: CheeseFarm._accrue() (src/CheeseFarm.sol#276-283) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CheeseEmissionController.sol:447: CheeseEmissionController._settle(uint256,ICheeseEmissionController.Pool) (src/CheeseEmissionController.sol#447-455) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CheeseFarm.sol:225: CheeseFarm.pendingRewards(address) (src/CheeseFarm.sol#225-233) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CheeseFarm.sol:208: CheeseFarm.claim(address) (src/CheeseFarm.sol#208-218) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/CheeseFarm.sol:153: Reentrancy in CheeseFarm._deposit(uint256,uint256) (src/CheeseFarm.sol#153-173):\n[medium/medium] reentrancy-no-eth at src/CheeseFarm.sol:177: Reentrancy in CheeseFarm.withdraw(uint256) (src/CheeseFarm.sol#177-205):\n[medium/medium] reentrancy-no-eth at src/CheeseFarm.sol:208: Reentrancy in CheeseFarm.claim(address) (src/CheeseFarm.sol#208-218):\n[medium/medium] uninitialized-local at src/CheeseEmissionController.sol:479: CheeseEmissionController._sync().h0 (src/CheeseEmissionController.sol#479) is a local variable never initialized\n[medium/medium] uninitialized-local at src/CheeseEmissionController.sol:468: CheeseEmissionController._sync().s (src/CheeseEmissionController.sol#468) is a local variable never initialized\n[medium/medium] uninitialized-local at src/CheeseEmissionController.sol:383: CheeseEmissionController.pendingCheckpoint().s (src/CheeseEmissionController.sol#383) is a local variable never initialized\n[medium/medium] uninitialized-local at src/CheeseEmissionController.sol:183: CheeseEmissionController.activate().config (src/CheeseEmissionController.sol#183) is a local variable never initialized\n[medium/medium] unused-return at src/CheeseEmissionController.sol:352: CheeseEmissionController.previewPoolEmission(uint256) (src/CheeseEmissionController.sol#352-361) ignores return value by (readable,None,unsynced) = this.pendingCheckpoint() (src/CheeseEmissionController.sol#357)\n[low/high] shadowing-local at src/interfaces/ICheeseFarm.sol:6: ICheeseFarm.onPoolRegistered(uint256).poolId (src/interfaces/ICheeseFarm.sol#8) shadows:\n[low/medium] missing-zero-check at src/CheeseEmissionController.sol:143: CheeseEmissionController.constructor(address,address,address).owner_ (src/CheeseEmissionController.sol#143) lacks a zero-check on :\n[low/medium] reentrancy-benign at src/CheeseFarm.sol:276: Reentrancy in CheeseFarm._accrue() (src/CheeseFarm.sol#276-283):\n[low/medium] reentrancy-benign at src/CheeseFarm.sol:208: Reentrancy in CheeseFarm.claim(address) (src/CheeseFarm.sol#208-218):\n[low/medium] reentrancy-benign at src/CheeseFarm.sol:177: Reentrancy in CheeseFarm.withdraw(uint256) (src/CheeseFarm.sol#177-205):\n[low/medium] reentrancy-benign at src/CheeseFarm.sol:153: Reentrancy in CheeseFarm._deposit(uint256,uint256) (src/CheeseFarm.sol#153-173):\n[low/medium] reentrancy-events at src/CheeseFarm.sol:153: Reentrancy in CheeseFarm._deposit(uint256,uint256) (src/CheeseFarm.sol#153-173):\n[low/medium] reentrancy-events at src/CheeseEmissionController.sol:220: Reentrancy in CheeseEmissionController.addPool(address,uint16) (src/CheeseEmissionController.sol#220-241):\n[low/medium] timestamp at src/CheeseEmissionController.sol:447: CheeseEmissionController._settle(uint256,ICheeseEmissionController.Pool) (src/CheeseEmissionController.sol#447-455) uses timestamp for comparisons\n[low/medium] timestamp at src/CheeseEmissionController.sol:419: CheeseEmissionController._currentRound(uint64) (src/CheeseEmissionController.sol#419-422) uses timestamp for comparisons\n[low/medium] timestamp at src/CheeseEmissionController.sol:464: CheeseEmissionController._sync() (src/CheeseEmissionController.sol#464-514) uses timestamp for comparisons\n[low/medium] timestamp at src/CheeseEmissionController.sol:381: CheeseEmissionController.pendingCheckpoint() (src/CheeseEmissionController.sol#381-403) uses timestamp for comparisons\n[low/medium] timestamp at src/CheeseEmissionController.sol:457: CheeseEmissionController._clip(uint256) (src/CheeseEmissionController.sol#457-460) uses timestamp for comparisons","passed":true},{"durationMs":681,"exitCode":0,"name":"aderyn","output":"[high] eth-send-unchecked-address at src/CheeseFarm.sol:177: ETH transferred without address checks\n[high] reentrancy-state-change at src/CheeseEmissionController.sol:160: Reentrancy: State change after external call (3 places)\n[low] centralization-risk at src/CheeseEmissionController.sol:30: Centralization Risk (15 places)\n[low] costly-loop at src/CheeseEmissionController.sol:253: Costly operations inside loop (2 places)\n[low] large-numeric-literal at src/CheeseEmissionController.sol:41: Large Numeric Literal (2 places)\n[low] non-reentrant-not-first at src/CheeseFarm.sol:140: `nonReentrant` is Not the First Modifier\n[low] require-revert-in-loop at src/CheeseEmissionController.sol:253: Loop Contains `require`/`revert` (2 places)\n[low] state-no-address-check at src/CheeseEmissionController.sol:147: Address State Variable Set Without Checks\n[low] unchecked-return at src/CheeseEmissionController.sol:298: Unchecked Return (3 places)\n[low] uninitialized-local-variable at src/CheeseEmissionController.sol:253: Uninitialized Local Variable (2 places)\n[low] unused-error at src/CheeseFarm.sol:74: Unused Error","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"58e33fbc7d4c3166dd705b3e579d83c7434ef91588c1f376ce57a0352e6a4a35","verifiedTreeHash":"0877357879c037f7b7526a62533e7eb9ba6db3ee","verifierVersion":"0.1.0+18543036"},{"checks":[{"durationMs":4258,"exitCode":0,"name":"build","output":"Compiling 72 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.16s\nCompiler run successful!\n","passed":true},{"durationMs":578,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/Deploy.t.sol:DeployTest\n[PASS] test_constructorsNeedNoLiveContracts() (gas: 18159387)\n[PASS] test_wireAndHandOver() (gas: 19702567)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 2.82ms (2.05ms CPU time)\n\nRan 23 tests for test/Emission.t.sol:EmissionTest\n[PASS] testFuzz_issuedMatchesReferenceAndIsMonotone(uint64,uint64) (runs: 256, μ: 6087, ~: 5961)\n[PASS] test_activateRevertsOnClockMismatch() (gas: 80602)\n[PASS] test_activateRevertsTwiceAndForNonOwner() (gas: 131446)\n[PASS] test_activateRevertsWhenCoreUnavailable() (gas: 75677)\n[PASS] test_activateSchedulesNextRound() (gas: 93496)\n[PASS] test_bindTokenOnceAndOnlyMatchingAuthority() (gas: 102940)\n[PASS] test_blocksBeforeActivationNeverEmit() (gas: 2639273)\n[PASS] test_checkpointBeforeActivationIsNoop() (gas: 90579)\n[PASS] test_completionEmitsEventAndMintsAtMostAllowance() (gas: 1429808)\n[PASS] test_coreRegressionIsIgnored() (gas: 690511)\n[PASS] test_coreUnavailableIsTolerated() (gas: 727194)\n[PASS] test_delayedCheckpointEqualsFrequentCheckpoints() (gas: 2408506)\n[PASS] test_fullScheduleCompletesAndStaysCapped() (gas: 730815)\n[PASS] test_guardianCanPauseOnlyOwnerUnpauses() (gas: 171190)\n[PASS] test_halvingCrossingInsideOneCheckpoint() (gas: 752848)\n[PASS] test_issuedMatchesLiveSqueekMiningVectors() (gas: 8033)\n[PASS] test_noDoubleMintingOnRepeatedCheckpoints() (gas: 802328)\n[PASS] test_noEmissionBeforeStartRoundCertified() (gas: 2415656)\n[PASS] test_pauseStopsEmissionAndUnpauseCatchesUp() (gas: 904567)\n[PASS] test_pendingCheckpointPreview() (gas: 2650457)\n[PASS] test_perBlockRatesAcrossHalvings() (gas: 12379)\n[PASS] test_scheduleConstantsMatchSqueekMining() (gas: 1694)\n[PASS] test_zeroBlockMovementEmitsNothing() (gas: 724419)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 34.74ms (55.71ms CPU time)\n\nRan 5 tests for test/Token.t.sol:TokenTest\n[PASS] testFuzz_mintNeverExceedsCap(uint256,uint256) (runs: 256, μ: 140364, ~: 142590)\n[PASS] test_capBoundsMintedTotalNotSupply() (gas: 237213)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 10284)\n[PASS] test_metadataAndPremint() (gas: 96648)\n[PASS] test_onlyEmissionAuthorityMints() (gas: 139970)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 55.34ms (56.76ms CPU time)\n\nRan 21 tests for test/Farm.t.sol:FarmTest\n[PASS] test_blockedTreasuryCannotBeSetByNonOwnerAndOwnerCanFix() (gas: 530175)\n[PASS] test_cheeseFarmKeepsPrincipalAndRewardReservesApart() (gas: 1621658)\n[PASS] test_depositMintsSharesOneToOneInitially() (gas: 170360)\n[PASS] test_depositRejectsZeroAndPausedAndUnconfigured() (gas: 287276)\n[PASS] test_depositsCloseWhileEmissionsPausedWithdrawalsStayOpen() (gas: 787184)\n[PASS] test_directEthTransferRejected() (gas: 33005)\n[PASS] test_donationsDoNotMoveSharePriceAndAreSkimmableOnly() (gas: 694851)\n[PASS] test_emptyFarmEmissionIsForfeitedNotInherited() (gas: 1178200)\n[PASS] test_feeOnTransferAssetCreditsReceivedAmount() (gas: 860345)\n[PASS] test_feeWorksAfterEmissionEnds() (gas: 1334833)\n[PASS] test_hamsterBiteSplit85_14_1() (gas: 421677)\n[PASS] test_inflationAttackIsUneconomic() (gas: 1203589)\n[PASS] test_lastDepositorWithdrawalSendsFull15ToTreasury() (gas: 283364)\n[PASS] test_newDepositorGetsNothingFromEarlierBlocks() (gas: 1237339)\n[PASS] test_noCooldownWithdrawSameBlockAsDeposit() (gas: 252423)\n[PASS] test_onPoolRegisteredOnlyController() (gas: 339141)\n[PASS] test_ownerCannotTouchPrincipalOrRewards() (gas: 1028309)\n[PASS] test_partialWithdrawerBenefitsFromOwnRetainedShare() (gas: 438514)\n[PASS] test_reentrancyBlockedOnWithdrawDepositAndClaim() (gas: 1183112)\n[PASS] test_rewardsAccrueProRataAndClaimIsIndependentOfPrincipal() (gas: 1740173)\n[PASS] test_withdrawRejectsZeroAndTooMany() (gas: 211556)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 64.10ms (16.56ms CPU time)\n\nRan 14 tests for test/Allocation.t.sol:AllocationTest\n[PASS] test_addPoolRejectsFarmOfAnotherController() (gas: 73703)\n[PASS] test_addPoolRejectsForeignOrDuplicateFarms() (gas: 41070)\n[PASS] test_allocationChangesRevertWhilePausedOrCoreUnavailable() (gas: 694910)\n[PASS] test_batchSetPoolWeightsReallocatesAtomically() (gas: 1229299)\n[PASS] test_disableAndEnablePool() (gas: 1392568)\n[PASS] test_emissionSplitsByWeightAndReserveStaysUnminted() (gas: 2246727)\n[PASS] test_gasScalabilityDoesNotGrowWithPoolCount() (gas: 11738473)\n[PASS] test_initialAllocationIs400_200_200_200() (gas: 86203)\n[PASS] test_onlyOwnerAdministers() (gas: 190832)\n[PASS] test_onlyPoolFarmPullsRewards() (gas: 569874)\n[PASS] test_reserveAssignedLaterIsNeverRetroactive() (gas: 975884)\n[PASS] test_setPoolWeightAppliesProspectivelyAfterCheckpoint() (gas: 1000166)\n[PASS] test_treasuryAndGuardianSetters() (gas: 105196)\n[PASS] test_weightsCannotExceedTotalPoints() (gas: 235783)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 73.96ms (16.08ms CPU time)\n\nRan 4 tests for test/FarmFuzz.t.sol:FarmFuzzTest\n[PASS] testFuzz_checkpointGroupingIsIrrelevant(uint16,uint16,uint16) (runs: 256, μ: 2822185, ~: 2823864)\n[PASS] testFuzz_depositWithdrawConservesAssets(uint96,uint96,uint96) (runs: 256, μ: 420305, ~: 425266)\n[PASS] testFuzz_lateDepositorNeverInheritsRewards(uint96,uint96,uint32) (runs: 256, μ: 1300033, ~: 1300295)\n[PASS] testFuzz_rewardsSplitProRata(uint96,uint96,uint32) (runs: 256, μ: 1335099, ~: 1335206)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 73.38ms (200.27ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_ethConservation\n[PASS] invariant_farmBalanceCoversPrincipal\n[PASS] invariant_sharesSumAndBacking\n[PASS] invariant_supplyAccounting\n[PASS] invariant_weightsWithinTotal\n InvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭---------------+------------+-------+---------+----------╮\n| Contract      | Selector   | Calls | Reverts | Discards |\n+=========================================================+\n| CheeseHandler | advance    | 298   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | checkpoint | 290   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | claim      | 289   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | deposit    | 287   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | setWeight  | 292   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | toggle     | 299   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | withdraw   | 293   | 0       | 0        |\n╰---------------+------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 432.19ms (421.42ms CPU time)\n\nRan 7 test suites in 434.63ms (736.53ms CPU time): 70 tests passed, 0 failed, 0 skipped (70 total tests)\n","passed":true},{"durationMs":49,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CheeseEmissionController.acceptOwnership()\",\"CheeseEmissionController.activate()\",\"CheeseEmissionController.addPool(address,uint16)\",\"CheeseEmissionController.batchSetPoolWeights(uint256[],uint16[])\",\"CheeseEmissionController.bindToken(address)\",\"CheeseEmissionController.checkpoint()\",\"CheeseEmissionController.disablePool(uint256)\",\"CheeseEmissionController.enablePool(uint256)\",\"CheeseEmissionController.pauseEmissions()\",\"CheeseEmissionController.pullRewards(uint256,bool)\",\"CheeseEmissionController.renounceOwnership()\",\"CheeseEmissionController.setGuardian(address)\",\"CheeseEmissionController.setPoolWeight(uint256,uint16)\",\"CheeseEmissionController.setTreasury(address)\",\"CheeseEmissionController.transferOwnership(address)\",\"CheeseEmissionController.unpauseEmissions()\",\"CheeseFarmERC20.acceptOwnership()\",\"CheeseFarmERC20.claim(address)\",\"CheeseFarmERC20.deposit(uint256)\",\"CheeseFarmERC20.onPoolRegistered(uint256)\",\"CheeseFarmERC20.renounceOwnership()\",\"CheeseFarmERC20.setDepositsPaused(bool)\",\"CheeseFarmERC20.skimSurplus()\",\"CheeseFarmERC20.transferOwnership(address)\",\"CheeseFarmERC20.withdraw(uint256)\",\"CheeseFarmETH.acceptOwnership()\",\"CheeseFarmETH.claim(address)\",\"CheeseFarmETH.deposit()\",\"CheeseFarmETH.onPoolRegistered(uint256)\",\"CheeseFarmETH.receive()\",\"CheeseFarmETH.renounceOwnership()\",\"CheeseFarmETH.setDepositsPaused(bool)\",\"CheeseFarmETH.skimSurplus()\",\"CheeseFarmETH.transferOwnership(address)\",\"CheeseFarmETH.withdraw(uint256)\",\"CheeseFarmSIMD.acceptOwnership()\",\"CheeseFarmSIMD.claim(address)\",\"CheeseFarmSIMD.deposit(uint256)\",\"CheeseFarmSIMD.onPoolRegistered(uint256)\",\"CheeseFarmSIMD.renounceOwnership()\",\"CheeseFarmSIMD.setAsset(address)\",\"CheeseFarmSIMD.setDepositsPaused(bool)\",\"CheeseFarmSIMD.skimSurplus()\",\"CheeseFarmSIMD.transferOwnership(address)\",\"CheeseFarmSIMD.withdraw(uint256)\",\"CheeseToken.approve(address,uint256)\",\"CheeseToken.burn(uint256)\",\"CheeseToken.mintEmission(address,uint256)\",\"CheeseToken.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"CheeseToken.transfer(address,uint256)\",\"CheeseToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"README.md\":217,\"docs/ARCHITECTURE.md\":71,\"docs/SECURITY.md\":76,\"docs/SQUEEKCHAIN_VERIFICATION.md\":85,\"foundry.toml\":28,\"launch.json\":45,\"remappings.txt\":2,\"script/Deploy.s.sol\":110,\"script/VerifySqueekchain.s.sol\":70,\"src/CheeseEmissionController.sol\":515,\"src/CheeseFarm.sol\":308,\"src/CheeseToken.sol\":81,\"src/farms/CheeseFarmERC20.sol\":43,\"src/farms/CheeseFarmETH.sol\":41,\"src/farms/CheeseFarmSIMD.sol\":61,\"src/interfaces/ICheeseEmissionController.sol\":25,\"src/interfaces/ICheeseFarm.sol\":9,\"src/interfaces/ICheeseToken.sol\":13,\"src/interfaces/ISqueekCore.sol\":68,\"src/interfaces/ISqueekMining.sol\":36,\"src/libraries/SqueekIssuance.sol\":70,\"test/Allocation.t.sol\":305,\"test/CheeseBase.t.sol\":142,\"test/Deploy.t.sol\":70,\"test/Emission.t.sol\":310,\"test/Farm.t.sol\":340,\"test/FarmFuzz.t.sol\":80,\"test/Invariants.t.sol\":154,\"test/Token.t.sol\":75,\"test/mocks/Attackers.sol\":52,\"test/mocks/MockERC20.sol\":30,\"test/mocks/MockSqueekCore.sol\":100,\"web/abi/CheeseEmissionController.json\":1400,\"web/abi/CheeseFarmERC20.json\":882,\"web/abi/CheeseFarmETH.json\":883,\"web/abi/CheeseFarmSIMD.json\":913,\"web/abi/CheeseToken.json\":750,\"web/config.sepolia.json\":49},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"6761efc4875b38d87e66f31e76773c689ec194d87983ba9781a35223c6e20a02","verifiedTreeHash":"448b24fa4d80ed18d4decc17764bfba3a158fb1d","verifierVersion":"0.1.0+18543036"},{"checks":[{"durationMs":5036,"exitCode":0,"name":"build","output":"Compiling 72 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.89s\nCompiler run successful!\n","passed":true},{"durationMs":930,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 5 tests for test/Token.t.sol:TokenTest\n[PASS] testFuzz_mintNeverExceedsCap(uint256,uint256) (runs: 256, μ: 140380, ~: 142590)\n[PASS] test_capBoundsMintedTotalNotSupply() (gas: 237213)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 10284)\n[PASS] test_metadataAndPremint() (gas: 96648)\n[PASS] test_onlyEmissionAuthorityMints() (gas: 139970)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 10.44ms (10.16ms CPU time)\n\nRan 29 tests for test/Emission.t.sol:EmissionTest\n[PASS] testFuzz_issuedMatchesReferenceAndIsMonotone(uint64,uint64) (runs: 256, μ: 6109, ~: 5953)\n[PASS] test_activateRevertsOnClockMismatch() (gas: 84662)\n[PASS] test_activateRevertsTwiceAndForNonOwner() (gas: 135465)\n[PASS] test_activateRevertsWhenCoreUnavailable() (gas: 76071)\n[PASS] test_activateSchedulesNextRound() (gas: 97471)\n[PASS] test_activateTreatsUndecodableConfigurationAsUnavailable() (gas: 118448)\n[PASS] test_bindTokenOnceAndOnlyMatchingAuthority() (gas: 103028)\n[PASS] test_blocksBeforeActivationNeverEmit() (gas: 2653636)\n[PASS] test_checkpointBeforeActivationIsNoop() (gas: 90666)\n[PASS] test_completionEmitsEventAndMintsAtMostAllowance() (gas: 1450964)\n[PASS] test_coreRegressionIsIgnored() (gas: 708125)\n[PASS] test_coreUnavailableIsTolerated() (gas: 742249)\n[PASS] test_coreWithoutCodeIsUnavailableNotARevert() (gas: 972360)\n[PASS] test_delayedCheckpointEqualsFrequentCheckpoints() (gas: 2455825)\n[PASS] test_fullScheduleCompletesAndStaysCapped() (gas: 745215)\n[PASS] test_guardianCanPauseOnlyOwnerUnpauses() (gas: 171298)\n[PASS] test_halvingCrossingInsideOneCheckpoint() (gas: 770494)\n[PASS] test_issuedMatchesLiveSqueekMiningVectors() (gas: 8033)\n[PASS] test_noDoubleMintingOnRepeatedCheckpoints() (gas: 833204)\n[PASS] test_noEmissionBeforeStartRoundCertified() (gas: 2422916)\n[PASS] test_pausePricesBlocksCertifiedBeforeItForThoseStakedThen() (gas: 1524001)\n[PASS] test_pauseStillWorksWhenCoreIsUnavailable() (gas: 790610)\n[PASS] test_pauseStopsEmissionAndUnpauseCatchesUp() (gas: 933880)\n[PASS] test_pendingCheckpointPreview() (gas: 2668040)\n[PASS] test_perBlockRatesAcrossHalvings() (gas: 12379)\n[PASS] test_scheduleConstantsMatchSqueekMining() (gas: 1716)\n[PASS] test_undecodableCoreStateIsUnavailableNotARevert() (gas: 1584305)\n[PASS] test_undecodableRoundPageDelaysAnchorWithoutRevert() (gas: 2533549)\n[PASS] test_zeroBlockMovementEmitsNothing() (gas: 742045)\nSuite result: ok. 29 passed; 0 failed; 0 skipped; finished in 17.32ms (22.41ms CPU time)\n\nRan 31 tests for test/Farm.t.sol:FarmTest\n[PASS] test_cheeseFarmDeferredTreasuryShareIsReservedAgainstSkim() (gas: 1785292)\n[PASS] test_cheeseFarmKeepsPrincipalAndRewardReservesApart() (gas: 1657902)\n[PASS] test_claimNeverRevertsOnReserveShortfall() (gas: 1631947)\n[PASS] test_deferredTreasuryShareIsPaidOnceTheTreasuryCanReceive() (gas: 842038)\n[PASS] test_depositMintsSharesOneToOneInitially() (gas: 171620)\n[PASS] test_depositRejectsZeroAndPausedAndUnconfigured() (gas: 287141)\n[PASS] test_depositWaitsForSyncWhileWithdrawAndClaimDoNot() (gas: 1612964)\n[PASS] test_depositsCloseWhileEmissionsPausedWithdrawalsStayOpen() (gas: 816221)\n[PASS] test_directEthTransferRejected() (gas: 32983)\n[PASS] test_donationsDoNotMoveSharePriceAndAreSkimmableOnly() (gas: 704251)\n[PASS] test_emptyFarmEmissionIsForfeitedNotInherited() (gas: 1200789)\n[PASS] test_emptyFarmSweepsResidualRetainedFeeToTreasury() (gas: 662630)\n[PASS] test_erc20FarmDefersTreasuryShareTheTokenRefusesToDeliver() (gas: 1267605)\n[PASS] test_everyAccountCanClaimAfterEmissionEnds() (gas: 4750886)\n[PASS] test_feeOnTransferAssetCreditsReceivedAmount() (gas: 861595)\n[PASS] test_feeWorksAfterEmissionEnds() (gas: 1357617)\n[PASS] test_firstDepositDuringOutageCannotInheritForfeitableEmission() (gas: 978297)\n[PASS] test_gasBurningOrReceiverlessTreasuryIsDeferredToo() (gas: 904656)\n[PASS] test_hamsterBiteSplit85_14_1() (gas: 425636)\n[PASS] test_inflationAttackIsUneconomic() (gas: 1219626)\n[PASS] test_lastDepositorWithdrawalSendsFull15ToTreasury() (gas: 291417)\n[PASS] test_newDepositorGetsNothingFromEarlierBlocks() (gas: 1267865)\n[PASS] test_noCooldownWithdrawSameBlockAsDeposit() (gas: 255325)\n[PASS] test_onPoolRegisteredOnlyController() (gas: 341789)\n[PASS] test_oneWeiFirstDepositorCannotBrickLaterDeposits() (gas: 4761772)\n[PASS] test_ownerCannotTouchPrincipalOrRewards() (gas: 1050252)\n[PASS] test_partialWithdrawerBenefitsFromOwnRetainedShare() (gas: 441669)\n[PASS] test_reentrancyBlockedOnWithdrawDepositAndClaim() (gas: 1190133)\n[PASS] test_revertingTreasuryNeverBlocksWithdrawalEvenWithoutAnOwner() (gas: 765968)\n[PASS] test_rewardsAccrueProRataAndClaimIsIndependentOfPrincipal() (gas: 1790219)\n[PASS] test_withdrawRejectsZeroAndTooMany() (gas: 212948)\nSuite result: ok. 31 passed; 0 failed; 0 skipped; finished in 18.99ms (34.93ms CPU time)\n\nRan 2 tests for test/Deploy.t.sol:DeployTest\n[PASS] test_constructorsNeedNoLiveContracts() (gas: 20298825)\n[PASS] test_wireAndHandOver() (gas: 21888965)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 54.47ms (4.54ms CPU time)\n\nRan 15 tests for test/Allocation.t.sol:AllocationTest\n[PASS] test_addPoolRejectsFarmOfAnotherController() (gas: 73769)\n[PASS] test_addPoolRejectsForeignOrDuplicateFarms() (gas: 41070)\n[PASS] test_allocationChangesRevertWhilePausedOrCoreUnavailable() (gas: 795106)\n[PASS] test_batchRejectsDuplicatePoolIds() (gas: 52423)\n[PASS] test_batchSetPoolWeightsReallocatesAtomically() (gas: 1253410)\n[PASS] test_disableAndEnablePool() (gas: 1421082)\n[PASS] test_emissionSplitsByWeightAndReserveStaysUnminted() (gas: 2284774)\n[PASS] test_gasScalabilityDoesNotGrowWithPoolCount() (gas: 11965926)\n[PASS] test_initialAllocationIs400_200_200_200() (gas: 86004)\n[PASS] test_onlyOwnerAdministers() (gas: 190854)\n[PASS] test_onlyPoolFarmPullsRewards() (gas: 581058)\n[PASS] test_reserveAssignedLaterIsNeverRetroactive() (gas: 996640)\n[PASS] test_setPoolWeightAppliesProspectivelyAfterCheckpoint() (gas: 1022167)\n[PASS] test_treasuryAndGuardianSetters() (gas: 105151)\n[PASS] test_weightsCannotExceedTotalPoints() (gas: 235936)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 63.63ms (24.09ms CPU time)\n\nRan 4 tests for test/FarmFuzz.t.sol:FarmFuzzTest\n[PASS] testFuzz_checkpointGroupingIsIrrelevant(uint16,uint16,uint16) (runs: 256, μ: 2839849, ~: 2841538)\n[PASS] testFuzz_depositWithdrawConservesAssets(uint96,uint96,uint96) (runs: 256, μ: 423986, ~: 429335)\n[PASS] testFuzz_lateDepositorNeverInheritsRewards(uint96,uint96,uint32) (runs: 256, μ: 1334007, ~: 1334304)\n[PASS] testFuzz_rewardsSplitProRata(uint96,uint96,uint32) (runs: 256, μ: 1363883, ~: 1364021)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 73.24ms (209.63ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_entitlementsNeverExceedReserve\n[PASS] invariant_ethConservation\n[PASS] invariant_farmBalanceCoversPrincipal\n[PASS] invariant_sharesSumAndBacking\n[PASS] invariant_supplyAccounting\n[PASS] invariant_userActionsNeverRevertUnexpectedly\n[PASS] invariant_weightsWithinTotal\n InvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭---------------+------------+-------+---------+----------╮\n| Contract      | Selector   | Calls | Reverts | Discards |\n+=========================================================+\n| CheeseHandler | advance    | 298   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | checkpoint | 290   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | claim      | 289   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | deposit    | 287   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | setWeight  | 292   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | toggle     | 299   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | withdraw   | 293   | 0       | 0        |\n╰---------------+------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 782.48ms (776.51ms CPU time)\n\nRan 7 test suites in 785.95ms (1.02s CPU time): 87 tests passed, 0 failed, 0 skipped (87 total tests)\n","passed":true},{"durationMs":65,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CheeseEmissionController.acceptOwnership()\",\"CheeseEmissionController.activate()\",\"CheeseEmissionController.addPool(address,uint16)\",\"CheeseEmissionController.batchSetPoolWeights(uint256[],uint16[])\",\"CheeseEmissionController.bindToken(address)\",\"CheeseEmissionController.checkpoint()\",\"CheeseEmissionController.disablePool(uint256)\",\"CheeseEmissionController.enablePool(uint256)\",\"CheeseEmissionController.pauseEmissions()\",\"CheeseEmissionController.pullRewards(uint256,bool)\",\"CheeseEmissionController.renounceOwnership()\",\"CheeseEmissionController.setGuardian(address)\",\"CheeseEmissionController.setPoolWeight(uint256,uint16)\",\"CheeseEmissionController.setTreasury(address)\",\"CheeseEmissionController.transferOwnership(address)\",\"CheeseEmissionController.unpauseEmissions()\",\"CheeseFarmERC20.acceptOwnership()\",\"CheeseFarmERC20.claim(address)\",\"CheeseFarmERC20.deposit(uint256)\",\"CheeseFarmERC20.onPoolRegistered(uint256)\",\"CheeseFarmERC20.payTreasury()\",\"CheeseFarmERC20.renounceOwnership()\",\"CheeseFarmERC20.setDepositsPaused(bool)\",\"CheeseFarmERC20.skimSurplus()\",\"CheeseFarmERC20.transferOwnership(address)\",\"CheeseFarmERC20.withdraw(uint256)\",\"CheeseFarmETH.acceptOwnership()\",\"CheeseFarmETH.claim(address)\",\"CheeseFarmETH.deposit()\",\"CheeseFarmETH.onPoolRegistered(uint256)\",\"CheeseFarmETH.payTreasury()\",\"CheeseFarmETH.receive()\",\"CheeseFarmETH.renounceOwnership()\",\"CheeseFarmETH.setDepositsPaused(bool)\",\"CheeseFarmETH.skimSurplus()\",\"CheeseFarmETH.transferOwnership(address)\",\"CheeseFarmETH.withdraw(uint256)\",\"CheeseFarmSIMD.acceptOwnership()\",\"CheeseFarmSIMD.claim(address)\",\"CheeseFarmSIMD.deposit(uint256)\",\"CheeseFarmSIMD.onPoolRegistered(uint256)\",\"CheeseFarmSIMD.payTreasury()\",\"CheeseFarmSIMD.renounceOwnership()\",\"CheeseFarmSIMD.setAsset(address)\",\"CheeseFarmSIMD.setDepositsPaused(bool)\",\"CheeseFarmSIMD.skimSurplus()\",\"CheeseFarmSIMD.transferOwnership(address)\",\"CheeseFarmSIMD.withdraw(uint256)\",\"CheeseToken.approve(address,uint256)\",\"CheeseToken.burn(uint256)\",\"CheeseToken.mintEmission(address,uint256)\",\"CheeseToken.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"CheeseToken.transfer(address,uint256)\",\"CheeseToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"README.md\":242,\"docs/ARCHITECTURE.md\":83,\"docs/SECURITY.md\":104,\"docs/SQUEEKCHAIN_VERIFICATION.md\":85,\"foundry.toml\":28,\"remappings.txt\":2,\"script/Deploy.s.sol\":112,\"script/VerifySqueekchain.s.sol\":70,\"src/CheeseEmissionController.sol\":571,\"src/CheeseFarm.sol\":375,\"src/CheeseToken.sol\":81,\"src/farms/CheeseFarmERC20.sol\":47,\"src/farms/CheeseFarmETH.sol\":46,\"src/farms/CheeseFarmSIMD.sol\":66,\"src/interfaces/ICheeseEmissionController.sol\":25,\"src/interfaces/ICheeseFarm.sol\":9,\"src/interfaces/ICheeseToken.sol\":13,\"src/interfaces/ISqueekCore.sol\":68,\"src/interfaces/ISqueekMining.sol\":36,\"src/libraries/SqueekIssuance.sol\":70,\"test/Allocation.t.sol\":319,\"test/CheeseBase.t.sol\":142,\"test/Deploy.t.sol\":76,\"test/Emission.t.sol\":427,\"test/Farm.t.sol\":602,\"test/FarmFuzz.t.sol\":80,\"test/Invariants.t.sol\":188,\"test/Token.t.sol\":75,\"test/mocks/Attackers.sol\":71,\"test/mocks/MockERC20.sol\":51,\"test/mocks/MockSqueekCore.sol\":100,\"web/abi/CheeseEmissionController.json\":1655,\"web/abi/CheeseFarmERC20.json\":969,\"web/abi/CheeseFarmETH.json\":970,\"web/abi/CheeseFarmSIMD.json\":1000,\"web/abi/CheeseToken.json\":750,\"web/config.sepolia.json\":49},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":3824,"exitCode":0,"name":"slither","output":"[medium/medium] divide-before-multiply at src/libraries/SqueekIssuance.sol:47: SqueekIssuance.issued(uint64) (src/libraries/SqueekIssuance.sol#47-57) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/libraries/SqueekIssuance.sol:47: SqueekIssuance.issued(uint64) (src/libraries/SqueekIssuance.sol#47-57) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/libraries/SqueekIssuance.sol:47: SqueekIssuance.issued(uint64) (src/libraries/SqueekIssuance.sol#47-57) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/CheeseEmissionController.sol:362: CheeseEmissionController.previewPoolEmission(uint256) (src/CheeseEmissionController.sol#362-371) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/libraries/SqueekIssuance.sol:47: SqueekIssuance.issued(uint64) (src/libraries/SqueekIssuance.sol#47-57) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/libraries/SqueekIssuance.sol:21: SqueekIssuance.slitherConstructorConstantVariables() (src/libraries/SqueekIssuance.sol#21-70) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/CheeseFarm.sol:155: CheeseFarm.skimSurplus() (src/CheeseFarm.sol#155-161) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CheeseFarm.sol:354: CheeseFarm._tryTransferERC20(IERC20,address,uint256) (src/CheeseFarm.sol#354-359) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CheeseFarm.sol:168: CheeseFarm._deposit(uint256,uint256) (src/CheeseFarm.sol#168-191) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CheeseFarm.sol:314: CheeseFarm._accrue() (src/CheeseFarm.sol#314-322) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CheeseFarm.sol:227: CheeseFarm.claim(address) (src/CheeseFarm.sol#227-241) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/CheeseFarm.sol:168: Reentrancy in CheeseFarm._deposit(uint256,uint256) (src/CheeseFarm.sol#168-191):\n[medium/medium] reentrancy-no-eth at src/CheeseFarm.sol:227: Reentrancy in CheeseFarm.claim(address) (src/CheeseFarm.sol#227-241):\n[medium/medium] reentrancy-no-eth at src/CheeseFarm.sol:195: Reentrancy in CheeseFarm.withdraw(uint256) (src/CheeseFarm.sol#195-224):\n[medium/medium] unused-return at src/CheeseEmissionController.sol:362: CheeseEmissionController.previewPoolEmission(uint256) (src/CheeseEmissionController.sol#362-371) ignores return value by (readable,None,unsynced) = this.pendingCheckpoint() (src/CheeseEmissionController.sol#367)\n[low/high] shadowing-local at src/interfaces/ICheeseFarm.sol:6: ICheeseFarm.onPoolRegistered(uint256).poolId (src/interfaces/ICheeseFarm.sol#8) shadows:\n[low/medium] missing-zero-check at src/CheeseEmissionController.sol:147: CheeseEmissionController.constructor(address,address,address).owner_ (src/CheeseEmissionController.sol#147) lacks a zero-check on :\n[low/medium] reentrancy-benign at src/CheeseFarm.sol:168: Reentrancy in CheeseFarm._deposit(uint256,uint256) (src/CheeseFarm.sol#168-191):\n[low/medium] reentrancy-benign at src/CheeseFarm.sol:195: Reentrancy in CheeseFarm.withdraw(uint256) (src/CheeseFarm.sol#195-224):\n[low/medium] reentrancy-benign at src/CheeseFarm.sol:314: Reentrancy in CheeseFarm._accrue() (src/CheeseFarm.sol#314-322):\n[low/medium] reentrancy-benign at src/CheeseFarm.sol:227: Reentrancy in CheeseFarm.claim(address) (src/CheeseFarm.sol#227-241):\n[low/medium] reentrancy-events at src/CheeseFarm.sol:168: Reentrancy in CheeseFarm._deposit(uint256,uint256) (src/CheeseFarm.sol#168-191):\n[low/medium] reentrancy-events at src/CheeseEmissionController.sol:222: Reentrancy in CheeseEmissionController.addPool(address,uint16) (src/CheeseEmissionController.sol#222-243):\n[low/medium] timestamp at src/CheeseEmissionController.sol:440: CheeseEmissionController._currentRound(uint64) (src/CheeseEmissionController.sol#440-443) uses timestamp for comparisons","passed":true},{"durationMs":574,"exitCode":0,"name":"aderyn","output":"[high] eth-send-unchecked-address at src/CheeseFarm.sol:195: ETH transferred without address checks (2 places)\n[high] reentrancy-state-change at src/CheeseEmissionController.sol:164: Reentrancy: State change after external call (2 places)\n[low] centralization-risk at src/CheeseEmissionController.sol:30: Centralization Risk (15 places)\n[low] costly-loop at src/CheeseEmissionController.sol:261: Costly operations inside loop (2 places)\n[low] large-numeric-literal at src/CheeseEmissionController.sol:41: Large Numeric Literal (3 places)\n[low] non-reentrant-not-first at src/CheeseFarm.sol:155: `nonReentrant` is Not the First Modifier\n[low] require-revert-in-loop at src/CheeseEmissionController.sol:254: Loop Contains `require`/`revert` (4 places)\n[low] state-no-address-check at src/CheeseEmissionController.sol:151: Address State Variable Set Without Checks\n[low] unchecked-return at src/CheeseEmissionController.sol:206: Unchecked Return (5 places)\n[low] uninitialized-local-variable at src/CheeseEmissionController.sol:254: Uninitialized Local Variable (3 places)\n[low] unsafe-erc20-operation at src/CheeseFarm.sol:355: Unsafe ERC20 Operation\n[low] unused-error at src/CheeseFarm.sol:85: Unused Error","passed":true},{"durationMs":2316,"exitCode":0,"name":"proof b2a7f3dd716e","output":"Compiling 62 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.64s\nCompiler run successful!\n\nRan 2 tests for test/imd-proof-9154fef9/Proof_b2a7f3dd716e.t.sol:AccOverflowTest\n[PASS] test_oneWeiFirstDepositorBricksDepositsAfterOneRound() (gas: 592191)\n[PASS] test_thresholdDropsWithEveryRound() (gas: 3527833)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 2.73ms (1.86ms CPU time)\n\nRan 1 test suite in 4.31ms (2.73ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)\n","passed":true},{"durationMs":1706,"exitCode":0,"name":"proof c6e8bcfe7c50","output":"2026-11-07T07:05:34.208095Z ERROR foundry_compilers_artifacts_solc::sources: error=\"/tmp/imd-verify-TuKeLS/repo/test/imd-proof-9154fef9/Proof_b2a7f3dd716e.t.sol\": No such file or directory (os error 2)\nCompiling 1 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.09s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-55ebe3b8/Proof_c6e8bcfe7c50.t.sol:RewardOverdraftTest\n[PASS] test_everyAccountCanClaimAfterEmissionEnds() (gas: 4303762)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.42ms (1.62ms CPU time)\n\nRan 1 test suite in 4.18ms (3.42ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true},{"durationMs":1516,"exitCode":0,"name":"proof 60de84fac23e","output":"2026-11-07T07:05:35.905715Z ERROR foundry_compilers_artifacts_solc::sources: error=\"/tmp/imd-verify-TuKeLS/repo/test/imd-proof-55ebe3b8/Proof_c6e8bcfe7c50.t.sol\": No such file or directory (os error 2)\nCompiling 1 files with Solc 0.8.26\nSolc 0.8.26 finished in 944.19ms\nCompiler run successful!\n\nRan 1 test for test/imd-proof-cdeb299b/Proof_60de84fac23e.t.sol:TreasuryFreezeTest\n[PASS] test_ownerCannotFreezePrincipalThroughTreasury() (gas: 389198)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 915.06µs (194.57µs CPU time)\n\nRan 1 test suite in 3.63ms (915.06µs CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true},{"durationMs":1770,"exitCode":0,"name":"proof d0222d548977","output":"2026-11-07T07:05:37.522607Z ERROR foundry_compilers_artifacts_solc::sources: error=\"/tmp/imd-verify-TuKeLS/repo/test/imd-proof-cdeb299b/Proof_60de84fac23e.t.sol\": No such file or directory (os error 2)\nCompiling 1 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.10s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-c39913a6/Proof_d0222d548977.t.sol:OutageDepositTest\n[PASS] test_outageDeposit() (gas: 761156)\nLogs:\n  alice: 734914140002202624511650\n  bob: 0\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.46ms (258.79µs CPU time)\n\nRan 1 test suite in 4.64ms (1.46ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"b29b2150bffba095b6456e83862ea93076cf8c5f60cf02fc3e64df6f2bc6efc7","verifiedTreeHash":"cae0958e103420e28c4baaf89ea7b04c63750ca4","verifierVersion":"0.1.0+18543036"},{"checks":[{"durationMs":5949,"exitCode":0,"name":"build","output":"Compiling 74 files with Solc 0.8.26\nSolc 0.8.26 finished in 5.86s\nCompiler run successful!\n","passed":true},{"durationMs":2390,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 5 tests for test/Token.t.sol:TokenTest\n[PASS] testFuzz_mintNeverExceedsCap(uint256,uint256) (runs: 256, μ: 140405, ~: 142602)\n[PASS] test_capBoundsMintedTotalNotSupply() (gas: 237213)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 10284)\n[PASS] test_metadataAndPremint() (gas: 96648)\n[PASS] test_onlyEmissionAuthorityMints() (gas: 139970)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 67.43ms (66.28ms CPU time)\n\nRan 4 tests for test/FarmFuzz.t.sol:FarmFuzzTest\n[PASS] testFuzz_checkpointGroupingIsIrrelevant(uint16,uint16,uint16) (runs: 256, μ: 2839863, ~: 2841538)\n[PASS] testFuzz_depositWithdrawConservesAssets(uint96,uint96,uint96) (runs: 256, μ: 423986, ~: 429335)\n[PASS] testFuzz_lateDepositorNeverInheritsRewards(uint96,uint96,uint32) (runs: 256, μ: 1334016, ~: 1334304)\n[PASS] testFuzz_rewardsSplitProRata(uint96,uint96,uint32) (runs: 256, μ: 1363885, ~: 1364021)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 67.44ms (205.82ms CPU time)\n\nRan 29 tests for test/Emission.t.sol:EmissionTest\n[PASS] testFuzz_issuedMatchesReferenceAndIsMonotone(uint64,uint64) (runs: 256, μ: 6136, ~: 6059)\n[PASS] test_activateRevertsOnClockMismatch() (gas: 84662)\n[PASS] test_activateRevertsTwiceAndForNonOwner() (gas: 135465)\n[PASS] test_activateRevertsWhenCoreUnavailable() (gas: 76071)\n[PASS] test_activateSchedulesNextRound() (gas: 97471)\n[PASS] test_activateTreatsUndecodableConfigurationAsUnavailable() (gas: 118448)\n[PASS] test_bindTokenOnceAndOnlyMatchingAuthority() (gas: 103028)\n[PASS] test_blocksBeforeActivationNeverEmit() (gas: 2653636)\n[PASS] test_checkpointBeforeActivationIsNoop() (gas: 90666)\n[PASS] test_completionEmitsEventAndMintsAtMostAllowance() (gas: 1450964)\n[PASS] test_coreRegressionIsIgnored() (gas: 708125)\n[PASS] test_coreUnavailableIsTolerated() (gas: 742249)\n[PASS] test_coreWithoutCodeIsUnavailableNotARevert() (gas: 972360)\n[PASS] test_delayedCheckpointEqualsFrequentCheckpoints() (gas: 2455825)\n[PASS] test_fullScheduleCompletesAndStaysCapped() (gas: 745215)\n[PASS] test_guardianCanPauseOnlyOwnerUnpauses() (gas: 171298)\n[PASS] test_halvingCrossingInsideOneCheckpoint() (gas: 770494)\n[PASS] test_issuedMatchesLiveSqueekMiningVectors() (gas: 8033)\n[PASS] test_noDoubleMintingOnRepeatedCheckpoints() (gas: 833204)\n[PASS] test_noEmissionBeforeStartRoundCertified() (gas: 2422916)\n[PASS] test_pausePricesBlocksCertifiedBeforeItForThoseStakedThen() (gas: 1524001)\n[PASS] test_pauseStillWorksWhenCoreIsUnavailable() (gas: 790610)\n[PASS] test_pauseStopsEmissionAndUnpauseCatchesUp() (gas: 933880)\n[PASS] test_pendingCheckpointPreview() (gas: 2668040)\n[PASS] test_perBlockRatesAcrossHalvings() (gas: 12379)\n[PASS] test_scheduleConstantsMatchSqueekMining() (gas: 1716)\n[PASS] test_undecodableCoreStateIsUnavailableNotARevert() (gas: 1584305)\n[PASS] test_undecodableRoundPageDelaysAnchorWithoutRevert() (gas: 2533549)\n[PASS] test_zeroBlockMovementEmitsNothing() (gas: 742045)\nSuite result: ok. 29 passed; 0 failed; 0 skipped; finished in 79.17ms (24.15ms CPU time)\n\nRan 2 tests for test/Deploy.t.sol:DeployTest\n[PASS] test_constructorsNeedNoLiveContracts() (gas: 20298825)\n[PASS] test_wireAndHandOver() (gas: 21888965)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 93.46ms (2.05ms CPU time)\n\nRan 31 tests for test/Farm.t.sol:FarmTest\n[PASS] test_cheeseFarmDeferredTreasuryShareIsReservedAgainstSkim() (gas: 1785292)\n[PASS] test_cheeseFarmKeepsPrincipalAndRewardReservesApart() (gas: 1657902)\n[PASS] test_claimNeverRevertsOnReserveShortfall() (gas: 1631947)\n[PASS] test_deferredTreasuryShareIsPaidOnceTheTreasuryCanReceive() (gas: 842038)\n[PASS] test_depositMintsSharesOneToOneInitially() (gas: 171620)\n[PASS] test_depositRejectsZeroAndPausedAndUnconfigured() (gas: 287141)\n[PASS] test_depositWaitsForSyncWhileWithdrawAndClaimDoNot() (gas: 1612964)\n[PASS] test_depositsCloseWhileEmissionsPausedWithdrawalsStayOpen() (gas: 816221)\n[PASS] test_directEthTransferRejected() (gas: 32983)\n[PASS] test_donationsDoNotMoveSharePriceAndAreSkimmableOnly() (gas: 704251)\n[PASS] test_emptyFarmEmissionIsForfeitedNotInherited() (gas: 1200789)\n[PASS] test_emptyFarmSweepsResidualRetainedFeeToTreasury() (gas: 662630)\n[PASS] test_erc20FarmDefersTreasuryShareTheTokenRefusesToDeliver() (gas: 1267605)\n[PASS] test_everyAccountCanClaimAfterEmissionEnds() (gas: 4750886)\n[PASS] test_feeOnTransferAssetCreditsReceivedAmount() (gas: 861595)\n[PASS] test_feeWorksAfterEmissionEnds() (gas: 1357617)\n[PASS] test_firstDepositDuringOutageCannotInheritForfeitableEmission() (gas: 978297)\n[PASS] test_gasBurningOrReceiverlessTreasuryIsDeferredToo() (gas: 904656)\n[PASS] test_hamsterBiteSplit85_14_1() (gas: 425636)\n[PASS] test_inflationAttackIsUneconomic() (gas: 1219626)\n[PASS] test_lastDepositorWithdrawalSendsFull15ToTreasury() (gas: 291417)\n[PASS] test_newDepositorGetsNothingFromEarlierBlocks() (gas: 1267865)\n[PASS] test_noCooldownWithdrawSameBlockAsDeposit() (gas: 255325)\n[PASS] test_onPoolRegisteredOnlyController() (gas: 341789)\n[PASS] test_oneWeiFirstDepositorCannotBrickLaterDeposits() (gas: 4761772)\n[PASS] test_ownerCannotTouchPrincipalOrRewards() (gas: 1050252)\n[PASS] test_partialWithdrawerBenefitsFromOwnRetainedShare() (gas: 441669)\n[PASS] test_reentrancyBlockedOnWithdrawDepositAndClaim() (gas: 1190133)\n[PASS] test_revertingTreasuryNeverBlocksWithdrawalEvenWithoutAnOwner() (gas: 765968)\n[PASS] test_rewardsAccrueProRataAndClaimIsIndependentOfPrincipal() (gas: 1790219)\n[PASS] test_withdrawRejectsZeroAndTooMany() (gas: 212948)\nSuite result: ok. 31 passed; 0 failed; 0 skipped; finished in 93.71ms (24.77ms CPU time)\n\nRan 15 tests for test/Allocation.t.sol:AllocationTest\n[PASS] test_addPoolRejectsFarmOfAnotherController() (gas: 73769)\n[PASS] test_addPoolRejectsForeignOrDuplicateFarms() (gas: 41070)\n[PASS] test_allocationChangesRevertWhilePausedOrCoreUnavailable() (gas: 795106)\n[PASS] test_batchRejectsDuplicatePoolIds() (gas: 52423)\n[PASS] test_batchSetPoolWeightsReallocatesAtomically() (gas: 1253410)\n[PASS] test_disableAndEnablePool() (gas: 1421082)\n[PASS] test_emissionSplitsByWeightAndReserveStaysUnminted() (gas: 2284774)\n[PASS] test_gasScalabilityDoesNotGrowWithPoolCount() (gas: 11965926)\n[PASS] test_initialAllocationIs400_200_200_200() (gas: 86004)\n[PASS] test_onlyOwnerAdministers() (gas: 190854)\n[PASS] test_onlyPoolFarmPullsRewards() (gas: 581058)\n[PASS] test_reserveAssignedLaterIsNeverRetroactive() (gas: 996640)\n[PASS] test_setPoolWeightAppliesProspectivelyAfterCheckpoint() (gas: 1022167)\n[PASS] test_treasuryAndGuardianSetters() (gas: 105151)\n[PASS] test_weightsCannotExceedTotalPoints() (gas: 235936)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 93.70ms (23.15ms CPU time)\n\nRan 42 tests for test/Adversarial.t.sol:AdversarialTest\n[PASS] testFuzz_anyCheckpointSplitMatchesSchedule(uint32[6]) (runs: 512, μ: 1287258, ~: 1293399)\n[PASS] testFuzz_hamsterBiteExactSplit(uint96,uint96,uint16) (runs: 512, μ: 452976, ~: 453002)\n[PASS] test_activateRequiresBoundToken() (gas: 67953)\n[PASS] test_addPoolZeroAddressAndOverweight() (gas: 232507)\n[PASS] test_allocationChangeRevertsDuringRegression() (gas: 718555)\n[PASS] test_allocationChangesAllowedAfterCompletion() (gas: 980297)\n[PASS] test_batchRejectsLengthMismatchEmptyAndOverweight() (gas: 153697)\n[PASS] test_bindTokenZeroAndNonOwner() (gas: 67311)\n[PASS] test_burnMoreThanBalanceReverts() (gas: 59667)\n[PASS] test_cannotWithdrawSomeoneElsesShares() (gas: 174462)\n[PASS] test_claimToZeroAddressReverts() (gas: 37311)\n[PASS] test_claimWithNothingReturnsZeroAndMovesNothing() (gas: 542656)\n[PASS] test_constructorRejectsZeroCoreOrTreasury() (gas: 8281)\n[PASS] test_depositAfterFullExitStartsAtFreshPrice() (gas: 620538)\n[PASS] test_depositWaitsForCoreRecoveryThenSucceeds() (gas: 1271029)\n[PASS] test_depositsPausedStillAllowsWithdrawAndClaim() (gas: 1372000)\n[PASS] test_disableTwiceAndEnableEnabledRevert() (gas: 117557)\n[PASS] test_disabledPoolKeepsAccruedButEarnsNothingMore() (gas: 1248428)\n[PASS] test_gasBurningCoreDoesNotBlockExitsOrPause() (gas: 89029732)\n[PASS] test_lastWithdrawerPaysFullFifteenPercentToTreasuryAfterFees() (gas: 507139)\n[PASS] test_malformedCoreDataDoesNotBlockExitsButBlocksDeposits() (gas: 1787614)\n[PASS] test_mintEmissionToZeroAddressReverts() (gas: 32474)\n[PASS] test_mintExactlyRemainingThenOneMoreReverts() (gas: 117779)\n[PASS] test_onlyOwnerPausesDepositsAndSkims() (gas: 113989)\n[PASS] test_ownerAndTreasuryCannotMint() (gas: 93544)\n[PASS] test_ownershipIsTwoStep() (gas: 136441)\n[PASS] test_partialBlockProgressNeverEmits() (gas: 734433)\n[PASS] test_pausePricesBlocksCertifiedBeforeIt() (gas: 1246202)\n[PASS] test_pauseStateMachine() (gas: 145319)\n[PASS] test_pauseSucceedsWhileCoreUnavailable() (gas: 756186)\n[PASS] test_reenabledPoolGetsNothingForDisabledPeriod() (gas: 1297383)\n[PASS] test_regressionThenRecoveryNeverDoubleCounts() (gas: 885735)\n[PASS] test_settersRejectZeroAndStrangers() (gas: 163471)\n[PASS] test_simdSetAssetGuards() (gas: 179575)\n[PASS] test_singleBlockEmissionAtEveryBoundary() (gas: 2199223)\n[PASS] test_startHeightIgnoresPartialProgressAtH0() (gas: 2635093)\n[PASS] test_unavailableCoreDoesNotBlockWithdrawOrClaim() (gas: 1219406)\n[PASS] test_unconfiguredSimdFarmForfeitsItsEmission() (gas: 941743)\n[PASS] test_unknownPoolIdsRevert() (gas: 145259)\n[PASS] test_weightChangeOnDisabledPoolAppliesOnEnable() (gas: 1188310)\n[PASS] test_weightZeroPoolEarnsNothing() (gas: 914515)\n[PASS] test_withdrawingDustSharesBurnsThemForNothing() (gas: 232925)\nSuite result: ok. 42 passed; 0 failed; 0 skipped; finished in 93.82ms (185.97ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_entitlementsNeverExceedReserve\n[PASS] invariant_ethConservation\n[PASS] invariant_farmBalanceCoversPrincipal\n[PASS] invariant_sharesSumAndBacking\n[PASS] invariant_supplyAccounting\n[PASS] invariant_userActionsNeverRevertUnexpectedly\n[PASS] invariant_weightsWithinTotal\n InvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭---------------+------------+-------+---------+----------╮\n| Contract      | Selector   | Calls | Reverts | Discards |\n+=========================================================+\n| CheeseHandler | advance    | 298   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | checkpoint | 290   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | claim      | 289   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | deposit    | 287   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | setWeight  | 292   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | toggle     | 299   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | withdraw   | 293   | 0       | 0        |\n╰---------------+------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 809.69ms (804.25ms CPU time)\n\nRan 1 test for test/SystemInvariants.t.sol:SystemInvariantTest\n[PASS]\nSystemInvariantTest invariants:\n[PASS] invariant_exitsNeverRevert\n[PASS] invariant_monotoneAndFinal\n[PASS] invariant_principalConservation\n[PASS] invariant_reservesBacked\n[PASS] invariant_rewardsOwedBoundedByReserve\n[PASS] invariant_sharesFullySubscribed\n[PASS] invariant_supplyAndMintAccounting\n SystemInvariantTest invariants (runs: 128, calls: 6144, reverts: 0)\n\n╭---------------+----------------+-------+---------+----------╮\n| Contract      | Selector       | Calls | Reverts | Discards |\n+=============================================================+\n| SystemHandler | advance        | 462   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | checkpoint     | 470   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | claim          | 484   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | depositCheese  | 459   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | depositEth     | 475   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | donateCheese   | 488   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | setWeight      | 496   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | skim           | 481   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | toggleOutage   | 455   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | togglePause    | 463   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | togglePool     | 478   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | withdrawCheese | 495   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | withdrawEth    | 438   | 0       | 0        |\n╰---------------+----------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.26s (2.25s CPU time)\n\nRan 9 test suites in 2.26s (3.66s CPU time): 130 tests passed, 0 failed, 0 skipped (130 total tests)\n","passed":true},{"durationMs":62,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CheeseEmissionController.acceptOwnership()\",\"CheeseEmissionController.activate()\",\"CheeseEmissionController.addPool(address,uint16)\",\"CheeseEmissionController.batchSetPoolWeights(uint256[],uint16[])\",\"CheeseEmissionController.bindToken(address)\",\"CheeseEmissionController.checkpoint()\",\"CheeseEmissionController.disablePool(uint256)\",\"CheeseEmissionController.enablePool(uint256)\",\"CheeseEmissionController.pauseEmissions()\",\"CheeseEmissionController.pullRewards(uint256,bool)\",\"CheeseEmissionController.renounceOwnership()\",\"CheeseEmissionController.setGuardian(address)\",\"CheeseEmissionController.setPoolWeight(uint256,uint16)\",\"CheeseEmissionController.setTreasury(address)\",\"CheeseEmissionController.transferOwnership(address)\",\"CheeseEmissionController.unpauseEmissions()\",\"CheeseFarmERC20.acceptOwnership()\",\"CheeseFarmERC20.claim(address)\",\"CheeseFarmERC20.deposit(uint256)\",\"CheeseFarmERC20.onPoolRegistered(uint256)\",\"CheeseFarmERC20.payTreasury()\",\"CheeseFarmERC20.renounceOwnership()\",\"CheeseFarmERC20.setDepositsPaused(bool)\",\"CheeseFarmERC20.skimSurplus()\",\"CheeseFarmERC20.transferOwnership(address)\",\"CheeseFarmERC20.withdraw(uint256)\",\"CheeseFarmETH.acceptOwnership()\",\"CheeseFarmETH.claim(address)\",\"CheeseFarmETH.deposit()\",\"CheeseFarmETH.onPoolRegistered(uint256)\",\"CheeseFarmETH.payTreasury()\",\"CheeseFarmETH.receive()\",\"CheeseFarmETH.renounceOwnership()\",\"CheeseFarmETH.setDepositsPaused(bool)\",\"CheeseFarmETH.skimSurplus()\",\"CheeseFarmETH.transferOwnership(address)\",\"CheeseFarmETH.withdraw(uint256)\",\"CheeseFarmSIMD.acceptOwnership()\",\"CheeseFarmSIMD.claim(address)\",\"CheeseFarmSIMD.deposit(uint256)\",\"CheeseFarmSIMD.onPoolRegistered(uint256)\",\"CheeseFarmSIMD.payTreasury()\",\"CheeseFarmSIMD.renounceOwnership()\",\"CheeseFarmSIMD.setAsset(address)\",\"CheeseFarmSIMD.setDepositsPaused(bool)\",\"CheeseFarmSIMD.skimSurplus()\",\"CheeseFarmSIMD.transferOwnership(address)\",\"CheeseFarmSIMD.withdraw(uint256)\",\"CheeseToken.approve(address,uint256)\",\"CheeseToken.burn(uint256)\",\"CheeseToken.mintEmission(address,uint256)\",\"CheeseToken.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"CheeseToken.transfer(address,uint256)\",\"CheeseToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"README.md\":242,\"docs/ARCHITECTURE.md\":83,\"docs/SECURITY.md\":104,\"docs/SQUEEKCHAIN_VERIFICATION.md\":85,\"foundry.toml\":28,\"remappings.txt\":2,\"script/Deploy.s.sol\":112,\"script/VerifySqueekchain.s.sol\":70,\"src/CheeseEmissionController.sol\":571,\"src/CheeseFarm.sol\":375,\"src/CheeseToken.sol\":81,\"src/farms/CheeseFarmERC20.sol\":47,\"src/farms/CheeseFarmETH.sol\":46,\"src/farms/CheeseFarmSIMD.sol\":66,\"src/interfaces/ICheeseEmissionController.sol\":25,\"src/interfaces/ICheeseFarm.sol\":9,\"src/interfaces/ICheeseToken.sol\":13,\"src/interfaces/ISqueekCore.sol\":68,\"src/interfaces/ISqueekMining.sol\":36,\"src/libraries/SqueekIssuance.sol\":70,\"test/Adversarial.t.sol\":662,\"test/Allocation.t.sol\":319,\"test/CheeseBase.t.sol\":142,\"test/Deploy.t.sol\":76,\"test/Emission.t.sol\":427,\"test/Farm.t.sol\":602,\"test/FarmFuzz.t.sol\":80,\"test/Invariants.t.sol\":188,\"test/SystemInvariants.t.sol\":336,\"test/Token.t.sol\":75,\"test/mocks/Attackers.sol\":71,\"test/mocks/MockERC20.sol\":51,\"test/mocks/MockSqueekCore.sol\":100,\"web/abi/CheeseEmissionController.json\":1655,\"web/abi/CheeseFarmERC20.json\":969,\"web/abi/CheeseFarmETH.json\":970,\"web/abi/CheeseFarmSIMD.json\":1000,\"web/abi/CheeseToken.json\":750,\"web/config.sepolia.json\":49},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"b3a13cdff0669c04f9f01878994106da7145803872c2198bb4917b005d26af09","verifiedTreeHash":"d7ce3436b7a4d7ec673b730111399ef9b05dc29b","verifierVersion":"0.1.0+18543036"},{"checks":[{"durationMs":4557,"exitCode":0,"name":"build","output":"Compiling 72 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.46s\nCompiler run successful!\n","passed":true},{"durationMs":932,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/Deploy.t.sol:DeployTest\n[PASS] test_constructorsNeedNoLiveContracts() (gas: 20298825)\n[PASS] test_wireAndHandOver() (gas: 21888965)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 14.68ms (2.17ms CPU time)\n\nRan 5 tests for test/Token.t.sol:TokenTest\n[PASS] testFuzz_mintNeverExceedsCap(uint256,uint256) (runs: 256, μ: 140380, ~: 142590)\n[PASS] test_capBoundsMintedTotalNotSupply() (gas: 237213)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 10284)\n[PASS] test_metadataAndPremint() (gas: 96648)\n[PASS] test_onlyEmissionAuthorityMints() (gas: 139970)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 46.81ms (8.13ms CPU time)\n\nRan 29 tests for test/Emission.t.sol:EmissionTest\n[PASS] testFuzz_issuedMatchesReferenceAndIsMonotone(uint64,uint64) (runs: 256, μ: 6109, ~: 5953)\n[PASS] test_activateRevertsOnClockMismatch() (gas: 84662)\n[PASS] test_activateRevertsTwiceAndForNonOwner() (gas: 135465)\n[PASS] test_activateRevertsWhenCoreUnavailable() (gas: 76071)\n[PASS] test_activateSchedulesNextRound() (gas: 97471)\n[PASS] test_activateTreatsUndecodableConfigurationAsUnavailable() (gas: 118448)\n[PASS] test_bindTokenOnceAndOnlyMatchingAuthority() (gas: 103028)\n[PASS] test_blocksBeforeActivationNeverEmit() (gas: 2653636)\n[PASS] test_checkpointBeforeActivationIsNoop() (gas: 90666)\n[PASS] test_completionEmitsEventAndMintsAtMostAllowance() (gas: 1450964)\n[PASS] test_coreRegressionIsIgnored() (gas: 708125)\n[PASS] test_coreUnavailableIsTolerated() (gas: 742249)\n[PASS] test_coreWithoutCodeIsUnavailableNotARevert() (gas: 972360)\n[PASS] test_delayedCheckpointEqualsFrequentCheckpoints() (gas: 2455825)\n[PASS] test_fullScheduleCompletesAndStaysCapped() (gas: 745215)\n[PASS] test_guardianCanPauseOnlyOwnerUnpauses() (gas: 171298)\n[PASS] test_halvingCrossingInsideOneCheckpoint() (gas: 770494)\n[PASS] test_issuedMatchesLiveSqueekMiningVectors() (gas: 8033)\n[PASS] test_noDoubleMintingOnRepeatedCheckpoints() (gas: 833204)\n[PASS] test_noEmissionBeforeStartRoundCertified() (gas: 2422916)\n[PASS] test_pausePricesBlocksCertifiedBeforeItForThoseStakedThen() (gas: 1524001)\n[PASS] test_pauseStillWorksWhenCoreIsUnavailable() (gas: 790610)\n[PASS] test_pauseStopsEmissionAndUnpauseCatchesUp() (gas: 933880)\n[PASS] test_pendingCheckpointPreview() (gas: 2668040)\n[PASS] test_perBlockRatesAcrossHalvings() (gas: 12379)\n[PASS] test_scheduleConstantsMatchSqueekMining() (gas: 1716)\n[PASS] test_undecodableCoreStateIsUnavailableNotARevert() (gas: 1584305)\n[PASS] test_undecodableRoundPageDelaysAnchorWithoutRevert() (gas: 2533549)\n[PASS] test_zeroBlockMovementEmitsNothing() (gas: 742045)\nSuite result: ok. 29 passed; 0 failed; 0 skipped; finished in 61.68ms (21.81ms CPU time)\n\nRan 4 tests for test/FarmFuzz.t.sol:FarmFuzzTest\n[PASS] testFuzz_checkpointGroupingIsIrrelevant(uint16,uint16,uint16) (runs: 256, μ: 2839849, ~: 2841538)\n[PASS] testFuzz_depositWithdrawConservesAssets(uint96,uint96,uint96) (runs: 256, μ: 423986, ~: 429335)\n[PASS] testFuzz_lateDepositorNeverInheritsRewards(uint96,uint96,uint32) (runs: 256, μ: 1334007, ~: 1334304)\n[PASS] testFuzz_rewardsSplitProRata(uint96,uint96,uint32) (runs: 256, μ: 1363883, ~: 1364021)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 62.75ms (189.26ms CPU time)\n\nRan 15 tests for test/Allocation.t.sol:AllocationTest\n[PASS] test_addPoolRejectsFarmOfAnotherController() (gas: 73769)\n[PASS] test_addPoolRejectsForeignOrDuplicateFarms() (gas: 41070)\n[PASS] test_allocationChangesRevertWhilePausedOrCoreUnavailable() (gas: 795106)\n[PASS] test_batchRejectsDuplicatePoolIds() (gas: 52423)\n[PASS] test_batchSetPoolWeightsReallocatesAtomically() (gas: 1253410)\n[PASS] test_disableAndEnablePool() (gas: 1421082)\n[PASS] test_emissionSplitsByWeightAndReserveStaysUnminted() (gas: 2284774)\n[PASS] test_gasScalabilityDoesNotGrowWithPoolCount() (gas: 11965926)\n[PASS] test_initialAllocationIs400_200_200_200() (gas: 86004)\n[PASS] test_onlyOwnerAdministers() (gas: 190854)\n[PASS] test_onlyPoolFarmPullsRewards() (gas: 581058)\n[PASS] test_reserveAssignedLaterIsNeverRetroactive() (gas: 996640)\n[PASS] test_setPoolWeightAppliesProspectivelyAfterCheckpoint() (gas: 1022167)\n[PASS] test_treasuryAndGuardianSetters() (gas: 105151)\n[PASS] test_weightsCannotExceedTotalPoints() (gas: 235936)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 62.89ms (25.70ms CPU time)\n\nRan 31 tests for test/Farm.t.sol:FarmTest\n[PASS] test_cheeseFarmDeferredTreasuryShareIsReservedAgainstSkim() (gas: 1785292)\n[PASS] test_cheeseFarmKeepsPrincipalAndRewardReservesApart() (gas: 1657902)\n[PASS] test_claimNeverRevertsOnReserveShortfall() (gas: 1631947)\n[PASS] test_deferredTreasuryShareIsPaidOnceTheTreasuryCanReceive() (gas: 842038)\n[PASS] test_depositMintsSharesOneToOneInitially() (gas: 171620)\n[PASS] test_depositRejectsZeroAndPausedAndUnconfigured() (gas: 287141)\n[PASS] test_depositWaitsForSyncWhileWithdrawAndClaimDoNot() (gas: 1612964)\n[PASS] test_depositsCloseWhileEmissionsPausedWithdrawalsStayOpen() (gas: 816221)\n[PASS] test_directEthTransferRejected() (gas: 32983)\n[PASS] test_donationsDoNotMoveSharePriceAndAreSkimmableOnly() (gas: 704251)\n[PASS] test_emptyFarmEmissionIsForfeitedNotInherited() (gas: 1200789)\n[PASS] test_emptyFarmSweepsResidualRetainedFeeToTreasury() (gas: 662630)\n[PASS] test_erc20FarmDefersTreasuryShareTheTokenRefusesToDeliver() (gas: 1267605)\n[PASS] test_everyAccountCanClaimAfterEmissionEnds() (gas: 4750886)\n[PASS] test_feeOnTransferAssetCreditsReceivedAmount() (gas: 861595)\n[PASS] test_feeWorksAfterEmissionEnds() (gas: 1357617)\n[PASS] test_firstDepositDuringOutageCannotInheritForfeitableEmission() (gas: 978297)\n[PASS] test_gasBurningOrReceiverlessTreasuryIsDeferredToo() (gas: 904656)\n[PASS] test_hamsterBiteSplit85_14_1() (gas: 425636)\n[PASS] test_inflationAttackIsUneconomic() (gas: 1219626)\n[PASS] test_lastDepositorWithdrawalSendsFull15ToTreasury() (gas: 291417)\n[PASS] test_newDepositorGetsNothingFromEarlierBlocks() (gas: 1267865)\n[PASS] test_noCooldownWithdrawSameBlockAsDeposit() (gas: 255325)\n[PASS] test_onPoolRegisteredOnlyController() (gas: 341789)\n[PASS] test_oneWeiFirstDepositorCannotBrickLaterDeposits() (gas: 4761772)\n[PASS] test_ownerCannotTouchPrincipalOrRewards() (gas: 1050252)\n[PASS] test_partialWithdrawerBenefitsFromOwnRetainedShare() (gas: 441669)\n[PASS] test_reentrancyBlockedOnWithdrawDepositAndClaim() (gas: 1190133)\n[PASS] test_revertingTreasuryNeverBlocksWithdrawalEvenWithoutAnOwner() (gas: 765968)\n[PASS] test_rewardsAccrueProRataAndClaimIsIndependentOfPrincipal() (gas: 1790219)\n[PASS] test_withdrawRejectsZeroAndTooMany() (gas: 212948)\nSuite result: ok. 31 passed; 0 failed; 0 skipped; finished in 63.52ms (31.59ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_entitlementsNeverExceedReserve\n[PASS] invariant_ethConservation\n[PASS] invariant_farmBalanceCoversPrincipal\n[PASS] invariant_sharesSumAndBacking\n[PASS] invariant_supplyAccounting\n[PASS] invariant_userActionsNeverRevertUnexpectedly\n[PASS] invariant_weightsWithinTotal\n InvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭---------------+------------+-------+---------+----------╮\n| Contract      | Selector   | Calls | Reverts | Discards |\n+=========================================================+\n| CheeseHandler | advance    | 298   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | checkpoint | 290   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | claim      | 289   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | deposit    | 287   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | setWeight  | 292   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | toggle     | 299   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | withdraw   | 293   | 0       | 0        |\n╰---------------+------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 804.04ms (797.05ms CPU time)\n\nRan 7 test suites in 805.84ms (1.12s CPU time): 87 tests passed, 0 failed, 0 skipped (87 total tests)\n","passed":true},{"durationMs":63,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CheeseEmissionController.acceptOwnership()\",\"CheeseEmissionController.activate()\",\"CheeseEmissionController.addPool(address,uint16)\",\"CheeseEmissionController.batchSetPoolWeights(uint256[],uint16[])\",\"CheeseEmissionController.bindToken(address)\",\"CheeseEmissionController.checkpoint()\",\"CheeseEmissionController.disablePool(uint256)\",\"CheeseEmissionController.enablePool(uint256)\",\"CheeseEmissionController.pauseEmissions()\",\"CheeseEmissionController.pullRewards(uint256,bool)\",\"CheeseEmissionController.renounceOwnership()\",\"CheeseEmissionController.setGuardian(address)\",\"CheeseEmissionController.setPoolWeight(uint256,uint16)\",\"CheeseEmissionController.setTreasury(address)\",\"CheeseEmissionController.transferOwnership(address)\",\"CheeseEmissionController.unpauseEmissions()\",\"CheeseFarmERC20.acceptOwnership()\",\"CheeseFarmERC20.claim(address)\",\"CheeseFarmERC20.deposit(uint256)\",\"CheeseFarmERC20.onPoolRegistered(uint256)\",\"CheeseFarmERC20.payTreasury()\",\"CheeseFarmERC20.renounceOwnership()\",\"CheeseFarmERC20.setDepositsPaused(bool)\",\"CheeseFarmERC20.skimSurplus()\",\"CheeseFarmERC20.transferOwnership(address)\",\"CheeseFarmERC20.withdraw(uint256)\",\"CheeseFarmETH.acceptOwnership()\",\"CheeseFarmETH.claim(address)\",\"CheeseFarmETH.deposit()\",\"CheeseFarmETH.onPoolRegistered(uint256)\",\"CheeseFarmETH.payTreasury()\",\"CheeseFarmETH.receive()\",\"CheeseFarmETH.renounceOwnership()\",\"CheeseFarmETH.setDepositsPaused(bool)\",\"CheeseFarmETH.skimSurplus()\",\"CheeseFarmETH.transferOwnership(address)\",\"CheeseFarmETH.withdraw(uint256)\",\"CheeseFarmSIMD.acceptOwnership()\",\"CheeseFarmSIMD.claim(address)\",\"CheeseFarmSIMD.deposit(uint256)\",\"CheeseFarmSIMD.onPoolRegistered(uint256)\",\"CheeseFarmSIMD.payTreasury()\",\"CheeseFarmSIMD.renounceOwnership()\",\"CheeseFarmSIMD.setAsset(address)\",\"CheeseFarmSIMD.setDepositsPaused(bool)\",\"CheeseFarmSIMD.skimSurplus()\",\"CheeseFarmSIMD.transferOwnership(address)\",\"CheeseFarmSIMD.withdraw(uint256)\",\"CheeseToken.approve(address,uint256)\",\"CheeseToken.burn(uint256)\",\"CheeseToken.mintEmission(address,uint256)\",\"CheeseToken.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"CheeseToken.transfer(address,uint256)\",\"CheeseToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"README.md\":242,\"docs/ARCHITECTURE.md\":83,\"docs/SECURITY.md\":104,\"docs/SQUEEKCHAIN_VERIFICATION.md\":85,\"foundry.toml\":28,\"launch.json\":45,\"remappings.txt\":2,\"script/Deploy.s.sol\":112,\"script/VerifySqueekchain.s.sol\":70,\"src/CheeseEmissionController.sol\":571,\"src/CheeseFarm.sol\":375,\"src/CheeseToken.sol\":81,\"src/farms/CheeseFarmERC20.sol\":47,\"src/farms/CheeseFarmETH.sol\":46,\"src/farms/CheeseFarmSIMD.sol\":66,\"src/interfaces/ICheeseEmissionController.sol\":25,\"src/interfaces/ICheeseFarm.sol\":9,\"src/interfaces/ICheeseToken.sol\":13,\"src/interfaces/ISqueekCore.sol\":68,\"src/interfaces/ISqueekMining.sol\":36,\"src/libraries/SqueekIssuance.sol\":70,\"test/Allocation.t.sol\":319,\"test/CheeseBase.t.sol\":142,\"test/Deploy.t.sol\":76,\"test/Emission.t.sol\":427,\"test/Farm.t.sol\":602,\"test/FarmFuzz.t.sol\":80,\"test/Invariants.t.sol\":188,\"test/Token.t.sol\":75,\"test/mocks/Attackers.sol\":71,\"test/mocks/MockERC20.sol\":51,\"test/mocks/MockSqueekCore.sol\":100,\"web/abi/CheeseEmissionController.json\":1655,\"web/abi/CheeseFarmERC20.json\":969,\"web/abi/CheeseFarmETH.json\":970,\"web/abi/CheeseFarmSIMD.json\":1000,\"web/abi/CheeseToken.json\":750,\"web/config.sepolia.json\":49},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"d251f89f27d35127eb0e4d5d81a21b5a32d94d7185a8112e2bacf2ddd617bea7","verifiedTreeHash":"86a49e48161f553ddedd2b0d830c9b6bb428ca5a","verifierVersion":"0.1.0+18543036"},{"checks":[{"durationMs":5564,"exitCode":0,"name":"build","output":"Compiling 72 files with Solc 0.8.26\nSolc 0.8.26 finished in 5.43s\nCompiler run successful!\n","passed":true},{"durationMs":1021,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/Deploy.t.sol:DeployTest\n[PASS] test_constructorsNeedNoLiveContracts() (gas: 20234299)\n[PASS] test_wireAndHandOver() (gas: 21824523)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 7.65ms (9.02ms CPU time)\n\nRan 5 tests for test/Token.t.sol:TokenTest\n[PASS] testFuzz_mintNeverExceedsCap(uint256,uint256) (runs: 256, μ: 140236, ~: 142578)\n[PASS] test_capBoundsMintedTotalNotSupply() (gas: 237213)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 10284)\n[PASS] test_metadataAndPremint() (gas: 96648)\n[PASS] test_onlyEmissionAuthorityMints() (gas: 139970)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 10.93ms (11.07ms CPU time)\n\nRan 32 tests for test/Farm.t.sol:FarmTest\n[PASS] test_cheeseFarmDeferredTreasuryShareIsReservedAgainstSkim() (gas: 1785805)\n[PASS] test_cheeseFarmKeepsPrincipalAndRewardReservesApart() (gas: 1658684)\n[PASS] test_claimNeverRevertsOnReserveShortfall() (gas: 1633340)\n[PASS] test_deferredTreasuryShareIsPaidOnceTheTreasuryCanReceive() (gas: 842038)\n[PASS] test_depositMintsSharesOneToOneInitially() (gas: 171642)\n[PASS] test_depositRejectsZeroAndPausedAndUnconfigured() (gas: 287163)\n[PASS] test_depositWaitsForSyncWhileWithdrawAndClaimDoNot() (gas: 1613657)\n[PASS] test_depositsCloseWhileEmissionsPausedWithdrawalsStayOpen() (gas: 816612)\n[PASS] test_directEthTransferRejected() (gas: 32983)\n[PASS] test_donationsDoNotMoveSharePriceAndAreSkimmableOnly() (gas: 704273)\n[PASS] test_emptyFarmEmissionIsForfeitedNotInherited() (gas: 1201451)\n[PASS] test_emptyFarmSweepsResidualRetainedFeeToTreasury() (gas: 662652)\n[PASS] test_erc20FarmDefersTreasuryShareTheTokenRefusesToDeliver() (gas: 1267605)\n[PASS] test_everyAccountCanClaimAfterEmissionEnds() (gas: 4753139)\n[PASS] test_feeOnTransferAssetCreditsReceivedAmount() (gas: 861595)\n[PASS] test_feeWorksAfterEmissionEnds() (gas: 1357986)\n[PASS] test_firstDepositDuringOutageCannotInheritForfeitableEmission() (gas: 978831)\n[PASS] test_gasBurningOrReceiverlessTreasuryIsDeferredToo() (gas: 904656)\n[PASS] test_hamsterBiteSplit85_14_1() (gas: 425636)\n[PASS] test_inflationAttackIsUneconomic() (gas: 1219626)\n[PASS] test_lastDepositorWithdrawalSendsFull15ToTreasury() (gas: 291417)\n[PASS] test_newDepositorGetsNothingFromEarlierBlocks() (gas: 1268602)\n[PASS] test_noCooldownWithdrawSameBlockAsDeposit() (gas: 255347)\n[PASS] test_onPoolRegisteredOnlyController() (gas: 341811)\n[PASS] test_oneWeiFirstDepositorCannotBrickLaterDeposits() (gas: 4764167)\n[PASS] test_ownerCannotTouchPrincipalOrRewards() (gas: 1050621)\n[PASS] test_partialWithdrawerBenefitsFromOwnRetainedShare() (gas: 441691)\n[PASS] test_pendingRewardsMatchesClaimExactlyWithUnsyncedBlocks() (gas: 26569510)\n[PASS] test_reentrancyBlockedOnWithdrawDepositAndClaim() (gas: 1190044)\n[PASS] test_revertingTreasuryNeverBlocksWithdrawalEvenWithoutAnOwner() (gas: 765990)\n[PASS] test_rewardsAccrueProRataAndClaimIsIndependentOfPrincipal() (gas: 1790710)\n[PASS] test_withdrawRejectsZeroAndTooMany() (gas: 212948)\nSuite result: ok. 32 passed; 0 failed; 0 skipped; finished in 71.64ms (49.02ms CPU time)\n\nRan 15 tests for test/Allocation.t.sol:AllocationTest\n[PASS] test_addPoolRejectsFarmOfAnotherController() (gas: 73769)\n[PASS] test_addPoolRejectsForeignOrDuplicateFarms() (gas: 41070)\n[PASS] test_allocationChangesRevertWhilePausedOrCoreUnavailable() (gas: 795463)\n[PASS] test_batchRejectsDuplicatePoolIds() (gas: 52423)\n[PASS] test_batchSetPoolWeightsReallocatesAtomically() (gas: 1253851)\n[PASS] test_disableAndEnablePool() (gas: 1421653)\n[PASS] test_emissionSplitsByWeightAndReserveStaysUnminted() (gas: 2285460)\n[PASS] test_gasScalabilityDoesNotGrowWithPoolCount() (gas: 11970744)\n[PASS] test_initialAllocationIs400_200_200_200() (gas: 86004)\n[PASS] test_onlyOwnerAdministers() (gas: 190854)\n[PASS] test_onlyPoolFarmPullsRewards() (gas: 581283)\n[PASS] test_reserveAssignedLaterIsNeverRetroactive() (gas: 997081)\n[PASS] test_setPoolWeightAppliesProspectivelyAfterCheckpoint() (gas: 1022608)\n[PASS] test_treasuryAndGuardianSetters() (gas: 105151)\n[PASS] test_weightsCannotExceedTotalPoints() (gas: 235936)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 72.44ms (18.73ms CPU time)\n\nRan 4 tests for test/FarmFuzz.t.sol:FarmFuzzTest\n[PASS] testFuzz_checkpointGroupingIsIrrelevant(uint16,uint16,uint16) (runs: 256, μ: 2840248, ~: 2841907)\n[PASS] testFuzz_depositWithdrawConservesAssets(uint96,uint96,uint96) (runs: 256, μ: 424120, ~: 429335)\n[PASS] testFuzz_lateDepositorNeverInheritsRewards(uint96,uint96,uint32) (runs: 256, μ: 1334808, ~: 1335103)\n[PASS] testFuzz_rewardsSplitProRata(uint96,uint96,uint32) (runs: 256, μ: 1364399, ~: 1364534)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 94.08ms (232.80ms CPU time)\n\nRan 32 tests for test/Emission.t.sol:EmissionTest\n[PASS] testFuzz_issuedMatchesReferenceAndIsMonotone(uint64,uint64) (runs: 256, μ: 6160, ~: 6059)\n[PASS] test_activateRevertsOnClockMismatch() (gas: 84746)\n[PASS] test_activateRevertsTwiceAndForNonOwner() (gas: 135549)\n[PASS] test_activateRevertsWhenCoreUnavailable() (gas: 76134)\n[PASS] test_activateSchedulesNextRound() (gas: 97555)\n[PASS] test_activateTreatsUndecodableConfigurationAsUnavailable() (gas: 118586)\n[PASS] test_bindTokenOnceAndOnlyMatchingAuthority() (gas: 103028)\n[PASS] test_blocksBeforeActivationNeverEmit() (gas: 2653933)\n[PASS] test_checkpointBeforeActivationIsNoop() (gas: 90644)\n[PASS] test_completionEmitsEventAndMintsAtMostAllowance() (gas: 1451547)\n[PASS] test_coreRegressionIsIgnored() (gas: 708494)\n[PASS] test_coreUnavailableIsTolerated() (gas: 742666)\n[PASS] test_coreWithoutCodeIsUnavailableNotARevert() (gas: 972987)\n[PASS] test_delayedCheckpointEqualsFrequentCheckpoints() (gas: 2456864)\n[PASS] test_fullScheduleCompletesAndStaysCapped() (gas: 745512)\n[PASS] test_guardianCanPauseOnlyOwnerUnpauses() (gas: 171320)\n[PASS] test_halvingCrossingInsideOneCheckpoint() (gas: 770863)\n[PASS] test_issuedMatchesLiveSqueekMiningVectors() (gas: 8033)\n[PASS] test_noDoubleMintingOnRepeatedCheckpoints() (gas: 833773)\n[PASS] test_noEmissionBeforeStartRoundCertified() (gas: 2423072)\n[PASS] test_oversizedConfigurationIsUnavailableAtActivation() (gas: 29535105)\n[PASS] test_oversizedRoundPageDelaysAnchorWithoutRevert() (gas: 2513958)\n[PASS] test_pausePricesBlocksCertifiedBeforeItForThoseStakedThen() (gas: 1524942)\n[PASS] test_pauseStillWorksWhenCoreIsUnavailable() (gas: 791011)\n[PASS] test_pauseStopsEmissionAndUnpauseCatchesUp() (gas: 934343)\n[PASS] test_pendingCheckpointPreview() (gas: 2668431)\n[PASS] test_perBlockRatesAcrossHalvings() (gas: 12379)\n[PASS] test_returnBombCoreDoesNotBlockExitsOrPause() (gas: 176435117)\n[PASS] test_scheduleConstantsMatchSqueekMining() (gas: 1716)\n[PASS] test_undecodableCoreStateIsUnavailableNotARevert() (gas: 1585116)\n[PASS] test_undecodableRoundPageDelaysAnchorWithoutRevert() (gas: 2534024)\n[PASS] test_zeroBlockMovementEmitsNothing() (gas: 742414)\nSuite result: ok. 32 passed; 0 failed; 0 skipped; finished in 243.14ms (349.16ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_entitlementsNeverExceedReserve\n[PASS] invariant_ethConservation\n[PASS] invariant_farmBalanceCoversPrincipal\n[PASS] invariant_sharesSumAndBacking\n[PASS] invariant_supplyAccounting\n[PASS] invariant_userActionsNeverRevertUnexpectedly\n[PASS] invariant_weightsWithinTotal\n InvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭---------------+------------+-------+---------+----------╮\n| Contract      | Selector   | Calls | Reverts | Discards |\n+=========================================================+\n| CheeseHandler | advance    | 298   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | checkpoint | 290   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | claim      | 289   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | deposit    | 287   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | setWeight  | 292   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | toggle     | 299   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | withdraw   | 293   | 0       | 0        |\n╰---------------+------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 858.47ms (854.23ms CPU time)\n\nRan 7 test suites in 861.90ms (1.36s CPU time): 91 tests passed, 0 failed, 0 skipped (91 total tests)\n","passed":true},{"durationMs":91,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CheeseEmissionController.acceptOwnership()\",\"CheeseEmissionController.activate()\",\"CheeseEmissionController.addPool(address,uint16)\",\"CheeseEmissionController.batchSetPoolWeights(uint256[],uint16[])\",\"CheeseEmissionController.bindToken(address)\",\"CheeseEmissionController.checkpoint()\",\"CheeseEmissionController.disablePool(uint256)\",\"CheeseEmissionController.enablePool(uint256)\",\"CheeseEmissionController.pauseEmissions()\",\"CheeseEmissionController.pullRewards(uint256,bool)\",\"CheeseEmissionController.renounceOwnership()\",\"CheeseEmissionController.setGuardian(address)\",\"CheeseEmissionController.setPoolWeight(uint256,uint16)\",\"CheeseEmissionController.setTreasury(address)\",\"CheeseEmissionController.transferOwnership(address)\",\"CheeseEmissionController.unpauseEmissions()\",\"CheeseFarmERC20.acceptOwnership()\",\"CheeseFarmERC20.claim(address)\",\"CheeseFarmERC20.deposit(uint256)\",\"CheeseFarmERC20.onPoolRegistered(uint256)\",\"CheeseFarmERC20.payTreasury()\",\"CheeseFarmERC20.renounceOwnership()\",\"CheeseFarmERC20.setDepositsPaused(bool)\",\"CheeseFarmERC20.skimSurplus()\",\"CheeseFarmERC20.transferOwnership(address)\",\"CheeseFarmERC20.withdraw(uint256)\",\"CheeseFarmETH.acceptOwnership()\",\"CheeseFarmETH.claim(address)\",\"CheeseFarmETH.deposit()\",\"CheeseFarmETH.onPoolRegistered(uint256)\",\"CheeseFarmETH.payTreasury()\",\"CheeseFarmETH.receive()\",\"CheeseFarmETH.renounceOwnership()\",\"CheeseFarmETH.setDepositsPaused(bool)\",\"CheeseFarmETH.skimSurplus()\",\"CheeseFarmETH.transferOwnership(address)\",\"CheeseFarmETH.withdraw(uint256)\",\"CheeseFarmSIMD.acceptOwnership()\",\"CheeseFarmSIMD.claim(address)\",\"CheeseFarmSIMD.deposit(uint256)\",\"CheeseFarmSIMD.onPoolRegistered(uint256)\",\"CheeseFarmSIMD.payTreasury()\",\"CheeseFarmSIMD.renounceOwnership()\",\"CheeseFarmSIMD.setAsset(address)\",\"CheeseFarmSIMD.setDepositsPaused(bool)\",\"CheeseFarmSIMD.skimSurplus()\",\"CheeseFarmSIMD.transferOwnership(address)\",\"CheeseFarmSIMD.withdraw(uint256)\",\"CheeseToken.approve(address,uint256)\",\"CheeseToken.burn(uint256)\",\"CheeseToken.mintEmission(address,uint256)\",\"CheeseToken.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"CheeseToken.transfer(address,uint256)\",\"CheeseToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"README.md\":256,\"docs/ARCHITECTURE.md\":87,\"docs/SECURITY.md\":129,\"docs/SQUEEKCHAIN_VERIFICATION.md\":85,\"foundry.toml\":28,\"remappings.txt\":2,\"script/Deploy.s.sol\":112,\"script/VerifySqueekchain.s.sol\":70,\"src/CheeseEmissionController.sol\":608,\"src/CheeseFarm.sol\":375,\"src/CheeseToken.sol\":81,\"src/farms/CheeseFarmERC20.sol\":47,\"src/farms/CheeseFarmETH.sol\":46,\"src/farms/CheeseFarmSIMD.sol\":71,\"src/interfaces/ICheeseEmissionController.sol\":25,\"src/interfaces/ICheeseFarm.sol\":9,\"src/interfaces/ICheeseToken.sol\":13,\"src/interfaces/ISqueekCore.sol\":68,\"src/interfaces/ISqueekMining.sol\":36,\"src/libraries/SqueekIssuance.sol\":70,\"test/Allocation.t.sol\":319,\"test/CheeseBase.t.sol\":142,\"test/Deploy.t.sol\":76,\"test/Emission.t.sol\":509,\"test/Farm.t.sol\":629,\"test/FarmFuzz.t.sol\":80,\"test/Invariants.t.sol\":188,\"test/Token.t.sol\":75,\"test/mocks/Attackers.sol\":71,\"test/mocks/MockERC20.sol\":51,\"test/mocks/MockSqueekCore.sol\":100,\"web/abi/CheeseEmissionController.json\":1671,\"web/abi/CheeseFarmERC20.json\":969,\"web/abi/CheeseFarmETH.json\":970,\"web/abi/CheeseFarmSIMD.json\":1000,\"web/abi/CheeseToken.json\":750,\"web/config.sepolia.json\":49},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":4226,"exitCode":0,"name":"slither","output":"[medium/medium] divide-before-multiply at src/libraries/SqueekIssuance.sol:47: SqueekIssuance.issued(uint64) (src/libraries/SqueekIssuance.sol#47-57) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/libraries/SqueekIssuance.sol:47: SqueekIssuance.issued(uint64) (src/libraries/SqueekIssuance.sol#47-57) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/libraries/SqueekIssuance.sol:47: SqueekIssuance.issued(uint64) (src/libraries/SqueekIssuance.sol#47-57) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/libraries/SqueekIssuance.sol:47: SqueekIssuance.issued(uint64) (src/libraries/SqueekIssuance.sol#47-57) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/libraries/SqueekIssuance.sol:21: SqueekIssuance.slitherConstructorConstantVariables() (src/libraries/SqueekIssuance.sol#21-70) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/CheeseFarm.sol:155: CheeseFarm.skimSurplus() (src/CheeseFarm.sol#155-161) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CheeseFarm.sol:354: CheeseFarm._tryTransferERC20(IERC20,address,uint256) (src/CheeseFarm.sol#354-359) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CheeseFarm.sol:168: CheeseFarm._deposit(uint256,uint256) (src/CheeseFarm.sol#168-191) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CheeseFarm.sol:314: CheeseFarm._accrue() (src/CheeseFarm.sol#314-322) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CheeseFarm.sol:227: CheeseFarm.claim(address) (src/CheeseFarm.sol#227-241) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/CheeseFarm.sol:168: Reentrancy in CheeseFarm._deposit(uint256,uint256) (src/CheeseFarm.sol#168-191):\n[medium/medium] reentrancy-no-eth at src/CheeseFarm.sol:227: Reentrancy in CheeseFarm.claim(address) (src/CheeseFarm.sol#227-241):\n[medium/medium] reentrancy-no-eth at src/CheeseFarm.sol:195: Reentrancy in CheeseFarm.withdraw(uint256) (src/CheeseFarm.sol#195-224):\n[medium/medium] unused-return at src/CheeseEmissionController.sol:369: CheeseEmissionController.previewPoolEmission(uint256) (src/CheeseEmissionController.sol#369-379) ignores return value by (readable,None,unsynced) = this.pendingCheckpoint() (src/CheeseEmissionController.sol#376)\n[low/high] shadowing-local at src/interfaces/ICheeseFarm.sol:6: ICheeseFarm.onPoolRegistered(uint256).poolId (src/interfaces/ICheeseFarm.sol#8) shadows:\n[low/medium] missing-zero-check at src/CheeseEmissionController.sol:154: CheeseEmissionController.constructor(address,address,address).owner_ (src/CheeseEmissionController.sol#154) lacks a zero-check on :\n[low/medium] reentrancy-benign at src/CheeseFarm.sol:168: Reentrancy in CheeseFarm._deposit(uint256,uint256) (src/CheeseFarm.sol#168-191):\n[low/medium] reentrancy-benign at src/CheeseFarm.sol:195: Reentrancy in CheeseFarm.withdraw(uint256) (src/CheeseFarm.sol#195-224):\n[low/medium] reentrancy-benign at src/CheeseFarm.sol:314: Reentrancy in CheeseFarm._accrue() (src/CheeseFarm.sol#314-322):\n[low/medium] reentrancy-benign at src/CheeseFarm.sol:227: Reentrancy in CheeseFarm.claim(address) (src/CheeseFarm.sol#227-241):\n[low/medium] reentrancy-events at src/CheeseFarm.sol:168: Reentrancy in CheeseFarm._deposit(uint256,uint256) (src/CheeseFarm.sol#168-191):\n[low/medium] reentrancy-events at src/CheeseEmissionController.sol:229: Reentrancy in CheeseEmissionController.addPool(address,uint16) (src/CheeseEmissionController.sol#229-250):\n[low/medium] timestamp at src/CheeseEmissionController.sol:448: CheeseEmissionController._currentRound(uint64) (src/CheeseEmissionController.sol#448-451) uses timestamp for comparisons","passed":true},{"durationMs":776,"exitCode":0,"name":"aderyn","output":"[high] eth-send-unchecked-address at src/CheeseFarm.sol:195: ETH transferred without address checks (2 places)\n[high] reentrancy-state-change at src/CheeseEmissionController.sol:171: Reentrancy: State change after external call (2 places)\n[low] centralization-risk at src/CheeseEmissionController.sol:30: Centralization Risk (15 places)\n[low] costly-loop at src/CheeseEmissionController.sol:268: Costly operations inside loop (2 places)\n[low] large-numeric-literal at src/CheeseEmissionController.sol:41: Large Numeric Literal (3 places)\n[low] non-reentrant-not-first at src/CheeseFarm.sol:155: `nonReentrant` is Not the First Modifier\n[low] require-revert-in-loop at src/CheeseEmissionController.sol:261: Loop Contains `require`/`revert` (4 places)\n[low] state-no-address-check at src/CheeseEmissionController.sol:158: Address State Variable Set Without Checks\n[low] unchecked-return at src/CheeseEmissionController.sol:213: Unchecked Return (5 places)\n[low] uninitialized-local-variable at src/CheeseEmissionController.sol:261: Uninitialized Local Variable (3 places)\n[low] unsafe-erc20-operation at src/CheeseFarm.sol:355: Unsafe ERC20 Operation\n[low] unused-error at src/CheeseFarm.sol:85: Unused Error","passed":true},{"durationMs":2735,"exitCode":0,"name":"proof a26643c61dcc","output":"Compiling 62 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.77s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-b4722a8b/Proof_a26643c61dcc.t.sol:ReturnBombCoreTest\n[PASS] test_returnBombCoreDoesNotBlockWithdrawClaimOrPause() (gas: 116610906)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 113.06ms (111.07ms CPU time)\n\nRan 1 test suite in 114.46ms (113.06ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"e15b2b328157bffd1feaf53e67a52a406432c87fc22c2df6510e16414d949343","verifiedTreeHash":"47e6ab719a10118d951f7a2c0d6eed9c28e980ae","verifierVersion":"0.1.0+18543036"},{"checks":[{"durationMs":6962,"exitCode":0,"name":"build","output":"Compiling 74 files with Solc 0.8.26\nSolc 0.8.26 finished in 6.84s\nCompiler run successful!\n","passed":true},{"durationMs":2488,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/Deploy.t.sol:DeployTest\n[PASS] test_constructorsNeedNoLiveContracts() (gas: 20234299)\n[PASS] test_wireAndHandOver() (gas: 21824523)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 82.16ms (2.87ms CPU time)\n\nRan 4 tests for test/FarmFuzz.t.sol:FarmFuzzTest\n[PASS] testFuzz_checkpointGroupingIsIrrelevant(uint16,uint16,uint16) (runs: 256, μ: 2840349, ~: 2841907)\n[PASS] testFuzz_depositWithdrawConservesAssets(uint96,uint96,uint96) (runs: 256, μ: 424120, ~: 429335)\n[PASS] testFuzz_lateDepositorNeverInheritsRewards(uint96,uint96,uint32) (runs: 256, μ: 1334827, ~: 1335103)\n[PASS] testFuzz_rewardsSplitProRata(uint96,uint96,uint32) (runs: 256, μ: 1364409, ~: 1364534)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 118.12ms (321.52ms CPU time)\n\nRan 32 tests for test/Farm.t.sol:FarmTest\n[PASS] test_cheeseFarmDeferredTreasuryShareIsReservedAgainstSkim() (gas: 1785805)\n[PASS] test_cheeseFarmKeepsPrincipalAndRewardReservesApart() (gas: 1658684)\n[PASS] test_claimNeverRevertsOnReserveShortfall() (gas: 1633340)\n[PASS] test_deferredTreasuryShareIsPaidOnceTheTreasuryCanReceive() (gas: 842038)\n[PASS] test_depositMintsSharesOneToOneInitially() (gas: 171642)\n[PASS] test_depositRejectsZeroAndPausedAndUnconfigured() (gas: 287163)\n[PASS] test_depositWaitsForSyncWhileWithdrawAndClaimDoNot() (gas: 1613657)\n[PASS] test_depositsCloseWhileEmissionsPausedWithdrawalsStayOpen() (gas: 816612)\n[PASS] test_directEthTransferRejected() (gas: 32983)\n[PASS] test_donationsDoNotMoveSharePriceAndAreSkimmableOnly() (gas: 704273)\n[PASS] test_emptyFarmEmissionIsForfeitedNotInherited() (gas: 1201451)\n[PASS] test_emptyFarmSweepsResidualRetainedFeeToTreasury() (gas: 662652)\n[PASS] test_erc20FarmDefersTreasuryShareTheTokenRefusesToDeliver() (gas: 1267605)\n[PASS] test_everyAccountCanClaimAfterEmissionEnds() (gas: 4753139)\n[PASS] test_feeOnTransferAssetCreditsReceivedAmount() (gas: 861595)\n[PASS] test_feeWorksAfterEmissionEnds() (gas: 1357986)\n[PASS] test_firstDepositDuringOutageCannotInheritForfeitableEmission() (gas: 978831)\n[PASS] test_gasBurningOrReceiverlessTreasuryIsDeferredToo() (gas: 904656)\n[PASS] test_hamsterBiteSplit85_14_1() (gas: 425636)\n[PASS] test_inflationAttackIsUneconomic() (gas: 1219626)\n[PASS] test_lastDepositorWithdrawalSendsFull15ToTreasury() (gas: 291417)\n[PASS] test_newDepositorGetsNothingFromEarlierBlocks() (gas: 1268602)\n[PASS] test_noCooldownWithdrawSameBlockAsDeposit() (gas: 255347)\n[PASS] test_onPoolRegisteredOnlyController() (gas: 341811)\n[PASS] test_oneWeiFirstDepositorCannotBrickLaterDeposits() (gas: 4764167)\n[PASS] test_ownerCannotTouchPrincipalOrRewards() (gas: 1050621)\n[PASS] test_partialWithdrawerBenefitsFromOwnRetainedShare() (gas: 441691)\n[PASS] test_pendingRewardsMatchesClaimExactlyWithUnsyncedBlocks() (gas: 26569510)\n[PASS] test_reentrancyBlockedOnWithdrawDepositAndClaim() (gas: 1190044)\n[PASS] test_revertingTreasuryNeverBlocksWithdrawalEvenWithoutAnOwner() (gas: 765990)\n[PASS] test_rewardsAccrueProRataAndClaimIsIndependentOfPrincipal() (gas: 1790710)\n[PASS] test_withdrawRejectsZeroAndTooMany() (gas: 212948)\nSuite result: ok. 32 passed; 0 failed; 0 skipped; finished in 129.14ms (45.06ms CPU time)\n\nRan 46 tests for test/Adversarial.t.sol:AdversarialTest\n[PASS] testFuzz_anyCheckpointSplitMatchesSchedule(uint32[6]) (runs: 512, μ: 1287823, ~: 1294386)\n[PASS] testFuzz_hamsterBiteExactSplit(uint96,uint96,uint16) (runs: 512, μ: 452993, ~: 452989)\n[PASS] testFuzz_pendingRewardsEqualsClaimWithUnsyncedBlocks(uint256,uint256,uint16,uint64,uint64,uint16) (runs: 512, μ: 1923887, ~: 1965006)\n[PASS] test_activateRequiresBoundToken() (gas: 67938)\n[PASS] test_addPoolZeroAddressAndOverweight() (gas: 232551)\n[PASS] test_allocationChangeRevertsDuringRegression() (gas: 719011)\n[PASS] test_allocationChangesAllowedAfterCompletion() (gas: 980754)\n[PASS] test_batchRejectsLengthMismatchEmptyAndOverweight() (gas: 153719)\n[PASS] test_bindTokenZeroAndNonOwner() (gas: 67266)\n[PASS] test_burnMoreThanBalanceReverts() (gas: 59689)\n[PASS] test_cannotWithdrawSomeoneElsesShares() (gas: 174506)\n[PASS] test_claimToZeroAddressReverts() (gas: 37334)\n[PASS] test_claimWithNothingReturnsZeroAndMovesNothing() (gas: 543033)\n[PASS] test_constructorRejectsZeroCoreOrTreasury() (gas: 8325)\n[PASS] test_depositAfterFullExitStartsAtFreshPrice() (gas: 620450)\n[PASS] test_depositWaitsForCoreRecoveryThenSucceeds() (gas: 1272117)\n[PASS] test_depositsPausedStillAllowsWithdrawAndClaim() (gas: 1372622)\n[PASS] test_disableTwiceAndEnableEnabledRevert() (gas: 117601)\n[PASS] test_disabledPoolKeepsAccruedButEarnsNothingMore() (gas: 1249065)\n[PASS] test_gasBurningCoreDoesNotBlockExitsOrPause() (gas: 89030851)\n[PASS] test_gasBurningCoreExitsFitUnderTxGasCap() (gas: 66820980)\n[PASS] test_lastWithdrawerPaysFullFifteenPercentToTreasuryAfterFees() (gas: 507095)\n[PASS] test_malformedCoreDataDoesNotBlockExitsButBlocksDeposits() (gas: 1788353)\n[PASS] test_mintEmissionToZeroAddressReverts() (gas: 32558)\n[PASS] test_mintExactlyRemainingThenOneMoreReverts() (gas: 117930)\n[PASS] test_onlyOwnerPausesDepositsAndSkims() (gas: 114011)\n[PASS] test_ownerAndTreasuryCannotMint() (gas: 93584)\n[PASS] test_ownershipIsTwoStep() (gas: 136463)\n[PASS] test_partialBlockProgressNeverEmits() (gas: 734875)\n[PASS] test_pausePricesBlocksCertifiedBeforeIt() (gas: 1246759)\n[PASS] test_pauseStateMachine() (gas: 145341)\n[PASS] test_pauseSucceedsWhileCoreUnavailable() (gas: 756653)\n[PASS] test_reenabledPoolGetsNothingForDisabledPeriod() (gas: 1298316)\n[PASS] test_regressionThenRecoveryNeverDoubleCounts() (gas: 886341)\n[PASS] test_roundPageBoundaryAcceptsAtBoundRejectsAbove() (gas: 2623587)\n[PASS] test_settersRejectZeroAndStrangers() (gas: 163405)\n[PASS] test_simdSetAssetGuards() (gas: 179597)\n[PASS] test_singleBlockEmissionAtEveryBoundary() (gas: 2200601)\n[PASS] test_startHeightIgnoresPartialProgressAtH0() (gas: 2635454)\n[PASS] test_stateReadAcceptsOnlyExactSize() (gas: 951258)\n[PASS] test_unavailableCoreDoesNotBlockWithdrawOrClaim() (gas: 1220014)\n[PASS] test_unconfiguredSimdFarmForfeitsItsEmission() (gas: 942199)\n[PASS] test_unknownPoolIdsRevert() (gas: 145214)\n[PASS] test_weightChangeOnDisabledPoolAppliesOnEnable() (gas: 1188924)\n[PASS] test_weightZeroPoolEarnsNothing() (gas: 914999)\n[PASS] test_withdrawingDustSharesBurnsThemForNothing() (gas: 232880)\nSuite result: ok. 46 passed; 0 failed; 0 skipped; finished in 168.00ms (475.60ms CPU time)\n\nRan 15 tests for test/Allocation.t.sol:AllocationTest\n[PASS] test_addPoolRejectsFarmOfAnotherController() (gas: 73769)\n[PASS] test_addPoolRejectsForeignOrDuplicateFarms() (gas: 41070)\n[PASS] test_allocationChangesRevertWhilePausedOrCoreUnavailable() (gas: 795463)\n[PASS] test_batchRejectsDuplicatePoolIds() (gas: 52423)\n[PASS] test_batchSetPoolWeightsReallocatesAtomically() (gas: 1253851)\n[PASS] test_disableAndEnablePool() (gas: 1421653)\n[PASS] test_emissionSplitsByWeightAndReserveStaysUnminted() (gas: 2285460)\n[PASS] test_gasScalabilityDoesNotGrowWithPoolCount() (gas: 11970744)\n[PASS] test_initialAllocationIs400_200_200_200() (gas: 86004)\n[PASS] test_onlyOwnerAdministers() (gas: 190854)\n[PASS] test_onlyPoolFarmPullsRewards() (gas: 581283)\n[PASS] test_reserveAssignedLaterIsNeverRetroactive() (gas: 997081)\n[PASS] test_setPoolWeightAppliesProspectivelyAfterCheckpoint() (gas: 1022608)\n[PASS] test_treasuryAndGuardianSetters() (gas: 105151)\n[PASS] test_weightsCannotExceedTotalPoints() (gas: 235936)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 221.30ms (25.97ms CPU time)\n\nRan 5 tests for test/Token.t.sol:TokenTest\n[PASS] testFuzz_mintNeverExceedsCap(uint256,uint256) (runs: 256, μ: 140388, ~: 142578)\n[PASS] test_capBoundsMintedTotalNotSupply() (gas: 237213)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 10284)\n[PASS] test_metadataAndPremint() (gas: 96648)\n[PASS] test_onlyEmissionAuthorityMints() (gas: 139970)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 221.31ms (117.23ms CPU time)\n\nRan 32 tests for test/Emission.t.sol:EmissionTest\n[PASS] testFuzz_issuedMatchesReferenceAndIsMonotone(uint64,uint64) (runs: 256, μ: 6062, ~: 5923)\n[PASS] test_activateRevertsOnClockMismatch() (gas: 84746)\n[PASS] test_activateRevertsTwiceAndForNonOwner() (gas: 135549)\n[PASS] test_activateRevertsWhenCoreUnavailable() (gas: 76134)\n[PASS] test_activateSchedulesNextRound() (gas: 97555)\n[PASS] test_activateTreatsUndecodableConfigurationAsUnavailable() (gas: 118586)\n[PASS] test_bindTokenOnceAndOnlyMatchingAuthority() (gas: 103028)\n[PASS] test_blocksBeforeActivationNeverEmit() (gas: 2653933)\n[PASS] test_checkpointBeforeActivationIsNoop() (gas: 90644)\n[PASS] test_completionEmitsEventAndMintsAtMostAllowance() (gas: 1451547)\n[PASS] test_coreRegressionIsIgnored() (gas: 708494)\n[PASS] test_coreUnavailableIsTolerated() (gas: 742666)\n[PASS] test_coreWithoutCodeIsUnavailableNotARevert() (gas: 972987)\n[PASS] test_delayedCheckpointEqualsFrequentCheckpoints() (gas: 2456864)\n[PASS] test_fullScheduleCompletesAndStaysCapped() (gas: 745512)\n[PASS] test_guardianCanPauseOnlyOwnerUnpauses() (gas: 171320)\n[PASS] test_halvingCrossingInsideOneCheckpoint() (gas: 770863)\n[PASS] test_issuedMatchesLiveSqueekMiningVectors() (gas: 8033)\n[PASS] test_noDoubleMintingOnRepeatedCheckpoints() (gas: 833773)\n[PASS] test_noEmissionBeforeStartRoundCertified() (gas: 2423072)\n[PASS] test_oversizedConfigurationIsUnavailableAtActivation() (gas: 29535105)\n[PASS] test_oversizedRoundPageDelaysAnchorWithoutRevert() (gas: 2513958)\n[PASS] test_pausePricesBlocksCertifiedBeforeItForThoseStakedThen() (gas: 1524942)\n[PASS] test_pauseStillWorksWhenCoreIsUnavailable() (gas: 791011)\n[PASS] test_pauseStopsEmissionAndUnpauseCatchesUp() (gas: 934343)\n[PASS] test_pendingCheckpointPreview() (gas: 2668431)\n[PASS] test_perBlockRatesAcrossHalvings() (gas: 12379)\n[PASS] test_returnBombCoreDoesNotBlockExitsOrPause() (gas: 176435117)\n[PASS] test_scheduleConstantsMatchSqueekMining() (gas: 1716)\n[PASS] test_undecodableCoreStateIsUnavailableNotARevert() (gas: 1585116)\n[PASS] test_undecodableRoundPageDelaysAnchorWithoutRevert() (gas: 2534024)\n[PASS] test_zeroBlockMovementEmitsNothing() (gas: 742414)\nSuite result: ok. 32 passed; 0 failed; 0 skipped; finished in 221.38ms (308.88ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_entitlementsNeverExceedReserve\n[PASS] invariant_ethConservation\n[PASS] invariant_farmBalanceCoversPrincipal\n[PASS] invariant_sharesSumAndBacking\n[PASS] invariant_supplyAccounting\n[PASS] invariant_userActionsNeverRevertUnexpectedly\n[PASS] invariant_weightsWithinTotal\n InvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭---------------+------------+-------+---------+----------╮\n| Contract      | Selector   | Calls | Reverts | Discards |\n+=========================================================+\n| CheeseHandler | advance    | 298   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | checkpoint | 290   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | claim      | 289   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | deposit    | 287   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | setWeight  | 292   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | toggle     | 299   | 0       | 0        |\n|---------------+------------+-------+---------+----------|\n| CheeseHandler | withdraw   | 293   | 0       | 0        |\n╰---------------+------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 911.22ms (906.50ms CPU time)\n\nRan 1 test for test/SystemInvariants.t.sol:SystemInvariantTest\n[PASS]\nSystemInvariantTest invariants:\n[PASS] invariant_exitsNeverRevert\n[PASS] invariant_monotoneAndFinal\n[PASS] invariant_principalConservation\n[PASS] invariant_reservesBacked\n[PASS] invariant_rewardsOwedBoundedByReserve\n[PASS] invariant_sharesFullySubscribed\n[PASS] invariant_supplyAndMintAccounting\n SystemInvariantTest invariants (runs: 128, calls: 6144, reverts: 0)\n\n╭---------------+----------------+-------+---------+----------╮\n| Contract      | Selector       | Calls | Reverts | Discards |\n+=============================================================+\n| SystemHandler | advance        | 461   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | checkpoint     | 477   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | claim          | 462   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | depositCheese  | 472   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | depositEth     | 453   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | donateCheese   | 480   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | setWeight      | 447   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | skim           | 484   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | toggleOutage   | 497   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | togglePause    | 462   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | togglePool     | 500   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | withdrawCheese | 484   | 0       | 0        |\n|---------------+----------------+-------+---------+----------|\n| SystemHandler | withdrawEth    | 465   | 0       | 0        |\n╰---------------+----------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.35s (2.34s CPU time)\n\nRan 9 test suites in 2.35s (4.42s CPU time): 138 tests passed, 0 failed, 0 skipped (138 total tests)\n","passed":true},{"durationMs":102,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CheeseEmissionController.acceptOwnership()\",\"CheeseEmissionController.activate()\",\"CheeseEmissionController.addPool(address,uint16)\",\"CheeseEmissionController.batchSetPoolWeights(uint256[],uint16[])\",\"CheeseEmissionController.bindToken(address)\",\"CheeseEmissionController.checkpoint()\",\"CheeseEmissionController.disablePool(uint256)\",\"CheeseEmissionController.enablePool(uint256)\",\"CheeseEmissionController.pauseEmissions()\",\"CheeseEmissionController.pullRewards(uint256,bool)\",\"CheeseEmissionController.renounceOwnership()\",\"CheeseEmissionController.setGuardian(address)\",\"CheeseEmissionController.setPoolWeight(uint256,uint16)\",\"CheeseEmissionController.setTreasury(address)\",\"CheeseEmissionController.transferOwnership(address)\",\"CheeseEmissionController.unpauseEmissions()\",\"CheeseFarmERC20.acceptOwnership()\",\"CheeseFarmERC20.claim(address)\",\"CheeseFarmERC20.deposit(uint256)\",\"CheeseFarmERC20.onPoolRegistered(uint256)\",\"CheeseFarmERC20.payTreasury()\",\"CheeseFarmERC20.renounceOwnership()\",\"CheeseFarmERC20.setDepositsPaused(bool)\",\"CheeseFarmERC20.skimSurplus()\",\"CheeseFarmERC20.transferOwnership(address)\",\"CheeseFarmERC20.withdraw(uint256)\",\"CheeseFarmETH.acceptOwnership()\",\"CheeseFarmETH.claim(address)\",\"CheeseFarmETH.deposit()\",\"CheeseFarmETH.onPoolRegistered(uint256)\",\"CheeseFarmETH.payTreasury()\",\"CheeseFarmETH.receive()\",\"CheeseFarmETH.renounceOwnership()\",\"CheeseFarmETH.setDepositsPaused(bool)\",\"CheeseFarmETH.skimSurplus()\",\"CheeseFarmETH.transferOwnership(address)\",\"CheeseFarmETH.withdraw(uint256)\",\"CheeseFarmSIMD.acceptOwnership()\",\"CheeseFarmSIMD.claim(address)\",\"CheeseFarmSIMD.deposit(uint256)\",\"CheeseFarmSIMD.onPoolRegistered(uint256)\",\"CheeseFarmSIMD.payTreasury()\",\"CheeseFarmSIMD.renounceOwnership()\",\"CheeseFarmSIMD.setAsset(address)\",\"CheeseFarmSIMD.setDepositsPaused(bool)\",\"CheeseFarmSIMD.skimSurplus()\",\"CheeseFarmSIMD.transferOwnership(address)\",\"CheeseFarmSIMD.withdraw(uint256)\",\"CheeseToken.approve(address,uint256)\",\"CheeseToken.burn(uint256)\",\"CheeseToken.mintEmission(address,uint256)\",\"CheeseToken.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"CheeseToken.transfer(address,uint256)\",\"CheeseToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"README.md\":256,\"docs/ARCHITECTURE.md\":87,\"docs/SECURITY.md\":129,\"docs/SQUEEKCHAIN_VERIFICATION.md\":85,\"foundry.toml\":28,\"remappings.txt\":2,\"script/Deploy.s.sol\":112,\"script/VerifySqueekchain.s.sol\":70,\"src/CheeseEmissionController.sol\":608,\"src/CheeseFarm.sol\":375,\"src/CheeseToken.sol\":81,\"src/farms/CheeseFarmERC20.sol\":47,\"src/farms/CheeseFarmETH.sol\":46,\"src/farms/CheeseFarmSIMD.sol\":71,\"src/interfaces/ICheeseEmissionController.sol\":25,\"src/interfaces/ICheeseFarm.sol\":9,\"src/interfaces/ICheeseToken.sol\":13,\"src/interfaces/ISqueekCore.sol\":68,\"src/interfaces/ISqueekMining.sol\":36,\"src/libraries/SqueekIssuance.sol\":70,\"test/Adversarial.t.sol\":801,\"test/Allocation.t.sol\":319,\"test/CheeseBase.t.sol\":142,\"test/Deploy.t.sol\":76,\"test/Emission.t.sol\":509,\"test/Farm.t.sol\":629,\"test/FarmFuzz.t.sol\":80,\"test/Invariants.t.sol\":188,\"test/SystemInvariants.t.sol\":343,\"test/Token.t.sol\":75,\"test/mocks/Attackers.sol\":71,\"test/mocks/MockERC20.sol\":51,\"test/mocks/MockSqueekCore.sol\":100,\"web/abi/CheeseEmissionController.json\":1671,\"web/abi/CheeseFarmERC20.json\":969,\"web/abi/CheeseFarmETH.json\":970,\"web/abi/CheeseFarmSIMD.json\":1000,\"web/abi/CheeseToken.json\":750,\"web/config.sepolia.json\":49},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"f2f3661f7c702c35b05243debedb8027a2a8db3d2273e74aeacf529c1d201dfd","verifiedTreeHash":"9e88fba2a07198d84c63c69735b6a79f8bf37a6f","verifierVersion":"0.1.0+18543036"}]}