{"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":"ee10bd28-8e70-4175-89b2-445a5e137bb3","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"2a0db6c2fda8d6be4f029a10c64c14cc04108c26d187ff96cd4b60a11ec11016","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"1593d866f6e0991f2c093a980b82297d803cd7b2e88578fcdf3ed69a5decc6f5","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"9af0f782e92dafa6d1db74b38e079a9ec1a25f3c8c6846bb39f0b1390ba3105f","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"6d3968fd67f67c6aa1976b381c250dbba0278340cc4fdcebb66cb9d8d5ad0260","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"1c8b82ac113782ca664e130edd1f2121520dc0644e855ceae7c883b1fa31b1a6","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"d77c76c03b6faf451deada73682a8f1b43806cf2f5bed348ccc8f8b792571a68","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"b6503de65ad02f845827887c23da4c3b56ccc5df7a459db263bae6e549d92f7f","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"b6503de65ad02f845827887c23da4c3b56ccc5df7a459db263bae6e549d92f7f","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"0e62e57cb09a06dd7f4c9bd2141960ea91103b2a4886d6d9e09e8c912859d924","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"f7c98dfbdcfa13a5cf556857e8111c7478da465d37b1f2a280d1f24c757ab94b","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Swarmopoly v2: an onchain Monopoly-style daily game on Robinhood Chain (4663), plus a website. Currency: IMD 0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127.\\n\\nContracts, in deploy order:\\n1 SeasonPot($owner, currency): holds the pot; only the bound game moves it.\\n2 DeedVault($owner, currency, poolManager): per-tile token vaults and deeds. poolManager is the Uniswap v4 PoolManager on 4663: read poolManager() on hook 0x19bec7c2e1b2aadaf67b259744751a9960d66000. Never guess it.\\n3 SwarmopolyGame($owner, currency, $contract:DeedVault, $contract:SeasonPot).\\nPot and Vault each have a one-shot, owner-only bindGame(game).\\n\\nBoard: classic 40 squares, hardcoded; the 28 property slots start vacant. Owner listTile(slot, PoolKey, tier 1-8) fills a vacant slot; the pool must pair with the currency; a tile holding tokens can't change.\\nDice: commitRoll, then revealRoll in a later block; entropy = secret + blockhash after the commit; never prevrandao. A reveal missed past the window can be resolved by anyone as jail. Roll cooldown 20h.\\nPlayers: joinSeason pays the buy-in to the pot. Play balance in IMD, deposit/withdraw any time; rolling needs balance >= rollBond. Rent, tax and bail come from it; if rent can't be covered, pay all and be bankrupt until next season.\\nDeeds: after landing, buyDeed(tile, amountIn <= maxBuy, minOut) swaps exact input via PoolManager.unlock and mints pro-rata shares. Internal token accounting only, no balanceOf. Non-transferable. Optional lock: 30d = 1.5x weight, 90d = 2x. redeem after 24h and any lock.\\nRent: tierRent (defaults 1,2,3,5,8,12,20,30 IMD). 80% to holders by weight through an accumulator, 20% to the pot; holders claimRent.\\nSquares: GO salary = pot x 10 bps, once per roll. Tax to pot. Jail: skip a roll or pay bail. Free parking: 50 bps of pot, once a day. Chance from entropy: GO, jail, +5 bps of pot, tax, or nothing.\\nSponsors: sponsorTile(tile, amount, perLanding) in the tile's token, paid to landers; remainder withdrawable after the season.\\nSeasons: owner startSeason(buyIn, duration, rollBond, maxBuy). Score = salary + parking + chance + rent claimed; keep a top 10 on each update. After the end anyone finalizes: 60% of pot to the top 10 (25/18/13/10/8/7/6/5/4/4%), pull claims; 40% carries over.\\nOwner can only: bindGame, listTile, startSeason, set params within caps (salaryBps <= 50, tierRent <= 1000 IMD, cooldown 1-48h), pause rolls and buys. Never touches user funds; withdraw, redeem and claim work while paused.\\nSecurity: reentrancy guards, SafeERC20, no upgrades, no ETH. foundry.toml: bytecode_hash none, cbor_metadata false.\\nTests: mock v4 swap; invariants for vault accounting, rent paid >= rent credited, pot solvency, unrevealed rolls never better.\\nSite: Swarmopoly v1 look (IMD job 55ae5ec7). Wallet, add chain 4663, live board (tiles, vacant lots 'Launch Day soon', players), commit then auto-reveal roll with animation, buy with quote, my balance/deeds/rent, leaderboard, owner admin panel.\",\n    \"context\": \"Robinhood Chain 4663, evm_contracts only, no token. GitHub and IPFS approved, site label swarmopoly. Mainnet tiles, oracle pricing and monopoly bonuses are out of scope (season 2).\",\n    \"draft\": {\n      \"objective\": \"Build SeasonPot, DeedVault and SwarmopolyGame with tests and a review, deploy on Robinhood Chain, then build the Swarmopoly site against the deployment.\",\n      \"shape\": \"dag\",\n      \"onchain\": \"evm_contracts\",\n      \"chainId\": 4663,\n      \"github\": true,\n      \"ipfs\": \"swarmopoly\",\n      \"contracts\": [\n        \"SeasonPot\",\n        \"DeedVault\",\n        \"SwarmopolyGame\"\n      ],\n      \"references\": [\n        \"defi-native\",\n        \"solidity-security-review\",\n        \"uniswap-v4-security\"\n      ],\n      \"steps\": [\n        {\n          \"skill\": \"build-contract-project\",\n          \"key\": \"contracts\",\n          \"dependsOn\": []\n        },\n        {\n          \"skill\": \"write-foundry-tests\",\n          \"key\": \"tests\",\n          \"dependsOn\": [\n            \"contracts\"\n          ],\n          \"paths\": [\n            \"test\"\n          ]\n        },\n        {\n          \"skill\": \"adversarial-review\",\n          \"key\": \"review\",\n          \"dependsOn\": [\n            \"tests\"\n          ]\n        },\n        {\n          \"skill\": \"frontend-for-contract\",\n          \"key\": \"site\",\n          \"dependsOn\": [\n            \"review\"\n          ]\n        }\n      ]\n    },\n    \"permissions\": {\n      \"github\": true,\n      \"ipfs\": \"swarmopoly\",\n      \"onchain\": {\n        \"kind\": \"evm_contracts\",\n        \"chainId\": 4663","parentJobId":null,"planHash":"39579f1891f1e8e720e5d2b3fcbb31ab472897111dc82f9e9ffb4221cde97e42","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"ee10bd28-8e70-4175-89b2-445a5e137bb3","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-895-swarmopoly-v2-onchain-monopoly-style"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"50981","feedbackHash":"ceeba4018dfdb1f19a2c3ccdb78e31d6b012abc18572a926e9d45f7e931305b4","nodeKey":"audit_economics","submissionHash":"2a0db6c2fda8d6be4f029a10c64c14cc04108c26d187ff96cd4b60a11ec11016","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52082","feedbackHash":"824bc9f4b949ca4ecc3bc3cf99112553f926bcd8838471697e4666be5cddb5e8","nodeKey":"audit_flow","submissionHash":"1593d866f6e0991f2c093a980b82297d803cd7b2e88578fcdf3ed69a5decc6f5","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51494","feedbackHash":"7f62fd28ba704b399fb02daf5792b4c762754e79202728e6989afd6cf2c8a116","nodeKey":"audit_judge","submissionHash":"cf8762353ef81c0adfbf6a8f9a2b57da5632c303e181b6621299407f37daa2c8","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52263","feedbackHash":"97a8a75c8978310c748863fe26f545eecdfe1cb04b2bc0f230d3c24b4ce3ab98","nodeKey":"audit_judge","submissionHash":"9af0f782e92dafa6d1db74b38e079a9ec1a25f3c8c6846bb39f0b1390ba3105f","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50959","feedbackHash":"2405a1576f691cfed51e99a42d6a281dc9bca5ec6e8939a28a60e6894aa673c7","nodeKey":"audit_math","submissionHash":"6d3968fd67f67c6aa1976b381c250dbba0278340cc4fdcebb66cb9d8d5ad0260","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51504","feedbackHash":"ac1ca01d79991429a7803df87cc6d83406f1233c1b4ea68ab38f47bb287d6542","nodeKey":"audit_permissions","submissionHash":"1c8b82ac113782ca664e130edd1f2121520dc0644e855ceae7c883b1fa31b1a6","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51302","feedbackHash":"9e081788bbdf5f2286d19f0c5a9243c11c68dd5b5279303f3d7d7e104c044238","nodeKey":"build_contract_project","submissionHash":"d77c76c03b6faf451deada73682a8f1b43806cf2f5bed348ccc8f8b792571a68","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51013","feedbackHash":"edde7d7739957cf10c2954f17ab97ef0e7c54824645f6f7048c55455516d49b3","nodeKey":"build_contract_project","submissionHash":"d1a55f5ac2c077677b32dbca20311c2fa2045452f3c269a60e54ef4074c2ee5b","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51061","feedbackHash":"fc4e2f97eecb8f48324f8220beb2cb1668b825f26fd8458ddc3c519136768721","nodeKey":"manifest","submissionHash":"0e62e57cb09a06dd7f4c9bd2141960ea91103b2a4886d6d9e09e8c912859d924","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51266","feedbackHash":"e5aed28d78cd5b0eb92c9b4a6390b0d9aa8687270ea5b2e19a3dedd3ecff2a32","nodeKey":"manifest","submissionHash":"6d3092bfce8b31685ed2625f7ff14f1d6cab573d71bcc3eecf3e580d29d983e7","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51080","feedbackHash":"738ee46c662a8c21dfd7a8fa324a8afd8b1509b751ccc85ea497c69a52fd0ed1","nodeKey":"write_foundry_tests","submissionHash":"d3fb034cbcfbce8728e12df1e1a9dac0dc95ff4c39c91f197001d4b5a2105ea8","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51167","feedbackHash":"d124b953561b7a8a4f953c242906727cbba396f71b4f9fd79b9ab8350904528d","nodeKey":"write_foundry_tests","submissionHash":"f7c98dfbdcfa13a5cf556857e8111c7478da465d37b1f2a280d1f24c757ab94b","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"b87dad7b4105d675a8b2ee83305fca0de1e576900d76ffcfaed24bc471633f88","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"84c048c9b9842c56fcbf6d31079f513dafa365521ffad90d3652b4f1de2ca503","device":"269d07167a05c205","findings":[],"hash":"0e62e57cb09a06dd7f4c9bd2141960ea91103b2a4886d6d9e09e8c912859d924","nodeId":"a7edd990-0d80-4ae0-9d68-a2a773ea7ab8","outcome":"completed","summary":"Updated [launch.json](/root/.identitymd/work/ee10bd28-8e70-4175-89b2-445a5e137bb3/a7edd990-0d80-4ae0-9d68-a2a773ea7ab8/launch.json) with fresh PoolManager verification evidence. Constructor arguments remain correct.\n\nSchema/ABI checks, `forge build`, and all 41 tests passed, including the protected deployment check. Only `launch.json` changed outside scratch space.","treeHash":"3159f46cb49175e678ca552b8eb0226723f97b24","usage":{"cachedInputTokens":373888,"inputTokens":61112,"model":"gpt-6-astra","outputTokens":6602,"runtime":"codex","turns":4,"wallClockMs":212045}},{"artifacts":[],"attempt":3,"bundleHash":null,"device":"077d2937780a81bc","findings":[{"citation":"resolved","description":"joinSeason inserts the joiner into _leaders[currentSeason] with score 0. finalizeSeason (line 442-448) pays every non-zero address in that list its rank share of 60% of pot.available(), and only skips address(0): `if (winner == address(0)) continue;` (line 444). Nothing requires a winner to have scored. The pot carries 40% between seasons plus rents, taxes and buy-ins, so whenever fewer than ten accounts hold a positive score the remaining ranks are filled by whoever merely joined, ordered by lowest address (_better tie-break, line 432). With buyIn == 0 (allowed by startSeason) an attacker can join from any number of fresh addresses at gas cost only and collect the empty ranks; with buyIn > 0 it is profitable whenever a rank's share (as little as 2.4% of the pot for rank 10, up to 15% for rank 1) exceeds the buy-in, and vanity-low addresses beat every other zero-score joiner. The money leaves the pot that funds future salaries, parking and prizes for real players. Minimal fix preserving the design: do not insert into the leaderboard when the resulting score is 0 (skip _score in joinSeason, or return early in _score when score == 0 and the address is not already listed), and skip zero-score entries in finalizeSeason so their share carries over as the author's review already describes for unoccupied ranks.","line":173,"path":"src/SwarmopolyGame.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\nimport {Test} from \"forge-std/Test.sol\";\nimport {SeasonPot} from \"src/SeasonPot.sol\";\nimport {DeedVault} from \"src/DeedVault.sol\";\nimport {SwarmopolyGame} from \"src/SwarmopolyGame.sol\";\n\ncontract Cash {\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n    function mint(address to, uint256 a) external { balanceOf[to] += a; }\n    function approve(address s, uint256 a) external returns (bool) { allowance[msg.sender][s] = a; return true; }\n    function transfer(address to, uint256 a) external returns (bool) { balanceOf[msg.sender] -= a; balanceOf[to] += a; return true; }\n    function transferFrom(address f, address to, uint256 a) external returns (bool) {\n        allowance[f][msg.sender] -= a; balanceOf[f] -= a; balanceOf[to] += a; return true;\n    }\n}\n\ncontract ZeroScorePrizeTest is Test {\n    SeasonPot pot; DeedVault vault; SwarmopolyGame game; Cash cash;\n\n    function setUp() public {\n        vm.warp(10 days);\n        cash = new Cash();\n        pot = new SeasonPot(address(this), address(cash));\n        vault = new DeedVault(address(this), address(cash), address(0xBEEF));\n        game = new SwarmopolyGame(address(this), address(cash), address(vault), address(pot));\n        pot.bindGame(address(game));\n        vault.bindGame(address(game));\n        // Pot carries value from earlier play (rents, taxes, buy-ins, carry-over).\n        cash.mint(address(this), 1000 ether);\n        cash.approve(address(game), type(uint256).max);\n        game.startSeason(0, 1 days, 1 ether, 1 ether);\n        game.fundPot(1000 ether);\n    }\n\n    function testJoinersWithZeroScoreArePaidPrizes() public {\n        address[10] memory sybils;\n        for (uint160 i; i < 10; ++i) {\n            sybils[i] = address(uint160(0x1000 + i));\n            vm.prank(sybils[i]);\n            game.joinSeason(); // buy-in 0: no funds, no deposit, no roll\n        }\n        vm.warp(block.timestamp + 1 days);\n        game.finalizeSeason();\n        uint256 total;\n        for (uint256 i; i < 10; ++i) {\n            uint256 prize = game.prizes(1, sybils[i]);\n            assertEq(game.scores(1, sybils[i]), 0);\n            total += prize;\n            if (prize != 0) {\n                vm.prank(sybils[i]);\n                game.claimPrize(1); // pull claim succeeds: the money really leaves the pot\n            }\n        }\n        // Expected: zero-score accounts receive nothing. Actual: 60% of the pot leaves to them.\n        assertEq(total, 0, \"zero-score joiners were paid 60% of the pot\");\n    }\n}","reproduction":"Deploy pot/vault/game, bind, startSeason(buyIn=0, duration=1 days, rollBond=1e18, maxBuy=1e18), fundPot(1000e18). From ten fresh addresses 0x1000..0x1009 call joinSeason() and nothing else (no deposit, no roll). Warp past end, call finalizeSeason(). Expected: no prize for accounts with scores[1][addr] == 0, 600e18 stays in pot.available() as carry-over. Actual: prizes[1][...] sum to 600e18 (25/18/13/10/8/7/6/5/4/4% of 600e18), each claimable via claimPrize(1); pot.available() drops to 400e18. The attached test fails on the current code with `600000000000000000000 != 0`.","severity":"high","snippet":"        _score(msg.sender, 0);","title":"joinSeason ranks zero-score accounts, so finalizeSeason pays prize money to addresses that never played"},{"citation":"resolved","description":"The brief says a reveal missed past the window 'can be resolved by anyone as jail'. expireRoll additionally sets bankrupt (ending the player's season) and moves r.exposure (max(TAX, max tierRent) capped at balance, 30 IMD at defaults) to the pot via _toPot. The author documents this as the mechanism that keeps 'unrevealed rolls never better' true, and the trace confirms the penalty is always at least the worst reveal outcome, so this is a deliberate, defensible deviation rather than a bug; it is recorded so the requester can confirm the stricter rule is accepted (a jail-only resolution would make withholding a bad roll strictly profitable once rent exceeds the cost of a lost turn).","line":259,"path":"src/SwarmopolyGame.sol","reproduction":"Player with balance 100 IMD commits a roll and never reveals. After the 200-block / 1-hour window anyone calls expireRoll(player). Brief-literal expectation: player is jailed. Actual: position=10, jailed=true, bankrupt=true (no further commit/buy/bail this season), balance 70 IMD, 30 IMD credited to the pot.","severity":"info","snippet":"        p.bankrupt = true;","title":"Missed reveal is resolved as forfeiture plus season bankruptcy, not only jail as the brief states"}],"hash":"1593d866f6e0991f2c093a980b82297d803cd7b2e88578fcdf3ed69a5decc6f5","nodeId":"3dabb100-b346-42d4-8cb4-176258ec2da6","outcome":"completed","summary":"The review is complete. Findings are in `.imd-findings.json` at the repository root, with 2 findings and 39 coverage rows (all 36 listed entry points plus 3 invariants). No source files were changed; the only extra file is the scratch proof under `test/scratch/`, which is gitignored.\n\n**What I found**\n\n- **High: zero-score joiners are paid prize money.** `joinSeason` inserts the caller into the leaderboard with score 0 and `finalizeSeason` pays every listed address. Ten fresh accounts that only called joinSeason under a zero buy-in received the full 60% prize budget (600 of 1000 IMD) and could claim it. With a positive buy-in it stays profitable whenever a rank share exceeds the buy-in. The embedded Foundry proof fails on the current code and passes once zero-score entries are excluded.\n- **Info: missed-reveal penalty exceeds the brief's wording.** The brief says an expired roll is \"resolved as jail\"; the code also bankrupts the player for the season and forfeits the reserved exposure. The author documents this as the mechanism that keeps the \"unrevealed never better\" invariant true, so I recorded it for confirmation rather than as a bug.\n\n**What I verified**\n\n- Full suite builds and passes (29 tests, including invariants).\n- Robinhood Chain RPC: the hook's `poolManager()` returns the address in launch.json. The EVM `block.number` is the Ethereum L1 height (ArbOS 116), so the 200-block reveal window is about 40 minutes and sits inside the 1-hour deadline. `blockhash` is nonzero within 256 blocks and is L2-derived, so the commit-reveal path works as designed.\n- Traced the balance-versus-exposure invariant across every balance-reducing path, the v4 unlock/swap/settle/take sequence and delta decoding, the rent accumulator and season baselines, the leaderboard insertion, and the pause and wrong-state behavior of each entry point. All held.\n\n**Not covered**: the site beyond the commit/reveal flow, and the live hook's swap behavior against a real pool, which no local test can reach.","treeHash":null,"usage":{"cachedInputTokens":1488403,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":58023,"runtime":"claude","turns":23,"wallClockMs":847834}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"0238a59bba722237","findings":[{"citation":"resolved","description":"Area: access x asymmetry (Trust Gap seam 3 with an unprivileged actor). sponsorTile is permissionless and first-come: the first caller for (season, slot) becomes the only address allowed to fund that tile until seasonEnd, and even that address is pinned to its first perLanding. There is no minimum amount, no minimum perLanding, no way for the owner or a later sponsor to displace a dust sponsorship, and the squatter recovers the dust via withdrawSponsor after the season. A griefer therefore blocks the sponsorship feature for all 28 tiles for the whole season for 28 wei of tile tokens plus gas, and landers receive 1 wei 'awards'. The brief asks for sponsorTile(tile, amount, perLanding) paid to landers; RULES.md documents one sponsor per tile per season for bounded landing cost but not that any address can lock the slot at zero cost. No funds are lost; the feature is denied. Minimal fix preserving the design: let a new sponsor replace an existing one when the existing remaining is below some floor, or require amount >= perLanding and a minimum perLanding, or allow the existing sponsor to be displaced after remaining hits zero. The existing tests never try a second sponsor on the same tile.","line":281,"path":"src/DeedVault.sol","reproduction":"State: season 1 active, tile 6 listed. (1) mallory: token.approve(vault, 1); vault.sponsorTile(6, 1, 1) succeeds. (2) alice (a real sponsor holding 1000e18 tokens): vault.sponsorTile(6, 1000e18, 10e18) reverts Invalid() because s.sponsor is mallory. Expected: a real sponsorship can be placed, or at least the dust one can be displaced. Actual: tile 6 cannot be sponsored by anyone but mallory, at perLanding 1 wei, until seasonEnd[1]. (3) After warp past seasonEnd, mallory: vault.withdrawSponsor(1, 6) returns the 1 wei. Verified with a scratch Foundry test (testSponsorSlotSquatting) on the current tree.","severity":"low","snippet":"        if (s.sponsor != address(0) && (s.sponsor != msg.sender || s.perLanding != perLanding)) revert Invalid();","title":"Anyone can squat a tile's single sponsor slot for a whole season with 1 wei"},{"citation":"resolved","description":"Area: asymmetry (storage-write symmetry between commitRoll/skipJailRoll, which set the cooldown, and joinSeason, which clears it). The brief fixes the roll cooldown at 20h (owner-settable 1-48h) and the comment two lines below says nonce and parkingDay persist 'to prevent daily/replay resets at season boundaries', yet nextRoll is zeroed on every join. A player who commits in the last allowed hour of season N (end - 1h - 1s) and reveals, then joins season N+1 as soon as the owner starts it, can commit again within about an hour of the previous roll, which is under the cooldown the owner configured. Every player gets one extra roll per season boundary, so a player who times the last roll gets an extra salary/parking/chance chance that players who rolled earlier in the day do not. Impact is one roll per season (bounded, no fund loss), hence low. Fix: do not touch p.nextRoll in joinSeason (keep the timestamp; it is harmless when already in the past), or set it to max(p.nextRoll, 0). The existing tests do not roll in consecutive seasons within one cooldown.","line":162,"path":"src/SwarmopolyGame.sol","reproduction":"State: season 1 started with duration 30d, alice joined, cooldown 20h. (1) warp to seasons[1].end - 3601; alice commitRoll + revealRoll; player(alice).nextRoll == end - 3601 + 20h. (2) warp to end; anyone finalizeSeason(); owner startSeason(0, 2 days, 1e18, 100e18). (3) alice joinSeason(); player(alice).nextRoll is now 0. (4) alice commitRoll(validCommitment) at timestamp end succeeds although block.timestamp < the nextRoll recorded in step 1 (only 3601s after the previous commit). Expected: commitRoll reverts TooEarly until the 20h cooldown elapses. Actual: it succeeds. Verified with a scratch Foundry test (testCooldownResetOnJoin) on the current tree.","severity":"low","snippet":"        p.nextRoll = 0;","title":"joinSeason resets nextRoll, so the roll cooldown is skipped across a season boundary"}],"hash":"1c8b82ac113782ca664e130edd1f2121520dc0644e855ceae7c883b1fa31b1a6","nodeId":"e7ef719d-a7ad-49e7-8e9b-2cf5ba8a7c84","outcome":"completed","summary":"Review complete. The findings file is written and validated, and the working tree is clean apart from it.\n\n**Outcome.** The permission model of SeasonPot, DeedVault and SwarmopolyGame holds. Every privileged function is owner-only or game-only, both bindings are one-shot, no owner path touches user funds, and the swap callback authenticates the PoolManager plus a single-use context. On-chain checks confirmed the PoolManager in launch.json matches the hook's `poolManager()` read, and the IMD token exposes no pause, blacklist, mint or proxy selectors.\n\n**Findings (both low, no fund loss):**\n\n- **Sponsor slot squatting** in `DeedVault.sponsorTile`. The slot is first-come with no minimum, so anyone can lock every tile's sponsorship for the season with 1 wei of tile token and reclaim it afterwards. Reproduced with a scratch test.\n- **Cooldown reset on join** in `SwarmopolyGame.joinSeason`. The function zeroes `nextRoll`, so a player who rolls in the last allowed hour of a season can roll again right after joining the next one. Reproduced with a scratch test.\n\n**Coverage.** All 36 listed entry points have a row. Two are marked `finding`, the rest `holds` with a one-line trace reason, plus three invariant rows for owner custody, mixed-guard storage writers and callback reentrancy. Leads I checked and rejected include expire-roll underflow, reveal/expire window overlap, claim-rent scoring across the season boundary, and confused-deputy approval spending.\n\n**Out of my area, noted only.** Blockhash predictability under a sequencer and Sybil scoring are documented trust assumptions, not defects in access control.","treeHash":null,"usage":{"cachedInputTokens":1519166,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":57186,"runtime":"claude","turns":34,"wallClockMs":852194}},{"artifacts":[],"attempt":2,"bundleHash":null,"device":"5739ce0d803a43cd","findings":[{"citation":"resolved","description":"Every reward (GO salary 10 bps, parking 50 bps per player per UTC day, chance 5 bps) is a percentage of pot.available(), whereas the cost of an account is fixed (buyIn once, balance >= rollBond at commit, line 207) and the worst case a roll can cost is min(balance, max rent or 5 IMD tax) (line 302). A player therefore never risks more than the bond they choose to hold, while the upside is proportional to everyone else's money. Expected gain per roll is about 3.2 bps of the pot (P(pass GO)~0.175 x 10 bps + P(land 20)~0.025 x 50 bps + chance ~0.15 bps). With the 20h cooldown that is ~36 rolls per 30-day season, i.e. about 1.15% of the pot per account per season, whereas the suggested parameters in docs/DEPLOYMENT.md cost 10 IMD buy-in plus a 1 IMD bond. While the 28 lots are vacant (the site brief expects 'Launch Day soon' lots at launch) there is no rent, so the only loss is tax capped at the 1 IMD balance and bankruptcy is impossible: break-even pot is roughly 1,100 IMD, and every additional Sybil account above that is profitable and drains the prize pool of honest players (100 accounts take ~(1-(1-0.00032)^3600) ~ 68% of the pot over a season). Once tiles are listed the bond is at risk on ~70% of rolls (expected rolls before bankruptcy ~1.4), which raises break-even to roughly 24,000 IMD, but the pot carries 40% forward across seasons so that level is reachable. docs/REVIEW.md acknowledges 'no Sybil defense' but no parameter guidance ties buyIn/rollBond to pot size, and startSeason accepts buyIn = 0. Design-preserving mitigations: cap each per-roll reward at k x rollBond (or at the player's play balance), require buyIn to be at least a fraction of pot.available() at join time, or make rollBond a genuine at-risk amount (e.g. forfeited on bankruptcy). This is the pot's main economic exposure and the owner currently has no lever that scales with the pot.","line":343,"path":"src/SwarmopolyGame.sol","reproduction":"State: fresh season (listTile(9,key,8) only; tiles otherwise vacant), startSeason(10e18, 30 days, 1e18, 100e18), pot funded to 10,010 IMD via fundPot. Alice joins (pays 10 IMD), deposits exactly 1 IMD (= rollBond), rolls 10 then 10 (position 20 = free parking): _reward(who, 50) pays 50 bps of 10,020 IMD = 50.1 IMD into her play balance, withdrawable immediately (test logs balance 51.05 IMD). Bob joins with the same 1 IMD balance and lands on tile 9 (tier 8, rent 30 IMD): pays 1 IMD, bankrupt (balance 0). Expected: a roll should not offer pot-scaled upside against a bond-capped downside. Actual: max loss per account is the 1 IMD bond, upside is 0.1-0.5% of the whole pot per roll, so repeating with N addresses is profitable above ~1,100 IMD pot (vacant board) at the suggested parameters. Scratch test: test/scratch/Leads.t.sol::testPotProportionalUpsideCappedDownside.","severity":"medium","snippet":"        uint256 amount = Math.mulDiv(pot.available(), bps, 10_000);","title":"Per-roll rewards scale with the pot while entry cost is flat and downside is capped at balance, so Sybil rolling becomes +EV once the pot is modest"},{"citation":"resolved","description":"joinSeason calls _score(msg.sender, 0) (line 173), inserting every joiner into the top ten while there is room, and _better() breaks equal scores by lower numeric address (line 432). finalizeSeason only skips empty slots, so a joiner with score 0 and a low address is paid 25/18/13/... percent of the 60% budget ahead of real players with score 0 and even evicts them. Because 40% of each pot carries over, a quiet season following an active one (or any season where fewer than ten players score) can be farmed by joining with cheap low-address accounts (vanity addresses with leading zero bytes are trivial to generate). Suggested fix preserving the brief ('Score = salary + parking + chance + rent claimed'): skip leaders with scores[season][winner] == 0 in finalizeSeason (and/or do not insert zero-score joiners), leaving unpaid weights in the pot as already done for empty slots.","line":444,"path":"src/SwarmopolyGame.sol","reproduction":"State: startSeason(10e18, 30 days, 1e18, 100e18); fundPot(1000e18) models carry-over. alice (0xA11CE) and bob (0xB0B) join; attacker joins with addresses 0x1..0x8 (8 x 10 IMD = 80 IMD). Nobody rolls. warp to end; finalizeSeason(). Expected: prizes go to players who scored, zero-score Sybils should not be paid. Actual: addresses 0x1..0x8 hold ranks 1-8 and receive 607.2 IMD of the 660 IMD budget (92%), alice and bob get 26.4 IMD each; the attacker nets +527 IMD for 80 IMD. Scratch test: test/scratch/Leads.t.sol::testZeroScoreLowAddressSybilsTakePrizes.","severity":"low","snippet":"            if (winner == address(0)) continue;","title":"Zero-score joiners occupy prize ranks and the ascending-address tie-break lets low vanity addresses capture 92% of the prize budget in a sparse season"},{"citation":"resolved","description":"The holder share is switched on by weight != 0 regardless of how much value backs that weight, and DeedVault.buy accepts any amount whose swap output is >= 1 unit (lines 139-141). The first buyer on a tile, with 1 wei of IMD, therefore receives the entire 80% holder share of every subsequent rent payment until someone else buys, and the pot (which funds every player's salary and prizes) drops from 100% to 20% of that tile's rent. There is no minimum purchase, no owner parameter for one, and the brief only bounds amountIn from above (maxBuy). Rational play is to dust-buy every tile landed on, which hollows out the pot. Suggested fix: add a minBuy season parameter (checked in _buy next to maxBuy) or require out/shares >= a per-tile minimum.","line":308,"path":"src/SwarmopolyGame.sol","reproduction":"State: listTile(6,key,3); startSeason(10e18, 30 days, 1e18, 100e18); alice and bob join with 100 IMD play balance. alice rolls 6, lands on tile 6 and calls buyDeed(6, 1, 1): the swap returns 2 token units, deed shares = 2, weight = 4. bob rolls 6 and pays tier-3 rent of 3 IMD. Expected: a negligible stake should not materially divert pot income. Actual: vault.pendingRent(alice) = 2.4 IMD and the pot receives only 0.6 IMD of bob's 3 IMD (before alice's deed the same landing sent 3 IMD to the pot). Scratch test: test/scratch/Leads.t.sol::testDustDeedCapturesRent (logs 'alice pending 2400000000000000000').","severity":"low","snippet":"            uint256 holders = weight == 0 ? 0 : Math.mulDiv(paid, 80, 100);","title":"A dust deed (1 wei of IMD) redirects 80% of all rent on a tile from the pot to its holder"},{"citation":"resolved","description":"sponsorTile allows exactly one sponsor address per (season, tile) and rejects anyone else, with no minimum amount or perLanding. A griefer can call sponsorTile(slot, 1, 1) on every listed tile at season start for 1 wei of each tile token plus gas, after which no genuine sponsor can fund any tile until the next season; the griefer's 1 wei is awarded on the first landing and the slot stays dead (remaining == 0, sponsor set). Suggested fix preserving the O(1) landing path: let a new sponsor take over when remaining == 0, or let any address top up as long as perLanding matches, or require a minimum amount >= perLanding x some count.","line":281,"path":"src/DeedVault.sol","reproduction":"State: listTile(6,key,3); startSeason(10e18, 30 days, 1e18, 100e18). Griefer (0x6) calls token.approve(vault, 1) then vault.sponsorTile(6, 1, 1). alice approves 100e18 and calls vault.sponsorTile(6, 100e18, 1e18). Expected: a real sponsorship should be acceptable. Actual: revert Invalid() because s.sponsor != msg.sender; tile 6 cannot be sponsored by anyone else for season 1. Scratch test: test/scratch/Leads.t.sol::testSponsorSlotGriefing.","severity":"low","snippet":"        if (s.sponsor != address(0) && (s.sponsor != msg.sender || s.perLanding != perLanding)) revert Invalid();","title":"One-wei sponsorship locks a tile's single sponsor slot for the whole season"},{"citation":"resolved","description":"The 20h (owner-configurable 1-48h) cooldown is a per-player timer stored in nextRoll, but joinSeason unconditionally zeroes it. A player who commits at the last permitted moment of season N (end - 1h - 1s) can reveal, then join season N+1 as soon as the owner starts it and commit again immediately. With back-to-back seasons this yields two full rolls (two chances at salary/parking/chance rewards, and a second parking payout if the UTC day flipped since parkingDay is keyed by day, line 277) about one hour apart. docs/RULES.md lists what joining resets and says nonces and parking day persist, but does not mention the cooldown. Suggested fix: do not touch nextRoll in joinSeason (or set it to max(nextRoll, 0)).","line":162,"path":"src/SwarmopolyGame.sol","reproduction":"State: startSeason(0, 1 days, 1e18, 100e18); alice joins with 100 IMD. warp to end - 3601; alice commits and reveals (nextRoll = commit + 20h). warp to end; finalizeSeason(); owner startSeason(0, 1 days, 1e18, 100e18); alice joinSeason() -> player(alice).nextRoll == 0; alice commitRoll succeeds. Expected: TooEarly until commit + 20h. Actual: second commit accepted 3,601 seconds after the first. Scratch test: test/scratch/Leads.t.sol::testCooldownResetAcrossSeasons (logs 'seconds between commits: 3601').","severity":"low","snippet":"        p.nextRoll = 0;","title":"joinSeason resets nextRoll to zero, bypassing the roll cooldown across a season boundary"},{"citation":"resolved","description":"The 1.5x/2x weight is compensation for forgoing redemption for 30/90 days, but the weight never decays: after redeemAt passes the lot is fully redeemable at any time yet keeps its boosted share of rent. Since there is no cost to keeping the lot and the boost is permanent, the dominant strategy is always to pick the 90-day lock, and early lockers hold a permanent structural advantage over later entrants that the brief's wording ('Optional lock: 30d = 1.5x weight, 90d = 2x') does not clearly intend. docs/RULES.md documents the behaviour as a choice, so this is reported for the author's confirmation rather than as a defect. If undesired, re-weight to 2x shares on first _accrue after redeemAt (updating t.weight) or require re-locking.","line":141,"path":"src/DeedVault.sol","reproduction":"State: listTile(6,key,3); 365-day season; alice buys 10 IMD on tile 6 with lockDays = 90 (weight 80 units); warp 91 days (lock matured, redeemable); bob buys 10 IMD unlocked (weight 40 units); charlie lands and pays 3 IMD rent. Expected (if boost is tied to being locked): equal 1.2 IMD each. Actual: alice pending 1.6 IMD, bob 0.8 IMD. Scratch test: test/scratch/Leads.t.sol::testMaturedLockKeepsWeight.","severity":"info","snippet":"        uint256 weight = shares * (lockDays == 90 ? 4 : lockDays == 30 ? 3 : 2);","title":"Lock weight boost persists after the lock matures, so a liquid matured 90-day lot earns 2x against new unlocked buyers forever"},{"citation":"resolved","description":"Robinhood Chain (4663) is an Arbitrum Orbit/Nitro chain, as the code's own chainBlockNumber() comment acknowledges. On Nitro, block.number is the parent-chain block number and blockhash(x) is served from ArbOS's L1-block-hash table, which (in ArbOS's ApplyInternalTxStartBlock -> Blockhashes.RecordNewL1Block) stores, for each new L1 number, the hash of the immediately preceding L2 block (and keccak-derived fills for skipped numbers). So the entropy for a commit made in the last L2 block carrying L1 number N is the hash of that very L2 block. On a quiet chain where the committer's transaction is alone in that block, the block hash is a function of state the committer controls (their commitment, gas, calldata) plus a sequencer-chosen timestamp, so a well-timed submission near an L1 boundary lets the committer grind secret/commitment pairs offline against a predicted block hash and bias the dice toward GO/parking. This could not be reproduced in Foundry (Ethereum semantics) and depends on Nitro internals I could not verify offline, so it is recorded as a lead, not a finding; docs/REVIEW.md already treats sequencer influence as an accepted residual. If the author wants to reduce it, use an entropy block further ahead (e.g. +2) so that at least one L1 boundary not adjacent to the commit is involved, or mix in the hashes of several blocks.","line":236,"path":"src/SwarmopolyGame.sol","reproduction":"Not reproducible in the local EVM. Concrete state to check on chain: send commitRoll in an otherwise empty L2 block whose L1 number is N, immediately before the sequencer observes L1 block N+1; after reveal, compare blockhash(N+1) as seen by the contract with the hash of the L2 block that contained the commit. If they are equal, the committer's own block determined the entropy.","severity":"info","snippet":"        bytes32 futureHash = blockhash(r.entropyBlock);","title":"Unverified lead: on Arbitrum Nitro the 'future blockhash' of L1 block N+1 is the hash of the last L2 block before the L1 number advanced, which can be the committer's own block"}],"hash":"2a0db6c2fda8d6be4f029a10c64c14cc04108c26d187ff96cd4b60a11ec11016","nodeId":"c9b7eb3c-4100-4bdf-94ed-13c1b0483c2b","outcome":"completed","summary":"The review is complete. Findings are written to `.imd-findings.json` at the repository root, no tracked file was changed, and the full suite including my scratch tests passes (38 of 38).\n\n**Outcome.** The core accounting is sound. I traced pot solvency, vault custody, the rent accumulator, the commit/reveal exposure scheme and the leaderboard, and found no fund-loss bug. The defects are in the economic design, where the game's own mechanisms can be turned against the pot. Seven findings, one medium, four low, two info, each with a reproduction run in `test/scratch/Leads.t.sol`.\n\n**Findings, most severe first:**\n- **Medium, pot-proportional rewards versus flat entry cost.** Salary, parking and chance pay a percentage of the pot, while an account's downside is capped at the balance it chooses to hold. At the suggested parameters on a vacant board, a Sybil account becomes profitable once the pot passes roughly 1,100 IMD and takes about 1.15% of the pot per season. Reproduced with a 1 IMD balance collecting 50.1 IMD from parking while a 1 IMD balance on a tier-8 tile loses only 1 IMD.\n- **Low, zero-score joiners are paid prizes.** The ascending-address tie-break lets eight low addresses collect 607 IMD of a 660 IMD budget for 80 IMD of buy-ins in a sparse season.\n- **Low, dust deed captures 80% of tile rent.** A 1 wei purchase redirected 2.4 IMD of the next 3 IMD rent from the pot to the dust holder.\n- **Low, 1 wei sponsorship locks a tile's sponsor slot** for the whole season.\n- **Low, cooldown bypass at season boundaries.** Joining resets the roll timer, so two rolls were accepted 3,601 seconds apart.\n- **Info, lock boosts never decay** after maturity, so a matured 90-day lot earns double against new unlocked buyers while fully liquid.\n- **Info, unverified lead on Nitro blockhash semantics.** The future blockhash of parent-chain block N+1 may be the hash of the committer's own L2 block. This cannot be reproduced in Foundry and is recorded as a lead only.\n\n**Coverage.** All 36 listed entry points have rows, plus four invariant rows. None are unreached. The invariant and flow-gap guides were fully applied. Pool-level behaviour on the live PoolManager and the IMD token's transfer semantics were outside what I could verify offline and remain trust assumptions, as the repository's own review states.","treeHash":null,"usage":{"cachedInputTokens":1840828,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":77735,"runtime":"claude","turns":30,"wallClockMs":1174504}},{"artifacts":[],"attempt":1,"bundleHash":"f53e2f74ab2d0c5e4a6de62914573bad74ae3f2a0ee50b494e95eab23de12d82","device":"fdeac05268aded13","findings":[],"hash":"6d3092bfce8b31685ed2625f7ff14f1d6cab573d71bcc3eecf3e580d29d983e7","nodeId":"a7edd990-0d80-4ae0-9d68-a2a773ea7ab8","outcome":"completed","summary":"Updated [launch.json](/root/.identitymd/work/ee10bd28-8e70-4175-89b2-445a5e137bb3/a7edd990-0d80-4ae0-9d68-a2a773ea7ab8/launch.json) with verified PoolManager evidence and accepted constructor arguments.\n\nSchema/ABI checks, `forge build`, and `forge test` passed: 30 tests, zero failures, including the protected deployment probe.\n\nNo other submitted files changed.","treeHash":"088cb85ff3bc2faf5544c717903a22aa0297a3c6","usage":{"cachedInputTokens":857216,"inputTokens":51423,"model":"gpt-6-astra","outputTokens":9813,"runtime":"codex","turns":6,"wallClockMs":590412}},{"artifacts":[],"attempt":3,"bundleHash":null,"device":"468e82a89b9bfe18","findings":[{"citation":"resolved","description":"The brief says 'Owner listTile(slot, PoolKey, tier 1-8) fills a vacant slot; the pool must pair with the currency; a tile holding tokens can't change.' That is, a listing may change as long as the tile holds no tokens (assets == 0). The implementation instead rejects any second listTile on a slot whose token is already set, regardless of whether it holds tokens (the author documents this at line 103: 'Listings are permanent, including after the last deed is redeemed'). There is no other correction path and the contracts are not upgradeable. Boundary consequence: listTile only checks that the key pairs with the currency and that token/hook have code; it cannot check that the (currency0, currency1, fee, tickSpacing, hooks) pool is actually initialized on the PoolManager. A typo in fee or tickSpacing (e.g. 10000 instead of 3000) produces a key for an uninitialized pool: every buyDeed on that slot reverts inside PoolManager.swap (PoolNotInitialized) forever, quote reverts, and yet _land still reads tileInfo(slot) with token != 0 and charges tierRent on every landing (100% to the pot since weight == 0). The slot is a dead rent trap for the lifetime of the game, and the owner cannot fix it as the brief allows. The same applies to a wrong tier (a 30 IMD tile listed as 1 IMD), which also cannot be corrected while the tile holds no tokens.","line":105,"path":"src/DeedVault.sol","reproduction":"State: DeedVault bound to game, no deeds. 1) owner calls game.listTile(6, PoolKey(IMD, TOKEN, fee=10000, tickSpacing=60, hooks=0), 3) -> succeeds; vault.tile(6).assets == 0, token == TOKEN. 2) owner calls game.listTile(6, PoolKey(IMD, TOKEN, fee=3000, tickSpacing=60, hooks=0), 3) to correct the fee -> reverts Invalid() at DeedVault.sol:105 because _tiles[6].token != address(0). Expected per brief: succeeds because the tile holds no tokens. Also 3) owner calls game.listTile(6, sameKey, 4) to fix the tier -> reverts Invalid(). Verified with a scratch Foundry test (RelistTest.testVacantListedTileCannotBeRelisted): both calls revert with Invalid() while vault.tile(6).assets == 0. Minimal fix preserving the design: allow re-listing when _tiles[slot].shares == 0 && _tiles[slot].assets == 0 (and no outstanding sponsor liability for the slot), resetting the key/token/tier; keep the refusal when assets != 0.","severity":"medium","snippet":"        if (!isProperty(slot) || _tiles[slot].token != address(0) || tier < 1 || tier > 8) revert Invalid();","title":"Listed tile with no tokens cannot be re-listed; a wrong PoolKey or tier permanently bricks one of the 28 property slots while it keeps charging rent"}],"hash":"6d3968fd67f67c6aa1976b381c250dbba0278340cc4fdcebb66cb9d8d5ad0260","nodeId":"1faf4e19-9e9f-4fb6-9a52-cad5061aa28a","outcome":"completed","summary":"The review is complete. `.imd-findings.json` is written at the repository root with one finding and 39 coverage rows (all 36 listed entry points plus three invariants). Scratch tests were removed, and no source, test or config file was changed.\n\n**What I checked (Math Precision, Boundary, Numerical Gap guides).** All three contracts were traced end to end, the existing suite was re-run with a second fuzz seed and 1024 runs, and six scratch probes exercised the arithmetic edges. The tree builds and all 29 tests pass.\n\n**Result: the arithmetic holds.**\n- Tile shares are pinned 1:1 to assets for every tile, so there is no share-price drift, inflation or zero-share rounding. Redeem returns exactly the lot's shares.\n- The 1e27 rent accumulator rounds down at credit and claim. With a 2-wei-weight lot beside a 200e18-weight lot, claims summed to one wei under the credited rent.\n- The commit-time exposure equals the exact worst-case landing cost, and no balance-reducing path exists while a roll is pending, so withdraw can never underflow and expiry can never fail.\n- Prize weights sum to 100, each prize rounds down, and reserve never exceeds 60% of the available pot.\n- The v4 callback's delta unpacking, exact-input check, price limits and settle check match the vendored v4-core. The 36-byte quote revert bubbles through PoolManager.unlock unchanged.\n- Robinhood Chain's `block.number` tracks Ethereum L1 (RPC header shows l1BlockNumber near 26.1M), so the 200-block reveal window is about 40 minutes and fits inside the 1-hour deadline.\n\n**One finding, medium, outside the pure math area but a brief deviation with permanent effect.** The brief says only \"a tile holding tokens can't change\", but `listTile` refuses any second listing on a slot once a token is set, even with zero assets. A mistyped fee or tick spacing yields an uninitialized pool: every buy on that slot reverts forever, yet landing still charges full rent to the pot. Reproduced with a scratch test: re-listing a freshly listed, empty tile reverts with Invalid for both a corrected key and a corrected tier. Suggested fix keeps the design: allow re-listing only when shares, assets and sponsor liability for the slot are all zero.\n\n**Not reproduced as defects.** The Slither weak-PRNG and timestamp lines describe the commissioned commit-reveal design and are documented in REVIEW.md. The reentrancy and uninitialized-local lines were traced and do not lead to harm. The IMD token being standard, the owner listing honest tokens, and sequencer influence over blockhash remain the documented trust assumptions.","treeHash":null,"usage":{"cachedInputTokens":2083509,"inputTokens":546,"model":"claude-fable-5-1","outputTokens":51214,"runtime":"claude","turns":33,"wallClockMs":905108}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"6b16b4979d227241","findings":[{"citation":"resolved","description":"Settlement of round-1 finding bbaef8b0 (reported low, author disputed). The holder share switches on at weight != 0 regardless of the IMD value behind that weight, and DeedVault.buy accepts any amountIn whose swap output is >= minOut >= 1, so the first buyer on a tile with 1 wei of IMD takes 80% of every later rent payment on that tile until another buyer dilutes them, and the pot's income from that tile drops from 100% to 20%. The author's answer holds: the brief specifies amountIn <= maxBuy and an 80/20 split by holder weight, with no minimum purchase or value-based qualification, so this is the specified distribution rather than an accounting defect. It is retained in RULES.md and in test/RevisionRegression.t.sol::testObservedDustDeedReceivesHolderShare. Downgraded from low to info: it is an economic property for the requester to confirm, not a defect that should hold the launch. If the requester wants rent income protected, a minBuy season parameter checked next to maxBuy in _buy is the design-preserving option.","line":307,"path":"src/SwarmopolyGame.sol","reproduction":"State: listTile(6, key, 3); startSeason(10e18, 30 days, 1e18, 100e18); alice and bob joined with 100 IMD play balance. alice rolls 6 and calls buyDeed(6, 1, 1): mock swap returns 2 units, shares = 2, weight = 4. bob rolls 6 and pays tier-3 rent 3e18. Expected under the brief's rule: holders get 80% by weight. Actual (matches): vault.pendingRent(alice deed) == 2.4e18, pot.available() rises by 0.6e18. Re-ran test/RevisionRegression.t.sol::testObservedDustDeedReceivesHolderShare on the current tree: PASS with exactly these values.","severity":"info","snippet":"            uint256 holders = weight == 0 ? 0 : Math.mulDiv(paid, 80, 100);","title":"A 1-wei deed receives the full 80% holder share of a tile's rent while it is the only holder (accepted design, non-blocking)"},{"citation":"resolved","description":"Settlement of round-1 finding f2daf55d (info, author disputed). expireRoll jails the player and additionally sets bankrupt for the rest of the season and moves r.exposure (min(balance, max(TAX, max tierRent)) = 30 IMD at defaults with a 100 IMD balance) to the pot. The author's reasoning is correct: a jail-only resolution would let a player withhold a reveal that would have charged rent or bankrupted them, breaking the brief's 'unrevealed rolls never better' invariant, which test/Swarmopoly.t.sol::testFuzzWithholdingNeverImprovesBalance checks differentially. The behaviour is unchanged this round and disclosed in RULES.md and the site. Recorded so the requester explicitly accepts that an honest player whose auto-reveal fails (wallet rejection, lost local secret, sequencer outage beyond 200 L1 blocks or 1 hour) loses up to the max tier rent and their season.","line":260,"path":"src/SwarmopolyGame.sol","reproduction":"Player alice with 100 IMD play balance calls commitRoll(commitment) and never reveals. vm.roll(+202); bob calls expireRoll(alice). Brief-literal expectation: alice jailed. Actual: position == 10, jailed == true, bankrupt == true, balance == 70e18, pot.available() + 30e18. Re-ran test/RevisionRegression.t.sol::testObservedMissedRevealPenalty on the current tree: PASS with these values.","severity":"info","snippet":"        _toPot(who, r.exposure);","title":"A missed reveal is resolved as forfeiture of the reserved exposure plus season bankruptcy, stricter than the brief's 'as jail' (accepted deviation)"},{"citation":"resolved","description":"Settlement of round-1 finding f61b4270 (info, author disputed). GO salary (salaryBps, default 10), free parking (50 bps per player per UTC day via parkingDay) and the chance card (5 bps) are all percentages of pot.available(), while joining costs a flat buyIn and rolling needs only balance >= rollBond, and the worst a roll can cost is min(balance, max tierRent or 5 IMD). Multi-account play therefore becomes profitable once the pot is large relative to buyIn + rollBond; the owner's only levers are buyIn and rollBond per season, and startSeason accepts buyIn = 0. This is the economics the brief specifies and not a code defect. The author expanded RULES.md and REVIEW.md to state the farming exposure and the operator's duty to size buy-in and bond against the pot. Unchanged this round; recorded as the main economic trust assumption the requester accepts by launching.","line":342,"path":"src/SwarmopolyGame.sol","reproduction":"fundPot(10000e18) on the fixture season (buyIn 10 IMD, rollBond 1 IMD). alice and bob each withdraw to a 1 IMD play balance. Each rolls 10 then 10 (square 20): alice.balance == 1e18 + potBefore*50/10000, then bob.balance == 1e18 + potBefore'*50/10000 on the same UTC day (two separate 50 bps payouts). charlie joins with 1 IMD, lands on tile 9 (rent 30 IMD): pays 1e18 and is bankrupt. Re-ran test/RevisionRegression.t.sol::testObservedParkingPaysEachPlayerAndDownsideIsBalance on the current tree: PASS.","severity":"info","snippet":"        uint256 amount = Math.mulDiv(pot.available(), bps, 10_000);","title":"Per-roll rewards scale with the pot while entry cost is flat and downside is capped at the play balance; parking pays each player once per UTC day (economic assumption)"}],"hash":"9af0f782e92dafa6d1db74b38e079a9ec1a25f3c8c6846bb39f0b1390ba3105f","nodeId":"dea5c587-9202-4713-a3d6-4065447ef209","outcome":"completed","summary":"The second-round review is complete. All four round-one defects are fixed, and the only items left are three informational notes the author disputed on design grounds, which I accepted and downgraded.\n\n**Settled as fixed**\n\n- **Zero-score joiners paid prizes (high).** The score update now returns early when the resulting score is zero, and finalizeSeason skips any winner with a zero score. The attached proof, copied unchanged into test/scratch and run against the revised code, passes. My own check with fifteen join-only accounts left the leaderboard empty while a real scorer took rank one.\n- **Empty listed tile could not be relisted (medium).** listTile now refuses only when the tile has assets, shares or outstanding sponsor liability. Fee and tier corrections on an empty tile succeed. I confirmed the refusal still holds when the only remaining liability is an awarded but unclaimed lander token with sponsor remaining already zero.\n- **1-wei sponsor squat (low).** A new sponsor or rate is blocked only while remaining is nonzero. I verified a partial last award followed by replacement: the old sponsor's withdrawal reverts with nothing owed, earlier awards stay reserved, and liability reconciles to zero at season end.\n- **joinSeason cleared the cooldown (low).** The reset line is gone. nextRoll persists for both normal and bankrupt rejoiners.\n\n**Kept as info, non-blocking**\n\n1. Dust deed takes the full holder share. Downgraded from low. The brief specifies only maxBuy and an 80/20 split by weight, so the author's dispute holds.\n2. Missed reveal resolves as forfeiture plus bankruptcy, stricter than \"as jail\". Deliberate, needed for the never-better invariant, disclosed in rules and site.\n3. Pot-scaled rewards against a flat buy-in and per-player daily parking. Specified economics, now documented as an operator responsibility.\n\n**Verification**\n\n| Check | Result |\n|---|---|\n| Project suite (no scratch) | 70 passed, 0 failed |\n| Round-one proofs on revised code | both pass |\n| My scratch edge tests | 4 passed |\n\nThe findings file holds three info findings and 44 coverage rows: all 36 entry points marked holds or finding, plus rows for each settled round-one item and the full-suite invariant. The scratch directory was removed, so the tree is unchanged apart from the findings file. Nothing new that reproduces as a defect was found in the changed code.","treeHash":null,"usage":{"cachedInputTokens":667820,"inputTokens":226,"model":"claude-fable-5-1","outputTokens":22446,"runtime":"claude","turns":19,"wallClockMs":464459}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"3a40eaafbd83a6bc","findings":[{"citation":"resolved","description":"joinSeason calls _score(msg.sender, 0) (line 173), which inserts every joiner into _leaders[currentSeason] while the list has room, ordered by score and then by ascending address (_better, line 432). finalizeSeason skips only address(0) and assigns each occupied rank its fixed share of 60% of pot.available(). Nothing requires a winner to have a positive score, so whenever fewer than ten accounts scored in a season (the launch state, with vacant lots and few players) the empty ranks are paid to whoever merely joined, and among zero-score joiners the lowest numeric addresses win. startSeason accepts buyIn = 0, so the cost can be gas only; with buyIn > 0 the attack is profitable whenever a rank share (2.4% to 15% of the pot) exceeds the buy-in, which it does for a pot above roughly 42x the buy-in. The pot carries 40% forward plus buy-ins, rents and taxes, so money that the author's RULES.md says should carry over for unoccupied ranks leaves to non-players instead. Merged with audit_economics' lower-severity report of the same root cause (zero-score insertion plus address tie-break). docs/RULES.md documents zero-score eligibility as a choice, but it contradicts the score-based prize the brief asks for and makes the prize budget farmable. Design-preserving fix: do not insert an address into the leaderboard while its score is 0 (return early in _score when score == 0 and the address is not already listed) and skip winners with scores[currentSeason][winner] == 0 in finalizeSeason so their weight carries over like empty ranks.","line":444,"path":"src/SwarmopolyGame.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\nimport {Test} from \"forge-std/Test.sol\";\nimport {SeasonPot} from \"src/SeasonPot.sol\";\nimport {DeedVault} from \"src/DeedVault.sol\";\nimport {SwarmopolyGame} from \"src/SwarmopolyGame.sol\";\n\ncontract Cash {\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n    function mint(address to, uint256 a) external { balanceOf[to] += a; }\n    function approve(address s, uint256 a) external returns (bool) { allowance[msg.sender][s] = a; return true; }\n    function transfer(address to, uint256 a) external returns (bool) { balanceOf[msg.sender] -= a; balanceOf[to] += a; return true; }\n    function transferFrom(address f, address to, uint256 a) external returns (bool) {\n        allowance[f][msg.sender] -= a; balanceOf[f] -= a; balanceOf[to] += a; return true;\n    }\n}\n\ncontract ZeroScorePrizeTest is Test {\n    SeasonPot pot; DeedVault vault; SwarmopolyGame game; Cash cash;\n\n    function setUp() public {\n        vm.warp(10 days);\n        cash = new Cash();\n        pot = new SeasonPot(address(this), address(cash));\n        vault = new DeedVault(address(this), address(cash), address(0xBEEF));\n        game = new SwarmopolyGame(address(this), address(cash), address(vault), address(pot));\n        pot.bindGame(address(game));\n        vault.bindGame(address(game));\n        // Pot carries value from earlier play (rents, taxes, buy-ins, carry-over).\n        cash.mint(address(this), 1000 ether);\n        cash.approve(address(game), type(uint256).max);\n        game.startSeason(0, 1 days, 1 ether, 1 ether);\n        game.fundPot(1000 ether);\n    }\n\n    function testJoinersWithZeroScoreArePaidPrizes() public {\n        address[10] memory sybils;\n        for (uint160 i; i < 10; ++i) {\n            sybils[i] = address(uint160(0x1000 + i));\n            vm.prank(sybils[i]);\n            game.joinSeason(); // buy-in 0: no funds, no deposit, no roll\n        }\n        vm.warp(block.timestamp + 1 days);\n        game.finalizeSeason();\n        uint256 total;\n        for (uint256 i; i < 10; ++i) {\n            uint256 prize = game.prizes(1, sybils[i]);\n            assertEq(game.scores(1, sybils[i]), 0);\n            total += prize;\n            if (prize != 0) {\n                vm.prank(sybils[i]);\n                game.claimPrize(1); // pull claim succeeds: the money really leaves the pot\n            }\n        }\n        // Expected: zero-score accounts receive nothing. Actual: 60% of the pot leaves to them.\n        assertEq(total, 0, \"zero-score joiners were paid 60% of the pot\");\n    }\n}","reproduction":"Deploy pot/vault/game, bind both, startSeason(buyIn=0, duration=1 days, rollBond=1e18, maxBuy=1e18), fundPot(1000e18). From ten fresh addresses 0x1000..0x1009 call joinSeason() only (no deposit, no roll). Warp past end and call finalizeSeason(). Expected: no prize for accounts with scores[1][addr] == 0 and pot.available() stays 1000e18. Actual: prizes[1][...] sum to 600e18 (25/18/13/10/8/7/6/5/4/4% of 600e18), each claimable via claimPrize(1); pot.available() drops to 400e18. Ran the attached test on the current tree: FAIL 'zero-score joiners were paid 60% of the pot: 600000000000000000000 != 0'.","severity":"high","snippet":"            if (winner == address(0)) continue;","title":"joinSeason ranks zero-score accounts and finalizeSeason pays them, so 60% of the pot can be claimed by addresses that never rolled"},{"citation":"resolved","description":"The brief says listTile fills a vacant slot and that 'a tile holding tokens can't change', i.e. a listing may be corrected while assets == 0. The implementation rejects any second listTile once token is set, regardless of assets (the comment at line 103 says listings are permanent even after the last deed is redeemed). listTile cannot verify that the (currency0, currency1, fee, tickSpacing, hooks) pool is initialized on the PoolManager, so a key with a wrong fee or tickSpacing passes validation; afterwards every buyDeed and quote on that slot reverts inside PoolManager.swap for the lifetime of the contracts, while _land still reads token != 0 and charges tierRent on every landing (100% to the pot because weight == 0). A wrong tier likewise cannot be corrected. The contracts are not upgradeable, so one of the 28 slots is lost. Design-preserving fix: allow listTile on a slot whose shares == 0, assets == 0 and sponsorLiability[slot] == 0, overwriting key/token/tier; keep the refusal while the tile holds tokens or sponsor funds.","line":105,"path":"src/DeedVault.sol","reproduction":"State: vault bound to game, no deeds. (1) owner calls game.listTile(6, PoolKey(IMD, TOKEN, fee=10000, tickSpacing=60, hooks=0), 3): succeeds, vault.tile(6).assets == 0. (2) owner calls game.listTile(6, PoolKey(IMD, TOKEN, fee=3000, tickSpacing=60, hooks=0), 3) to correct the fee: reverts Invalid() at DeedVault.sol:105. (3) owner calls game.listTile(6, sameWrongKey, 4) to fix the tier: reverts Invalid(). Expected per brief: both succeed because the tile holds no tokens. Verified with test/scratch/Judge.t.sol::testVacantListedTileCannotBeRelisted on the current tree.","severity":"medium","snippet":"        if (!isProperty(slot) || _tiles[slot].token != address(0) || tier < 1 || tier > 8) revert Invalid();","title":"A listed tile holding no tokens cannot be re-listed, so a wrong PoolKey or tier permanently bricks a property slot that still charges rent"},{"citation":"resolved","description":"sponsorTile is permissionless and first-come: the first caller for (season, slot) becomes the only address allowed to fund that tile until seasonEnd, pinned to its first perLanding. There is no minimum amount or perLanding and no way to displace a sponsorship whose remaining is 0. A griefer can call sponsorTile(slot, 1, 1) on every listed tile at season start for 28 wei of tile tokens plus gas, after which no real sponsor can fund any tile that season; the 1 wei is awarded on the first landing and the slot stays dead, and the griefer recovers nothing of value but denies the feature. No funds are lost. Reported identically by audit_economics and audit_permissions; merged. Fix preserving the O(1) landing path: let a new sponsor take over when s.remaining == 0, or require amount >= perLanding with a minimum perLanding.","line":281,"path":"src/DeedVault.sol","reproduction":"State: tile 6 listed, season 1 active. (1) mallory: token.approve(vault, 1); vault.sponsorTile(6, 1, 1) succeeds. (2) alice: token.approve(vault, 100e18); vault.sponsorTile(6, 100e18, 1e18) reverts Invalid() because s.sponsor == mallory. Expected: a real sponsorship can be placed or the dust one displaced. Actual: tile 6 cannot be sponsored by anyone but mallory, at 1 wei per landing, until seasonEnd[1]. Verified with test/scratch/Judge.t.sol::testSponsorSquat on the current tree.","severity":"low","snippet":"        if (s.sponsor != address(0) && (s.sponsor != msg.sender || s.perLanding != perLanding)) revert Invalid();","title":"Anyone can lock a tile's single sponsor slot for the whole season with 1 wei"},{"citation":"resolved","description":"The 20h (owner-configurable 1-48h) cooldown is stored per player in nextRoll, but joinSeason zeroes it unconditionally, while the comment two lines later says nonce and parkingDay are deliberately preserved to prevent resets at season boundaries. A player who commits and reveals at the last permitted moment of season N (end - 1h - 1s) can join season N+1 as soon as it is started and commit again immediately, giving two full rolls (two chances at salary, chance and, if the UTC day changed, a second parking payout) about an hour apart. Bounded to one extra roll per season per player; no fund loss. Reported identically by audit_economics and audit_permissions; merged. Fix: do not touch p.nextRoll in joinSeason.","line":162,"path":"src/SwarmopolyGame.sol","reproduction":"startSeason(0, 1 days, 1e18, 100e18); alice joins with 100 IMD balance. warp to seasons[1].end - 3601; alice commits and reveals; player(alice).nextRoll == end - 3601 + 20h. warp to end; finalizeSeason(); owner startSeason(0, 1 days, 1e18, 100e18); alice joinSeason(): player(alice).nextRoll == 0; alice commitRoll(validCommitment) at timestamp end succeeds. Expected: TooEarly until 20h after the first commit. Actual: second commit accepted 3,601 seconds after the first (test logs 'seconds between commits 3601'). Verified with test/scratch/Judge.t.sol::testCooldownResetAcrossSeasons on the current tree.","severity":"low","snippet":"        p.nextRoll = 0;","title":"joinSeason resets nextRoll, so the roll cooldown is skipped across a season boundary"},{"citation":"resolved","description":"The holder share switches on at weight != 0 regardless of how much value backs that weight, and DeedVault.buy accepts any amountIn whose swap output is at least minOut >= 1. The first buyer on a tile, with 1 wei of IMD, therefore receives the entire 80% holder share of every later rent payment on that tile until someone else buys, and the pot (which funds every salary, parking payout and prize) drops from 100% to 20% of that tile's rent. The brief bounds amountIn only from above (maxBuy); there is no minimum purchase and no owner parameter for one, so the rational play is to dust-buy every tile landed on, hollowing out the pot's rent income. Fix: add a minBuy season parameter checked in _buy next to maxBuy, or require a per-tile minimum of shares.","line":308,"path":"src/SwarmopolyGame.sol","reproduction":"State: listTile(6, key, 3); startSeason(10e18, 30 days, 1e18, 100e18); alice and bob join with 100 IMD play balance. alice rolls 6, lands on tile 6, calls buyDeed(6, 1, 1): swap returns 2 token units, deed shares = 2, weight = 4. bob rolls 6 and pays tier-3 rent of 3 IMD. Expected: a negligible stake should not divert the pot's rent income. Actual: vault.pendingRent(alice's deed) == 2.4e18 and the pot receives 0.6e18 of bob's 3e18 (before alice's deed the same landing sent 3e18 to the pot). Verified with test/scratch/Judge.t.sol::testDustDeedCapturesRent on the current tree (logs 'alice pending 2400000000000000000', 'pot got 600000000000000000').","severity":"low","snippet":"            uint256 holders = weight == 0 ? 0 : Math.mulDiv(paid, 80, 100);","title":"A 1-wei deed redirects 80% of a tile's rent from the pot to its holder"},{"citation":"resolved","description":"The brief says a reveal missed past the window 'can be resolved by anyone as jail'. expireRoll additionally marks the player bankrupt for the rest of the season and moves r.exposure (max(TAX, max tierRent) capped at balance, 30 IMD at defaults) to the pot. The author documents this as the mechanism that keeps the 'unrevealed rolls never better' invariant true, and the trace confirms the penalty is at least the worst reveal outcome, so this is a deliberate deviation rather than a bug. It also means an honest player whose auto-reveal fails (wallet rejection, lost localStorage, sequencer outage longer than 40 minutes of L1 blocks) loses up to 30 IMD and their season. Recorded for the requester to confirm the stricter rule.","line":259,"path":"src/SwarmopolyGame.sol","reproduction":"Player with 100 IMD balance commits a roll and never reveals. After 200 L1 blocks (or 1 hour) anyone calls expireRoll(player). Brief-literal expectation: player is jailed. Actual: position == 10, jailed == true, bankrupt == true, balance 70 IMD, pot.available() up by 30 IMD. Verified with test/scratch/Judge.t.sol::testExpireRollSemantics.","severity":"info","snippet":"        p.bankrupt = true;","title":"A missed reveal is resolved as forfeiture plus season bankruptcy, not only jail as the brief states"},{"citation":"resolved","description":"This is the economics the brief specifies, not a code defect, and is recorded as a trust/economic assumption. GO salary (10 bps), parking (50 bps) and the chance card (5 bps) are all percentages of pot.available(), while an account costs a fixed buy-in and must only hold rollBond to roll, and the worst a roll can cost is min(balance, max rent or 5 IMD tax). Expected gain per roll is roughly 3.2 bps of the pot, so with the suggested 10 IMD buy-in and 1 IMD bond, Sybil accounts become profitable once the pot exceeds roughly 1,100 IMD on a vacant board (no rent, tax cannot bankrupt) and roughly 24,000 IMD once tiles charge rent. The owner's only levers are buyIn and rollBond per season; startSeason accepts buyIn = 0. Separately, the brief's 'Free parking: 50 bps of pot, once a day' is implemented per player per UTC day (parkingDay), so N players landing on square 20 the same day each take 50 bps; if 'once a day' was meant globally this multiplies the drain by the number of landers.","line":343,"path":"src/SwarmopolyGame.sol","reproduction":"Fresh season with only tile 9 listed, startSeason(10e18, 30 days, 1e18, 100e18), pot funded to 10,010 IMD. Alice joins (10 IMD) and deposits exactly 1 IMD, rolls 10 then 10 (square 20): _reward(alice, 50) pays 50 bps of the pot (about 50.1 IMD) into her withdrawable balance. Bob joins with 1 IMD and lands on tile 9 (rent 30): pays 1 IMD and is bankrupt. Maximum loss per account is the 1 IMD bond plus buy-in; upside per roll is 0.1 to 0.5% of the whole pot.","severity":"info","snippet":"        uint256 amount = Math.mulDiv(pot.available(), bps, 10_000);","title":"Per-roll rewards scale with the pot while entry cost is flat and downside is capped at the play balance; parking pays every player once per UTC day"}],"hash":"cf8762353ef81c0adfbf6a8f9a2b57da5632c303e181b6621299407f37daa2c8","nodeId":"dea5c587-9202-4713-a3d6-4065447ef209","outcome":"completed","summary":"Review complete. The report is in `.imd-findings.json` at the repository root, with seven findings and a coverage row for all 36 entry points plus four invariants. No source files were changed; `test/scratch/` holds only my throwaway reproductions, and git shows no tracked changes.\n\n**What I reproduced and kept**\n\n- **High: zero-score joiners are paid prizes.** Joining inserts an address into the top ten at score 0, and finalization pays every non-empty rank. Ten fresh addresses that only call `joinSeason` take 60% of the pot. The attached proof from the flow specialist fails on the current tree for exactly that reason, so I kept it as the proof. Merged with the economics specialist's lower-severity report of the same root cause.\n- **Medium: a vacant listed tile can never be corrected.** The brief allows changing a tile that holds no tokens. A wrong fee or tier makes the slot unbuyable for the life of the contracts while it keeps charging rent.\n- **Low: one-wei sponsorship squats a tile for the season.** Reported by two specialists, merged into one.\n- **Low: joining resets the roll cooldown**, giving one extra roll per season boundary. Reported by two specialists, merged.\n- **Low: a one-wei deed captures 80% of a tile's rent** from the pot. No minimum purchase exists.\n- **Info: expiry is forfeiture plus bankruptcy**, stricter than the brief's \"resolved as jail\". Deliberate and documented, recorded for confirmation.\n- **Info: rewards scale with the pot while entry cost is flat**, and parking pays each player once per UTC day. This is the brief's own economics, recorded as a trust assumption.\n\n**What I dropped**\n\n- The matured-lock weight note is documented behavior and does not contradict the brief.\n- The Arbitrum Nitro blockhash-grinding lead does not apply here. I sampled the chain: it produces roughly ten L2 blocks per second with several transactions each, so a committer's block can never be the last block of an L1 slot.\n\n**My own pass** found nothing beyond the specialists' leads. I traced the v4 callback (context hash, signed deltas, sync/settle/take), the share and rent accumulator math, exposure versus balance, season boundaries, pause semantics and the leaderboard insertion. The project's 56 tests pass. On chain I confirmed the hook returns the PoolManager address written in the manifest, IMD is a non-proxy 18-decimal token, and `block.number` tracks Ethereum L1 height, so the 200-block reveal window is about 40 minutes as documented.","treeHash":null,"usage":{"cachedInputTokens":1504136,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":57209,"runtime":"claude","turns":35,"wallClockMs":999391}},{"artifacts":[],"attempt":1,"bundleHash":"9e101245e99a3d06af9c45e1da38d5cf0e758df8da810c37f3c78a50bbc3fa76","device":"84a29c6575331b77","findings":[],"hash":"d1a55f5ac2c077677b32dbca20311c2fa2045452f3c269a60e54ef4074c2ee5b","nodeId":"fe65f089-d29c-415e-aec3-6f59156ae1c9","outcome":"completed","summary":"Implemented the three contracts, static website, deployment manifest, tests, and documentation. PoolManager was verified through the supplied hook.\n\nPassed `forge build`, `forge test` (29 tests), `forge fmt --check`, and site checks.\n\nDeployment, IPFS publication, and independent review remain outstanding. The site awaits confirmed contract addresses.\n\nSee [README](/home/imd/.identitymd/work/ee10bd28-8e70-4175-89b2-445a5e137bb3/fe65f089-d29c-415e-aec3-6f59156ae1c9/README.md) for rules, assumptions, and deployment instructions.","treeHash":"d4902fe4cfa7c1fe2551d0642d7176684880e10c","usage":{"cachedInputTokens":1730304,"inputTokens":119774,"model":"gpt-6-astra","outputTokens":56052,"runtime":"codex","turns":8,"wallClockMs":1902357}},{"artifacts":[],"attempt":1,"bundleHash":"cd04f258834d53e94d96afb24d5ab6c6c953b21d4d2dfbf8f5284001ef1b1141","device":"19e9c602f9934c65","findings":[],"hash":"d3fb034cbcfbce8728e12df1e1a9dac0dc95ff4c39c91f197001d4b5a2105ea8","nodeId":"87d86f4e-21ce-4ace-94bc-cbf910f82dcc","outcome":"completed","summary":"Added 25 adversarial/fuzz tests and 12-player stateful invariants covering custody, rent, sponsorships, prizes, and withheld rolls.\n\nValidation passed:\n\n- `forge build`\n- `forge test`: 56 passed, zero failures\n- New invariant campaign: 24,576 calls, zero reverts\n\nDeliverable changes are confined to `test/`. No reproducible contract defects found. Live-chain integration remains unverified.","treeHash":"d9daa817bd96bdfd9f531058dca72506e0824986","usage":{"cachedInputTokens":2250880,"inputTokens":114829,"model":"gpt-6-astra","outputTokens":21424,"runtime":"codex","turns":7,"wallClockMs":830651}},{"artifacts":[],"attempt":1,"bundleHash":"d05207a67d3e3c2c6bfcf80e7d990d6b507eb3713cb5548fda68bf97397ac549","device":"24b3b94660bddda1","findings":[],"hash":"d77c76c03b6faf451deada73682a8f1b43806cf2f5bed348ccc8f8b792571a68","nodeId":"fe65f089-d29c-415e-aec3-6f59156ae1c9","outcome":"completed","summary":"Fixed four defects: zero-score prizes, empty-tile relisting, exhausted-sponsor replacement, and season-boundary cooldown resets.\n\nRecorded all seven verdicts in `.imd-responses.json`; three economic/timeout advisories were reproduced and disputed with explanations.\n\nValidation passed: `forge build`, all 40 tests, `forge fmt --check`, the unchanged reviewer proof, and website smoke checks.","treeHash":"89543324630cba1ec2e147d2af1922da996fe650","usage":{"cachedInputTokens":1266176,"inputTokens":112880,"model":"gpt-6-astra","outputTokens":14987,"runtime":"codex","turns":6,"wallClockMs":530950}},{"artifacts":[],"attempt":1,"bundleHash":"fcadbae2e9d68bf482f3e4c349dc5b655d2cb4e1ef7cc7b3e7e288ae66e1bdaa","device":"2c736d2f8963f8a5","findings":[],"hash":"f7c98dfbdcfa13a5cf556857e8111c7478da465d37b1f2a280d1f24c757ab94b","nodeId":"87d86f4e-21ce-4ace-94bc-cbf910f82dcc","outcome":"completed","summary":"Updated prize fixtures and leaderboard invariants, and added season-rollover regressions.\n\n`forge build` and `forge test` pass: **70 passed, 0 failed**, including randomized custody invariants.\n\nOnly `test/` files changed. No contract defects found; live-chain integration remains unverified.","treeHash":"15aef2f9a5e7007d1fae79afd297b660606144f5","usage":{"cachedInputTokens":2624256,"inputTokens":92732,"model":"gpt-6-astra","outputTokens":8040,"runtime":"codex","turns":7,"wallClockMs":578800}}],"verification":[{"checks":[{"durationMs":24976,"exitCode":0,"name":"build","output":"Compiling 35 files with Solc 0.8.26\nSolc 0.8.26 finished in 24.75s\nCompiler run successful!\nwarning[missing-events-access-control]: `swapContext` is changed without an event but is used for access control\n    ╭▸ src/DeedVault.sol:172:9\n    │\n172 │         swapContext = keccak256(data);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:105:35\n    │\n105 │         if (currentSeason == 0 || block.timestamp >= seasons[currentSeason].end || seasons[currentSeason].finalized) {\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[missing-events-access-control]: `swapContext` is changed without an event but is used for access control\n    ╭▸ src/DeedVault.sol:191:9\n    │\n191 │         swapContext = bytes32(0);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SeasonPot.sol:28:23\n   │\n28 │     function bindGame(address game_) external onlyOwner {\n   │                       ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/SeasonPot.sol:43:64\n   │\n43 │     function pay(address to, uint256 amount) external onlyGame nonReentrant {\n   │                                                                ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/SeasonPot.sol:55:72\n   │\n55 │     function payReserved(address to, uint256 amount) external onlyGame nonReentrant {\n   │                                                                        ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/DeedVault.sol:79:23\n   │\n79 │     function bindGame(address game_) external onlyOwner {\n   │                       ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:116:9\n    │\n116 │         vault.listTile(slot, key, tier);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/SwarmopolyGame.sol:115:88\n    │\n115 │     function listTile(uint8 slot, PoolKey calldata key, uint8 tier) external onlyOwner nonReentrant {\n    │                                                                                        ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/DeedVault.sol:173:13\n    │\n173 │         try poolManager.unlock(data) returns (bytes memory result) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/DeedVault.sol:133:9\n    │\n133 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:159:9\n    │\n159 │         emit DeedBought(id, holder, slot, out, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:168:73\n    │\n168 │         if (_tiles[slot].token == address(0) || amount == 0 || amount > uint256(uint128(type(int128).max))) {\n    │                                                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:168:81\n    │\n168 │         if (_tiles[slot].token == address(0) || amount == 0 || amount > uint256(uint128(type(int128).max))) {\n    │                                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:178:51\n    │\n178 │             if (quoting && reason.length == 36 && bytes4(reason) == QuoteResult.selector) {\n    │                                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:198:30\n    │\n198 │                 zeroForOne, -int256(amount), zeroForOne ? 4295128740 : 1461446703485210103287273052203988822378723970341\n    │                              ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:202:21\n    │\n202 │         int128 d0 = int128(delta >> 128);\n    │                     ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:203:21\n    │\n203 │         int128 d1 = int128(delta);\n    │                     ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:206:23\n    │\n206 │         if (input != -int256(amount) || output <= 0) revert SwapFailed();\n    │                       ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:207:23\n    │\n207 │         uint256 out = uint256(output);\n    │                       ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:221:9\n    │\n221 │         emit RentCredited(slot, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/DeedVault.sol:245:9\n    │\n245 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:257:9\n    │\n257 │         emit RentClaimed(id, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:133:9\n    │\n133 │         vault.beginSeason(currentSeason, end);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:263:30\n    │\n263 │         if (d.shares == 0 || block.timestamp < d.redeemAt) revert Invalid();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:273:9\n    │\n273 │         emit Redeemed(id, assets);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/SwarmopolyGame.sol:122:9\n    │\n122 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:279:35\n    │\n279 │             currentSeason == 0 || block.timestamp >= seasonEnd[currentSeason] || _tiles[slot].token == address(0)\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:134:9\n    │\n134 │         emit SeasonStarted(currentSeason, buyIn, end, rollBond, maxBuy);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:128:29\n    │\n128 │                 || maxBuy > uint256(uint128(type(int128).max))\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:128:37\n    │\n128 │                 || maxBuy > uint256(uint128(type(int128).max))\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:312:13\n    │\n312 │         if (block.timestamp < seasonEnd[id] || s.remaining == 0) revert Invalid();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SwarmopolyGame.sol:142:40\n    │\n142 │             if (rents[i] > 1000 ether) revert Invalid();\n    │                                        ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:170:13\n    │\n170 │             pot.credit(amount);\n    │             ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:416:17\n    │\n416 │                 emit ScoreChanged(currentSeason, who, score);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:426:9\n    │\n426 │         emit ScoreChanged(currentSeason, who, score);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:173:9\n    │\n173 │         emit Joined(currentSeason, msg.sender);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:196:9\n    │\n196 │         pot.credit(amount);\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:210:13\n    │\n210 │         if (block.timestamp < p.nextRoll) revert TooEarly();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:212:13\n    │\n212 │         if (block.timestamp + REVEAL_SECONDS >= seasons[currentSeason].end) revert Inactive();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_players` is updated\n    ╭▸ src/SwarmopolyGame.sol:346:9\n    │\n346 │         pot.pay(address(this), amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:311:17\n    │\n311 │                 vault.creditRent(slot, holders);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:315:13\n    │\n315 │             pot.credit(potAmount);\n    │             ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:317:9\n    │\n317 │         vault.awardSponsor(slot, who);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:230:62\n    │\n230 │         if (block.number > r.entropyBlock + REVEAL_BLOCKS || block.timestamp > r.deadline) revert Inactive();\n    │                                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:246:9\n    │\n246 │         emit Rolled(msg.sender, die1, die2, p.position);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:239:22\n    │\n239 │         uint8 die1 = uint8(entropy % 6 + 1);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:240:22\n    │\n240 │         uint8 die2 = uint8((entropy / 6) % 6 + 1);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:244:22\n    │\n244 │         p.position = uint8(destination % 40);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:337:13\n    │\n337 │             pot.credit(amount);\n    │             ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:253:63\n    │\n253 │         if (block.number <= r.entropyBlock + REVEAL_BLOCKS && block.timestamp <= r.deadline) revert TooEarly();\n    │                                                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:261:9\n    │\n261 │         emit RollExpired(who, r.exposure);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:277:17\n    │\n277 │             if (p.parkingDay != day) {\n    │                 ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:353:41\n    │\n353 │         if (rollsPaused || !p.jailed || block.timestamp < p.nextRoll) revert Invalid();\n    │                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:365:9\n    │\n365 │         emit JailLeft(msg.sender, true);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:394:33\n    │\n394 │         (amount, scoreAmount) = vault.claimRent(id, msg.sender);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:396:61\n    │\n396 │             _players[msg.sender].season == currentSeason && block.timestamp < seasons[currentSeason].end\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:449:9\n    │\n449 │         pot.reserve(total);\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SwarmopolyGame.sol:447:13\n    │\n447 │             total += amount;\n    │             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:437:35\n    │\n437 │         if (currentSeason == 0 || block.timestamp < s.end || s.finalized) revert Inactive();\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:450:9\n    │\n450 │         emit SeasonFinalized(currentSeason, total);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:457:9\n    │\n457 │         pot.payReserved(msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:458:9\n    │\n458 │         emit PrizeClaimed(id, msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":2518,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 11 tests for test/RevisionRegression.t.sol:RevisionRegressionTest\n[PASS] testCooldownPersistsWhenJoiningNewSeason() (gas: 1300129)\n[PASS] testEmptyListingCanBeCorrected() (gas: 1547002)\n[PASS] testExhaustedDustSponsorCanBeReplaced() (gas: 1806744)\n[PASS] testFundedSponsorCannotBeReplacedOrRateChanged() (gas: 683414)\n[PASS] testObservedDustDeedReceivesHolderShare() (gas: 1552381)\n[PASS] testObservedMissedRevealPenalty() (gas: 530311)\n[PASS] testObservedParkingPaysEachPlayerAndDownsideIsBalance() (gas: 2920964)\n[PASS] testRelistAfterRedemptionPreservesOldRentAndNewDeedBaseline() (gas: 4957981)\n[PASS] testSponsorLiabilitiesBlockRelistingAcrossSeasons() (gas: 1743753)\n[PASS] testZeroRentClaimDoesNotRankButPositiveScoreDoes() (gas: 5366345)\n[PASS] testZeroScoreJoinersLeaveAllPrizeBudgetAvailable() (gas: 2058507)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 63.31ms (19.46ms CPU time)\n\nRan 28 tests for test/Swarmopoly.t.sol:SwarmopolyTest\n[PASS] testApplicationRuntimeBoundsAndNoEscapeOpcodes() (gas: 10408031)\n[PASS] testBindingsAndOwnerRestrictions() (gas: 222247)\n[PASS] testBoardAndFundedListingRestrictions() (gas: 1205377)\n[PASS] testBuyQuoteSlippageAndOnePurchasePerLanding() (gas: 1782410)\n[PASS] testCommitFutureHashWrongSecretReplayAndCooldown() (gas: 682245)\n[PASS] testDonationCannotChangeSharesAndLockWeights() (gas: 2038685)\n[PASS] testExpiryLocksWorstCostAllowsExcessAndAnyoneResolves() (gas: 788244)\n[PASS] testFinalizeFullTopTenPullPrizesAndCarry() (gas: 9069268)\n[PASS] testFuzzRentAccounting(uint96) (runs: 256, μ: 1790469, ~: 1790612)\nLogs:\n  Bound result 4754504046826414170\n\n[PASS] testFuzzWithholdingNeverImprovesBalance(uint96) (runs: 256, μ: 716338, ~: 689935)\nLogs:\n  Bound result 99000000000000010509\n\n[PASS] testGoSalaryOnceChanceAndParkingDaily() (gas: 2062275)\n[PASS] testJoinDepositWithdrawalAndFailedTokenRollback() (gas: 404663)\n[PASS] testNoReturnERC20AndFailureIsolation() (gas: 2149374)\n[PASS] testOldRentCannotScoreInNewSeasonAndClaimSurvivesRedeem() (gas: 2231257)\n[PASS] testParameterCapsAndSeasonImmutability() (gas: 481076)\n[PASS] testParkingCannotPayTwiceSameDay() (gas: 2986306)\n[PASS] testPartialInputAndInvalidSettlementRevertAtomically() (gas: 1257305)\n[PASS] testPausedRollCanRevealAndNoETH() (gas: 589450)\n[PASS] testPendingRollCannotHoldSeasonOpenOrAffectFinalScores() (gas: 1043454)\n[PASS] testReentrantTokenCannotWithdrawOrJoinDuringDeposit() (gas: 396882)\n[PASS] testRentBankruptcyAndNextSeasonReset() (gas: 1010279)\n[PASS] testRentEightyTwentyClaimAndRedeemWhilePaused() (gas: 2055588)\n[PASS] testRevealBlockWindowLastBlockAndTimestampExpiry() (gas: 948753)\n[PASS] testSponsorFundsSeparateFromAssetsAndPullClaims() (gas: 1112958)\n[PASS] testSponsorTokenFailureDoesNotBlockLanding() (gas: 1103560)\n[PASS] testSwapOppositeCurrencyOrdering() (gas: 1259003)\n[PASS] testTaxJailBailAndSkip() (gas: 1612997)\n[PASS] testWeightedRentAndNoRetroactiveRewards() (gas: 3285384)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 63.63ms (175.63ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:AccountingInvariants\n[PASS]\nAccountingInvariants invariants:\n[PASS] invariant_gamePotRentAndVaultSolvent\n[PASS] invariant_rentNeverCreatesMoney\n[PASS] invariant_sharesAndWeightsMatchDeeds\n[PASS] invariant_topTenUniqueAndSorted\n AccountingInvariants invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------+-----------------+-------+---------+----------╮\n| Contract    | Selector        | Calls | Reverts | Discards |\n+============================================================+\n| GameHandler | claimRedeem     | 1615  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | depositWithdraw | 1657  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | newSeason       | 1675  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | rollBuy         | 1636  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | sponsorAndClaim | 1609  | 0       | 0        |\n╰-------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 10869\n  Bound result 274325993534050\n  Bound result 8807424000812005463\n  Bound result 11569\n  Bound result 11152\n  Bound result 10433\n  Bound result 11568\n  Bound result 1023\n  Bound result 9301\n  Bound result 736767119236076420\n  Bound result 58384586793081315985\n  Bound result 7498\n  Bound result 13665\n  Bound result 157950\n  Bound result 1563600688\n  Bound result 2696\n  Bound result 75629\n  Bound result 4807\n  Bound result 7774\n  Bound result 3900\n  Bound result 4897\n  Bound result 304625110\n  Bound result 415\n  Bound result 4135\n  Bound result 121\n  Bound result 7353\n  Bound result 2032\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.40s (2.39s CPU time)\n\nRan 3 test suites in 2.41s (2.53s CPU time): 40 tests passed, 0 failed, 0 skipped (40 total tests)\n","passed":true},{"durationMs":51,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DeedVault.awardSponsor(uint8,address)\",\"DeedVault.beginSeason(uint256,uint256)\",\"DeedVault.bindGame(address)\",\"DeedVault.buy(uint8,address,uint256,uint256,uint8)\",\"DeedVault.claimRent(uint256,address)\",\"DeedVault.claimSponsor(uint8)\",\"DeedVault.creditRent(uint8,uint256)\",\"DeedVault.listTile(uint8,(address,address,uint24,int24,address),uint8)\",\"DeedVault.quote(uint8,uint256)\",\"DeedVault.redeem(uint256)\",\"DeedVault.sponsorTile(uint8,uint256,uint256)\",\"DeedVault.unlockCallback(bytes)\",\"DeedVault.withdrawSponsor(uint256,uint8)\",\"SeasonPot.bindGame(address)\",\"SeasonPot.credit(uint256)\",\"SeasonPot.pay(address,uint256)\",\"SeasonPot.payReserved(address,uint256)\",\"SeasonPot.reserve(uint256)\",\"SwarmopolyGame.buyDeed(uint8,uint256,uint256)\",\"SwarmopolyGame.buyDeed(uint8,uint256,uint256,uint8)\",\"SwarmopolyGame.claimPrize(uint256)\",\"SwarmopolyGame.claimRent(uint256)\",\"SwarmopolyGame.commitRoll(bytes32)\",\"SwarmopolyGame.deposit(uint256)\",\"SwarmopolyGame.expireRoll(address)\",\"SwarmopolyGame.finalizeSeason()\",\"SwarmopolyGame.fundPot(uint256)\",\"SwarmopolyGame.joinSeason()\",\"SwarmopolyGame.listTile(uint8,(address,address,uint24,int24,address),uint8)\",\"SwarmopolyGame.payBail()\",\"SwarmopolyGame.revealRoll(bytes32)\",\"SwarmopolyGame.setParams(uint256,uint256,uint256[8])\",\"SwarmopolyGame.setPaused(bool,bool)\",\"SwarmopolyGame.skipJailRoll()\",\"SwarmopolyGame.startSeason(uint256,uint256,uint256,uint256)\",\"SwarmopolyGame.withdraw(uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":24,\"docs/DEPLOYMENT.md\":52,\"docs/REVIEW.md\":45,\"docs/RULES.md\":45,\"docs/TESTING.md\":33,\"docs/check-results.json\":26,\"docs/network-verification.json\":14,\"foundry.toml\":21,\"launch.json\":30,\"scripts/configure-site.mjs\":20,\"scripts/export-abi.py\":7,\"scripts/verify-network.py\":34,\"site/abi.json\":1,\"site/app.js\":245,\"site/config.json\":13,\"site/index.html\":15,\"site/package.json\":1,\"site/rules.html\":1,\"site/style.css\":2,\"site/vendor/ethers.LICENSE.md\":21,\"site/vendor/ethers.min.js\":1,\"src/DeedVault.sol\":319,\"src/Owned.sol\":17,\"src/SeasonPot.sol\":60,\"src/SwarmopolyGame.sol\":460,\"src/interfaces/IV4.sol\":35,\"test/Invariants.t.sol\":179,\"test/RevisionRegression.t.sol\":295,\"test/Swarmopoly.t.sol\":691,\"test/mocks/MockERC20.sol\":60,\"test/mocks/MockPoolManager.sol\":66,\"test/site.test.mjs\":51},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"0e62e57cb09a06dd7f4c9bd2141960ea91103b2a4886d6d9e09e8c912859d924","verifiedTreeHash":"3159f46cb49175e678ca552b8eb0226723f97b24","verifierVersion":"0.1.0+be003835"},{"checks":[{"durationMs":15896,"exitCode":0,"name":"build","output":"Compiling 34 files with Solc 0.8.26\nSolc 0.8.26 finished in 15.78s\nCompiler run successful!\nwarning[missing-events-access-control]: `swapContext` is changed without an event but is used for access control\n    ╭▸ src/DeedVault.sol:170:9\n    │\n170 │         swapContext = keccak256(data);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `swapContext` is changed without an event but is used for access control\n    ╭▸ src/DeedVault.sol:189:9\n    │\n189 │         swapContext = bytes32(0);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:105:35\n    │\n105 │         if (currentSeason == 0 || block.timestamp >= seasons[currentSeason].end || seasons[currentSeason].finalized) {\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SeasonPot.sol:28:23\n   │\n28 │     function bindGame(address game_) external onlyOwner {\n   │                       ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/SeasonPot.sol:43:64\n   │\n43 │     function pay(address to, uint256 amount) external onlyGame nonReentrant {\n   │                                                                ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/SeasonPot.sol:55:72\n   │\n55 │     function payReserved(address to, uint256 amount) external onlyGame nonReentrant {\n   │                                                                        ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:116:9\n    │\n116 │         vault.listTile(slot, key, tier);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/SwarmopolyGame.sol:115:88\n    │\n115 │     function listTile(uint8 slot, PoolKey calldata key, uint8 tier) external onlyOwner nonReentrant {\n    │                                                                                        ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/DeedVault.sol:79:23\n   │\n79 │     function bindGame(address game_) external onlyOwner {\n   │                       ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/DeedVault.sol:171:13\n    │\n171 │         try poolManager.unlock(data) returns (bytes memory result) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/DeedVault.sol:131:9\n    │\n131 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:157:9\n    │\n157 │         emit DeedBought(id, holder, slot, out, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:166:73\n    │\n166 │         if (_tiles[slot].token == address(0) || amount == 0 || amount > uint256(uint128(type(int128).max))) {\n    │                                                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:166:81\n    │\n166 │         if (_tiles[slot].token == address(0) || amount == 0 || amount > uint256(uint128(type(int128).max))) {\n    │                                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:176:51\n    │\n176 │             if (quoting && reason.length == 36 && bytes4(reason) == QuoteResult.selector) {\n    │                                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:133:9\n    │\n133 │         vault.beginSeason(currentSeason, end);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/SwarmopolyGame.sol:122:9\n    │\n122 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:196:30\n    │\n196 │                 zeroForOne, -int256(amount), zeroForOne ? 4295128740 : 1461446703485210103287273052203988822378723970341\n    │                              ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:134:9\n    │\n134 │         emit SeasonStarted(currentSeason, buyIn, end, rollBond, maxBuy);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:200:21\n    │\n200 │         int128 d0 = int128(delta >> 128);\n    │                     ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:128:29\n    │\n128 │                 || maxBuy > uint256(uint128(type(int128).max))\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:128:37\n    │\n128 │                 || maxBuy > uint256(uint128(type(int128).max))\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:201:21\n    │\n201 │         int128 d1 = int128(delta);\n    │                     ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:204:23\n    │\n204 │         if (input != -int256(amount) || output <= 0) revert SwapFailed();\n    │                       ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:205:23\n    │\n205 │         uint256 out = uint256(output);\n    │                       ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:219:9\n    │\n219 │         emit RentCredited(slot, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/DeedVault.sol:243:9\n    │\n243 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:255:9\n    │\n255 │         emit RentClaimed(id, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:261:30\n    │\n261 │         if (d.shares == 0 || block.timestamp < d.redeemAt) revert Invalid();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:271:9\n    │\n271 │         emit Redeemed(id, assets);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:277:35\n    │\n277 │             currentSeason == 0 || block.timestamp >= seasonEnd[currentSeason] || _tiles[slot].token == address(0)\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:310:13\n    │\n310 │         if (block.timestamp < seasonEnd[id] || s.remaining == 0) revert Invalid();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SwarmopolyGame.sol:142:40\n    │\n142 │             if (rents[i] > 1000 ether) revert Invalid();\n    │                                        ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:171:13\n    │\n171 │             pot.credit(amount);\n    │             ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:416:17\n    │\n416 │                 emit ScoreChanged(currentSeason, who, score);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:426:9\n    │\n426 │         emit ScoreChanged(currentSeason, who, score);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:174:9\n    │\n174 │         emit Joined(currentSeason, msg.sender);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:197:9\n    │\n197 │         pot.credit(amount);\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:211:13\n    │\n211 │         if (block.timestamp < p.nextRoll) revert TooEarly();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:213:13\n    │\n213 │         if (block.timestamp + REVEAL_SECONDS >= seasons[currentSeason].end) revert Inactive();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_players` is updated\n    ╭▸ src/SwarmopolyGame.sol:347:9\n    │\n347 │         pot.pay(address(this), amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:312:17\n    │\n312 │                 vault.creditRent(slot, holders);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:316:13\n    │\n316 │             pot.credit(potAmount);\n    │             ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:318:9\n    │\n318 │         vault.awardSponsor(slot, who);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:231:62\n    │\n231 │         if (block.number > r.entropyBlock + REVEAL_BLOCKS || block.timestamp > r.deadline) revert Inactive();\n    │                                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:247:9\n    │\n247 │         emit Rolled(msg.sender, die1, die2, p.position);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:240:22\n    │\n240 │         uint8 die1 = uint8(entropy % 6 + 1);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:241:22\n    │\n241 │         uint8 die2 = uint8((entropy / 6) % 6 + 1);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:245:22\n    │\n245 │         p.position = uint8(destination % 40);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:338:13\n    │\n338 │             pot.credit(amount);\n    │             ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:254:63\n    │\n254 │         if (block.number <= r.entropyBlock + REVEAL_BLOCKS && block.timestamp <= r.deadline) revert TooEarly();\n    │                                                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:262:9\n    │\n262 │         emit RollExpired(who, r.exposure);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:278:17\n    │\n278 │             if (p.parkingDay != day) {\n    │                 ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:354:41\n    │\n354 │         if (rollsPaused || !p.jailed || block.timestamp < p.nextRoll) revert Invalid();\n    │                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:366:9\n    │\n366 │         emit JailLeft(msg.sender, true);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:395:33\n    │\n395 │         (amount, scoreAmount) = vault.claimRent(id, msg.sender);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:397:61\n    │\n397 │             _players[msg.sender].season == currentSeason && block.timestamp < seasons[currentSeason].end\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:449:9\n    │\n449 │         pot.reserve(total);\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SwarmopolyGame.sol:447:13\n    │\n447 │             total += amount;\n    │             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:437:35\n    │\n437 │         if (currentSeason == 0 || block.timestamp < s.end || s.finalized) revert Inactive();\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:450:9\n    │\n450 │         emit SeasonFinalized(currentSeason, total);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:457:9\n    │\n457 │         pot.payReserved(msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:458:9\n    │\n458 │         emit PrizeClaimed(id, msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":2878,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 28 tests for test/Swarmopoly.t.sol:SwarmopolyTest\n[PASS] testApplicationRuntimeBoundsAndNoEscapeOpcodes() (gas: 10373522)\n[PASS] testBindingsAndOwnerRestrictions() (gas: 222247)\n[PASS] testBoardAndImmutableListing() (gas: 334957)\n[PASS] testBuyQuoteSlippageAndOnePurchasePerLanding() (gas: 1782410)\n[PASS] testCommitFutureHashWrongSecretReplayAndCooldown() (gas: 682245)\n[PASS] testDonationCannotChangeSharesAndLockWeights() (gas: 2038685)\n[PASS] testExpiryLocksWorstCostAllowsExcessAndAnyoneResolves() (gas: 788244)\n[PASS] testFinalizeFullTopTenPullPrizesAndCarry() (gas: 4891809)\n[PASS] testFuzzRentAccounting(uint96) (runs: 256, μ: 1757441, ~: 1757337)\nLogs:\n  Bound result 42430423629587659\n\n[PASS] testFuzzWithholdingNeverImprovesBalance(uint96) (runs: 256, μ: 718016, ~: 689952)\nLogs:\n  Bound result 99000000000000046878\n\n[PASS] testGoSalaryOnceChanceAndParkingDaily() (gas: 2032215)\n[PASS] testJoinDepositWithdrawalAndFailedTokenRollback() (gas: 404663)\n[PASS] testNoReturnERC20AndFailureIsolation() (gas: 2119338)\n[PASS] testOldRentCannotScoreInNewSeasonAndClaimSurvivesRedeem() (gas: 2357810)\n[PASS] testParameterCapsAndSeasonImmutability() (gas: 548510)\n[PASS] testParkingCannotPayTwiceSameDay() (gas: 3060628)\n[PASS] testPartialInputAndInvalidSettlementRevertAtomically() (gas: 1257305)\n[PASS] testPausedRollCanRevealAndNoETH() (gas: 589450)\n[PASS] testPendingRollCannotHoldSeasonOpenOrAffectFinalScores() (gas: 1164988)\n[PASS] testReentrantTokenCannotWithdrawOrJoinDuringDeposit() (gas: 396882)\n[PASS] testRentBankruptcyAndNextSeasonReset() (gas: 1131791)\n[PASS] testRentEightyTwentyClaimAndRedeemWhilePaused() (gas: 2025574)\n[PASS] testRevealBlockWindowLastBlockAndTimestampExpiry() (gas: 948753)\n[PASS] testSponsorFundsSeparateFromAssetsAndPullClaims() (gas: 1112984)\n[PASS] testSponsorTokenFailureDoesNotBlockLanding() (gas: 1103586)\n[PASS] testSwapOppositeCurrencyOrdering() (gas: 1253116)\n[PASS] testTaxJailBailAndSkip() (gas: 1612997)\n[PASS] testWeightedRentAndNoRetroactiveRewards() (gas: 3312785)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 57.02ms (122.79ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:AccountingInvariants\n[PASS]\nAccountingInvariants invariants:\n[PASS] invariant_gamePotRentAndVaultSolvent\n[PASS] invariant_rentNeverCreatesMoney\n[PASS] invariant_sharesAndWeightsMatchDeeds\n[PASS] invariant_topTenUniqueAndSorted\n AccountingInvariants invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------+-----------------+-------+---------+----------╮\n| Contract    | Selector        | Calls | Reverts | Discards |\n+============================================================+\n| GameHandler | claimRedeem     | 1668  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | depositWithdraw | 1595  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | newSeason       | 1625  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | rollBuy         | 1657  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | sponsorAndClaim | 1647  | 0       | 0        |\n╰-------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3301\n  Bound result 3004440495439\n  Bound result 4413\n  Bound result 3088\n  Bound result 3444007447\n  Bound result 5187\n  Bound result 7992\n  Bound result 5504\n  Bound result 14690410091086119722\n  Bound result 7850\n  Bound result 3729553\n  Bound result 98619224384202674868\n  Bound result 7500324288361088659\n  Bound result 1479\n  Bound result 10056\n  Bound result 3076\n  Bound result 12077\n  Bound result 1876\n  Bound result 4420819695790500940\n  Bound result 55639851435769445371\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.79s (2.79s CPU time)\n\nRan 2 test suites in 2.79s (2.85s CPU time): 29 tests passed, 0 failed, 0 skipped (29 total tests)\n","passed":true},{"durationMs":47,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DeedVault.awardSponsor(uint8,address)\",\"DeedVault.beginSeason(uint256,uint256)\",\"DeedVault.bindGame(address)\",\"DeedVault.buy(uint8,address,uint256,uint256,uint8)\",\"DeedVault.claimRent(uint256,address)\",\"DeedVault.claimSponsor(uint8)\",\"DeedVault.creditRent(uint8,uint256)\",\"DeedVault.listTile(uint8,(address,address,uint24,int24,address),uint8)\",\"DeedVault.quote(uint8,uint256)\",\"DeedVault.redeem(uint256)\",\"DeedVault.sponsorTile(uint8,uint256,uint256)\",\"DeedVault.unlockCallback(bytes)\",\"DeedVault.withdrawSponsor(uint256,uint8)\",\"SeasonPot.bindGame(address)\",\"SeasonPot.credit(uint256)\",\"SeasonPot.pay(address,uint256)\",\"SeasonPot.payReserved(address,uint256)\",\"SeasonPot.reserve(uint256)\",\"SwarmopolyGame.buyDeed(uint8,uint256,uint256)\",\"SwarmopolyGame.buyDeed(uint8,uint256,uint256,uint8)\",\"SwarmopolyGame.claimPrize(uint256)\",\"SwarmopolyGame.claimRent(uint256)\",\"SwarmopolyGame.commitRoll(bytes32)\",\"SwarmopolyGame.deposit(uint256)\",\"SwarmopolyGame.expireRoll(address)\",\"SwarmopolyGame.finalizeSeason()\",\"SwarmopolyGame.fundPot(uint256)\",\"SwarmopolyGame.joinSeason()\",\"SwarmopolyGame.listTile(uint8,(address,address,uint24,int24,address),uint8)\",\"SwarmopolyGame.payBail()\",\"SwarmopolyGame.revealRoll(bytes32)\",\"SwarmopolyGame.setParams(uint256,uint256,uint256[8])\",\"SwarmopolyGame.setPaused(bool,bool)\",\"SwarmopolyGame.skipJailRoll()\",\"SwarmopolyGame.startSeason(uint256,uint256,uint256,uint256)\",\"SwarmopolyGame.withdraw(uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":24,\"docs/DEPLOYMENT.md\":52,\"docs/REVIEW.md\":43,\"docs/RULES.md\":45,\"docs/TESTING.md\":25,\"docs/check-results.json\":24,\"docs/network-verification.json\":14,\"foundry.toml\":21,\"launch.json\":30,\"scripts/configure-site.mjs\":20,\"scripts/export-abi.py\":7,\"scripts/verify-network.py\":34,\"site/abi.json\":1,\"site/app.js\":245,\"site/config.json\":13,\"site/index.html\":15,\"site/package.json\":1,\"site/rules.html\":1,\"site/style.css\":2,\"site/vendor/ethers.LICENSE.md\":21,\"site/vendor/ethers.min.js\":1,\"src/DeedVault.sol\":317,\"src/Owned.sol\":17,\"src/SeasonPot.sol\":60,\"src/SwarmopolyGame.sol\":460,\"src/interfaces/IV4.sol\":35,\"test/Invariants.t.sol\":178,\"test/Swarmopoly.t.sol\":679,\"test/mocks/MockERC20.sol\":60,\"test/mocks/MockPoolManager.sol\":66,\"test/site.test.mjs\":51},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"6d3092bfce8b31685ed2625f7ff14f1d6cab573d71bcc3eecf3e580d29d983e7","verifiedTreeHash":"088cb85ff3bc2faf5544c717903a22aa0297a3c6","verifierVersion":"0.1.0+be003835"},{"checks":[{"durationMs":17733,"exitCode":0,"name":"build","output":"Compiling 34 files with Solc 0.8.26\nSolc 0.8.26 finished in 17.59s\nCompiler run successful!\nwarning[missing-events-access-control]: `swapContext` is changed without an event but is used for access control\n    ╭▸ src/DeedVault.sol:170:9\n    │\n170 │         swapContext = keccak256(data);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `swapContext` is changed without an event but is used for access control\n    ╭▸ src/DeedVault.sol:189:9\n    │\n189 │         swapContext = bytes32(0);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:105:35\n    │\n105 │         if (currentSeason == 0 || block.timestamp >= seasons[currentSeason].end || seasons[currentSeason].finalized) {\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SeasonPot.sol:28:23\n   │\n28 │     function bindGame(address game_) external onlyOwner {\n   │                       ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/SeasonPot.sol:43:64\n   │\n43 │     function pay(address to, uint256 amount) external onlyGame nonReentrant {\n   │                                                                ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/SeasonPot.sol:55:72\n   │\n55 │     function payReserved(address to, uint256 amount) external onlyGame nonReentrant {\n   │                                                                        ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:116:9\n    │\n116 │         vault.listTile(slot, key, tier);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/SwarmopolyGame.sol:115:88\n    │\n115 │     function listTile(uint8 slot, PoolKey calldata key, uint8 tier) external onlyOwner nonReentrant {\n    │                                                                                        ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/DeedVault.sol:79:23\n   │\n79 │     function bindGame(address game_) external onlyOwner {\n   │                       ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/DeedVault.sol:171:13\n    │\n171 │         try poolManager.unlock(data) returns (bytes memory result) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/DeedVault.sol:131:9\n    │\n131 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:157:9\n    │\n157 │         emit DeedBought(id, holder, slot, out, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:133:9\n    │\n133 │         vault.beginSeason(currentSeason, end);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/SwarmopolyGame.sol:122:9\n    │\n122 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:134:9\n    │\n134 │         emit SeasonStarted(currentSeason, buyIn, end, rollBond, maxBuy);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:128:29\n    │\n128 │                 || maxBuy > uint256(uint128(type(int128).max))\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:128:37\n    │\n128 │                 || maxBuy > uint256(uint128(type(int128).max))\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:166:73\n    │\n166 │         if (_tiles[slot].token == address(0) || amount == 0 || amount > uint256(uint128(type(int128).max))) {\n    │                                                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:166:81\n    │\n166 │         if (_tiles[slot].token == address(0) || amount == 0 || amount > uint256(uint128(type(int128).max))) {\n    │                                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:176:51\n    │\n176 │             if (quoting && reason.length == 36 && bytes4(reason) == QuoteResult.selector) {\n    │                                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:196:30\n    │\n196 │                 zeroForOne, -int256(amount), zeroForOne ? 4295128740 : 1461446703485210103287273052203988822378723970341\n    │                              ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:200:21\n    │\n200 │         int128 d0 = int128(delta >> 128);\n    │                     ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:201:21\n    │\n201 │         int128 d1 = int128(delta);\n    │                     ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:204:23\n    │\n204 │         if (input != -int256(amount) || output <= 0) revert SwapFailed();\n    │                       ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:205:23\n    │\n205 │         uint256 out = uint256(output);\n    │                       ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:219:9\n    │\n219 │         emit RentCredited(slot, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/DeedVault.sol:243:9\n    │\n243 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:255:9\n    │\n255 │         emit RentClaimed(id, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:261:30\n    │\n261 │         if (d.shares == 0 || block.timestamp < d.redeemAt) revert Invalid();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:271:9\n    │\n271 │         emit Redeemed(id, assets);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:277:35\n    │\n277 │             currentSeason == 0 || block.timestamp >= seasonEnd[currentSeason] || _tiles[slot].token == address(0)\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SwarmopolyGame.sol:142:40\n    │\n142 │             if (rents[i] > 1000 ether) revert Invalid();\n    │                                        ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:310:13\n    │\n310 │         if (block.timestamp < seasonEnd[id] || s.remaining == 0) revert Invalid();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:171:13\n    │\n171 │             pot.credit(amount);\n    │             ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:416:17\n    │\n416 │                 emit ScoreChanged(currentSeason, who, score);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:426:9\n    │\n426 │         emit ScoreChanged(currentSeason, who, score);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:174:9\n    │\n174 │         emit Joined(currentSeason, msg.sender);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:197:9\n    │\n197 │         pot.credit(amount);\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:211:13\n    │\n211 │         if (block.timestamp < p.nextRoll) revert TooEarly();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:213:13\n    │\n213 │         if (block.timestamp + REVEAL_SECONDS >= seasons[currentSeason].end) revert Inactive();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_players` is updated\n    ╭▸ src/SwarmopolyGame.sol:347:9\n    │\n347 │         pot.pay(address(this), amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:312:17\n    │\n312 │                 vault.creditRent(slot, holders);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:316:13\n    │\n316 │             pot.credit(potAmount);\n    │             ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:318:9\n    │\n318 │         vault.awardSponsor(slot, who);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:231:62\n    │\n231 │         if (block.number > r.entropyBlock + REVEAL_BLOCKS || block.timestamp > r.deadline) revert Inactive();\n    │                                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:247:9\n    │\n247 │         emit Rolled(msg.sender, die1, die2, p.position);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:240:22\n    │\n240 │         uint8 die1 = uint8(entropy % 6 + 1);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:241:22\n    │\n241 │         uint8 die2 = uint8((entropy / 6) % 6 + 1);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:245:22\n    │\n245 │         p.position = uint8(destination % 40);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:338:13\n    │\n338 │             pot.credit(amount);\n    │             ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:254:63\n    │\n254 │         if (block.number <= r.entropyBlock + REVEAL_BLOCKS && block.timestamp <= r.deadline) revert TooEarly();\n    │                                                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:262:9\n    │\n262 │         emit RollExpired(who, r.exposure);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:278:17\n    │\n278 │             if (p.parkingDay != day) {\n    │                 ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:354:41\n    │\n354 │         if (rollsPaused || !p.jailed || block.timestamp < p.nextRoll) revert Invalid();\n    │                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:366:9\n    │\n366 │         emit JailLeft(msg.sender, true);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:395:33\n    │\n395 │         (amount, scoreAmount) = vault.claimRent(id, msg.sender);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:397:61\n    │\n397 │             _players[msg.sender].season == currentSeason && block.timestamp < seasons[currentSeason].end\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:449:9\n    │\n449 │         pot.reserve(total);\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SwarmopolyGame.sol:447:13\n    │\n447 │             total += amount;\n    │             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:437:35\n    │\n437 │         if (currentSeason == 0 || block.timestamp < s.end || s.finalized) revert Inactive();\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:450:9\n    │\n450 │         emit SeasonFinalized(currentSeason, total);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:457:9\n    │\n457 │         pot.payReserved(msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:458:9\n    │\n458 │         emit PrizeClaimed(id, msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":3305,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 28 tests for test/Swarmopoly.t.sol:SwarmopolyTest\n[PASS] testApplicationRuntimeBoundsAndNoEscapeOpcodes() (gas: 10373522)\n[PASS] testBindingsAndOwnerRestrictions() (gas: 222247)\n[PASS] testBoardAndImmutableListing() (gas: 334957)\n[PASS] testBuyQuoteSlippageAndOnePurchasePerLanding() (gas: 1782410)\n[PASS] testCommitFutureHashWrongSecretReplayAndCooldown() (gas: 682245)\n[PASS] testDonationCannotChangeSharesAndLockWeights() (gas: 2038685)\n[PASS] testExpiryLocksWorstCostAllowsExcessAndAnyoneResolves() (gas: 788244)\n[PASS] testFinalizeFullTopTenPullPrizesAndCarry() (gas: 4891809)\n[PASS] testFuzzRentAccounting(uint96) (runs: 256, μ: 1757447, ~: 1757318)\nLogs:\n  Bound result 98688618777916067318\n\n[PASS] testFuzzWithholdingNeverImprovesBalance(uint96) (runs: 256, μ: 719292, ~: 702955)\nLogs:\n  Bound result 99000000000000003503\n\n[PASS] testGoSalaryOnceChanceAndParkingDaily() (gas: 2032215)\n[PASS] testJoinDepositWithdrawalAndFailedTokenRollback() (gas: 404663)\n[PASS] testNoReturnERC20AndFailureIsolation() (gas: 2119338)\n[PASS] testOldRentCannotScoreInNewSeasonAndClaimSurvivesRedeem() (gas: 2357810)\n[PASS] testParameterCapsAndSeasonImmutability() (gas: 548510)\n[PASS] testParkingCannotPayTwiceSameDay() (gas: 3060628)\n[PASS] testPartialInputAndInvalidSettlementRevertAtomically() (gas: 1257305)\n[PASS] testPausedRollCanRevealAndNoETH() (gas: 589450)\n[PASS] testPendingRollCannotHoldSeasonOpenOrAffectFinalScores() (gas: 1164988)\n[PASS] testReentrantTokenCannotWithdrawOrJoinDuringDeposit() (gas: 396882)\n[PASS] testRentBankruptcyAndNextSeasonReset() (gas: 1131791)\n[PASS] testRentEightyTwentyClaimAndRedeemWhilePaused() (gas: 2025574)\n[PASS] testRevealBlockWindowLastBlockAndTimestampExpiry() (gas: 948753)\n[PASS] testSponsorFundsSeparateFromAssetsAndPullClaims() (gas: 1112984)\n[PASS] testSponsorTokenFailureDoesNotBlockLanding() (gas: 1103586)\n[PASS] testSwapOppositeCurrencyOrdering() (gas: 1253116)\n[PASS] testTaxJailBailAndSkip() (gas: 1612997)\n[PASS] testWeightedRentAndNoRetroactiveRewards() (gas: 3312785)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 46.75ms (122.84ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:AccountingInvariants\n[PASS]\nAccountingInvariants invariants:\n[PASS] invariant_gamePotRentAndVaultSolvent\n[PASS] invariant_rentNeverCreatesMoney\n[PASS] invariant_sharesAndWeightsMatchDeeds\n[PASS] invariant_topTenUniqueAndSorted\n AccountingInvariants invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------+-----------------+-------+---------+----------╮\n| Contract    | Selector        | Calls | Reverts | Discards |\n+============================================================+\n| GameHandler | claimRedeem     | 1683  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | depositWithdraw | 1602  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | newSeason       | 1600  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | rollBuy         | 1633  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | sponsorAndClaim | 1674  | 0       | 0        |\n╰-------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1\n  Bound result 8928\n  Bound result 35918542076458788965\n  Bound result 9877\n  Bound result 8509\n  Bound result 14652971048047\n  Bound result 4222\n  Bound result 5192\n  Bound result 2174428601\n  Bound result 1054\n  Bound result 2518410128\n  Bound result 972\n  Bound result 9155\n  Bound result 19305953694233576564\n  Bound result 8034\n  Bound result 5063\n  Bound result 3\n  Bound result 6876347495411\n  Bound result 5699\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.21s (3.21s CPU time)\n\nRan 2 test suites in 3.21s (3.26s CPU time): 29 tests passed, 0 failed, 0 skipped (29 total tests)\n","passed":true},{"durationMs":53,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DeedVault.awardSponsor(uint8,address)\",\"DeedVault.beginSeason(uint256,uint256)\",\"DeedVault.bindGame(address)\",\"DeedVault.buy(uint8,address,uint256,uint256,uint8)\",\"DeedVault.claimRent(uint256,address)\",\"DeedVault.claimSponsor(uint8)\",\"DeedVault.creditRent(uint8,uint256)\",\"DeedVault.listTile(uint8,(address,address,uint24,int24,address),uint8)\",\"DeedVault.quote(uint8,uint256)\",\"DeedVault.redeem(uint256)\",\"DeedVault.sponsorTile(uint8,uint256,uint256)\",\"DeedVault.unlockCallback(bytes)\",\"DeedVault.withdrawSponsor(uint256,uint8)\",\"SeasonPot.bindGame(address)\",\"SeasonPot.credit(uint256)\",\"SeasonPot.pay(address,uint256)\",\"SeasonPot.payReserved(address,uint256)\",\"SeasonPot.reserve(uint256)\",\"SwarmopolyGame.buyDeed(uint8,uint256,uint256)\",\"SwarmopolyGame.buyDeed(uint8,uint256,uint256,uint8)\",\"SwarmopolyGame.claimPrize(uint256)\",\"SwarmopolyGame.claimRent(uint256)\",\"SwarmopolyGame.commitRoll(bytes32)\",\"SwarmopolyGame.deposit(uint256)\",\"SwarmopolyGame.expireRoll(address)\",\"SwarmopolyGame.finalizeSeason()\",\"SwarmopolyGame.fundPot(uint256)\",\"SwarmopolyGame.joinSeason()\",\"SwarmopolyGame.listTile(uint8,(address,address,uint24,int24,address),uint8)\",\"SwarmopolyGame.payBail()\",\"SwarmopolyGame.revealRoll(bytes32)\",\"SwarmopolyGame.setParams(uint256,uint256,uint256[8])\",\"SwarmopolyGame.setPaused(bool,bool)\",\"SwarmopolyGame.skipJailRoll()\",\"SwarmopolyGame.startSeason(uint256,uint256,uint256,uint256)\",\"SwarmopolyGame.withdraw(uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":24,\"docs/DEPLOYMENT.md\":52,\"docs/REVIEW.md\":43,\"docs/RULES.md\":45,\"docs/TESTING.md\":25,\"docs/check-results.json\":24,\"docs/network-verification.json\":14,\"foundry.toml\":21,\"launch.json\":9,\"scripts/configure-site.mjs\":20,\"scripts/export-abi.py\":7,\"scripts/verify-network.py\":34,\"site/abi.json\":1,\"site/app.js\":245,\"site/config.json\":13,\"site/index.html\":15,\"site/package.json\":1,\"site/rules.html\":1,\"site/style.css\":2,\"site/vendor/ethers.LICENSE.md\":21,\"site/vendor/ethers.min.js\":1,\"src/DeedVault.sol\":317,\"src/Owned.sol\":17,\"src/SeasonPot.sol\":60,\"src/SwarmopolyGame.sol\":460,\"src/interfaces/IV4.sol\":35,\"test/Invariants.t.sol\":178,\"test/Swarmopoly.t.sol\":679,\"test/mocks/MockERC20.sol\":60,\"test/mocks/MockPoolManager.sol\":66,\"test/site.test.mjs\":51},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":4530,"exitCode":0,"name":"slither","output":"[high/medium] weak-prng at src/SwarmopolyGame.sol:227: SwarmopolyGame.revealRoll(bytes32) (src/SwarmopolyGame.sol#227-248) uses a weak PRNG: \"die1 = uint8(entropy % 6 + 1) (src/SwarmopolyGame.sol#240)\"\n[high/medium] weak-prng at src/SwarmopolyGame.sol:227: SwarmopolyGame.revealRoll(bytes32) (src/SwarmopolyGame.sol#227-248) uses a weak PRNG: \"die2 = uint8((entropy / 6) % 6 + 1) (src/SwarmopolyGame.sol#241)\"\n[high/medium] weak-prng at src/SwarmopolyGame.sol:265: SwarmopolyGame._land(address,uint256,bool) (src/SwarmopolyGame.sol#265-320) uses a weak PRNG: \"card = (entropy >> 16) % 5 (src/SwarmopolyGame.sol#286)\"\n[high/medium] weak-prng at src/SwarmopolyGame.sol:227: SwarmopolyGame.revealRoll(bytes32) (src/SwarmopolyGame.sol#227-248) uses a weak PRNG: \"p.position = uint8(destination % 40) (src/SwarmopolyGame.sol#245)\"\n[high/high] uninitialized-state at src/SwarmopolyGame.sol:95: SwarmopolyGame._leaders (src/SwarmopolyGame.sol#62) is never initialized. It is used in:\n[medium/high] incorrect-equality at src/SwarmopolyGame.sol:393: SwarmopolyGame.claimRent(uint256) (src/SwarmopolyGame.sol#393-400) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/SwarmopolyGame.sol:265: Reentrancy in SwarmopolyGame._land(address,uint256,bool) (src/SwarmopolyGame.sol#265-320):\n[medium/medium] reentrancy-no-eth at src/SwarmopolyGame.sol:227: Reentrancy in SwarmopolyGame.revealRoll(bytes32) (src/SwarmopolyGame.sol#227-248):\n[medium/medium] reentrancy-no-eth at src/DeedVault.sol:128: Reentrancy in DeedVault.buy(uint8,address,uint256,uint256,uint8) (src/DeedVault.sol#128-158):\n[medium/medium] uninitialized-local at src/SwarmopolyGame.sol:441: SwarmopolyGame.finalizeSeason().total (src/SwarmopolyGame.sol#441) is a local variable never initialized\n[low/medium] events-maths at src/DeedVault.sol:119: DeedVault.beginSeason(uint256,uint256) (src/DeedVault.sol#119-125) should emit an event for: \n[low/medium] reentrancy-benign at src/SwarmopolyGame.sol:393: Reentrancy in SwarmopolyGame.claimRent(uint256) (src/SwarmopolyGame.sol#393-400):\n[low/medium] reentrancy-benign at src/DeedVault.sol:128: Reentrancy in DeedVault.buy(uint8,address,uint256,uint256,uint8) (src/DeedVault.sol#128-158):\n[low/medium] reentrancy-benign at src/DeedVault.sol:165: Reentrancy in DeedVault._swap(uint8,uint256,bool) (src/DeedVault.sol#165-183):\n[low/medium] reentrancy-benign at src/SwarmopolyGame.sol:342: Reentrancy in SwarmopolyGame._reward(address,uint256) (src/SwarmopolyGame.sol#342-349):\n[low/medium] reentrancy-benign at src/SwarmopolyGame.sol:156: Reentrancy in SwarmopolyGame.joinSeason() (src/SwarmopolyGame.sol#156-175):\n[low/medium] timestamp at src/SwarmopolyGame.sol:227: SwarmopolyGame.revealRoll(bytes32) (src/SwarmopolyGame.sol#227-248) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:119: SwarmopolyGame.startSeason(uint256,uint256,uint256,uint256) (src/SwarmopolyGame.sol#119-135) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:156: SwarmopolyGame.joinSeason() (src/SwarmopolyGame.sol#156-175) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:204: SwarmopolyGame.commitRoll(bytes32) (src/SwarmopolyGame.sol#204-225) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:104: SwarmopolyGame._active() (src/SwarmopolyGame.sol#104-108) uses timestamp for comparisons\n[low/medium] timestamp at src/DeedVault.sol:307: DeedVault.withdrawSponsor(uint256,uint8) (src/DeedVault.sol#307-316) uses timestamp for comparisons\n[low/medium] timestamp at src/DeedVault.sol:275: DeedVault.sponsorTile(uint8,uint256,uint256) (src/DeedVault.sol#275-288) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:333: SwarmopolyGame._toPot(address,uint256) (src/SwarmopolyGame.sol#333-340) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:435: SwarmopolyGame.finalizeSeason() (src/SwarmopolyGame.sol#435-451) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:251: SwarmopolyGame.expireRoll(address) (src/SwarmopolyGame.sol#251-263) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:393: SwarmopolyGame.claimRent(uint256) (src/SwarmopolyGame.sol#393-400) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:351: SwarmopolyGame.skipJailRoll() (src/SwarmopolyGame.sol#351-358) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:265: SwarmopolyGame._land(address,uint256,bool) (src/SwarmopolyGame.sol#265-320) uses timestamp for comparisons\n[low/medium] timestamp at src/DeedVault.sol:258: DeedVault.redeem(uint256) (src/DeedVault.sol#258-272) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:328: SwarmopolyGame._tax(address) (src/SwarmopolyGame.sol#328-331) uses timestamp for comparisons","passed":true},{"durationMs":488,"exitCode":0,"name":"aderyn","output":"[high] eth-send-unchecked-address at src/DeedVault.sol:298: ETH transferred without address checks (8 places)\n[high] reentrancy-state-change at src/DeedVault.sol:81: Reentrancy: State change after external call (4 places)\n[low] centralization-risk at src/DeedVault.sol:10: Centralization Risk (9 places)\n[low] costly-loop at src/DeedVault.sol:122: Costly operations inside loop (4 places)\n[low] large-numeric-literal at src/DeedVault.sol:108: Large Numeric Literal (2 places)\n[low] literal-instead-of-constant at src/DeedVault.sol:89: Literal Instead of Constant (44 places)\n[low] non-reentrant-not-first at src/DeedVault.sol:131: `nonReentrant` is Not the First Modifier (6 places)\n[low] require-revert-in-loop at src/SwarmopolyGame.sol:141: Loop Contains `require`/`revert` (2 places)\n[low] state-change-without-event at src/DeedVault.sol:119: State Change Without Event (7 places)\n[low] uninitialized-local-variable at src/SwarmopolyGame.sol:215: Uninitialized Local Variable (3 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"d1a55f5ac2c077677b32dbca20311c2fa2045452f3c269a60e54ef4074c2ee5b","verifiedTreeHash":"d4902fe4cfa7c1fe2551d0642d7176684880e10c","verifierVersion":"0.1.0+be003835"},{"checks":[{"durationMs":32411,"exitCode":0,"name":"build","output":"Compiling 37 files with Solc 0.8.26\nSolc 0.8.26 finished in 32.23s\nCompiler run successful!\nwarning[missing-events-access-control]: `swapContext` is changed without an event but is used for access control\n    ╭▸ src/DeedVault.sol:170:9\n    │\n170 │         swapContext = keccak256(data);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `swapContext` is changed without an event but is used for access control\n    ╭▸ src/DeedVault.sol:189:9\n    │\n189 │         swapContext = bytes32(0);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:105:35\n    │\n105 │         if (currentSeason == 0 || block.timestamp >= seasons[currentSeason].end || seasons[currentSeason].finalized) {\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SeasonPot.sol:28:23\n   │\n28 │     function bindGame(address game_) external onlyOwner {\n   │                       ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/SeasonPot.sol:43:64\n   │\n43 │     function pay(address to, uint256 amount) external onlyGame nonReentrant {\n   │                                                                ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/SeasonPot.sol:55:72\n   │\n55 │     function payReserved(address to, uint256 amount) external onlyGame nonReentrant {\n   │                                                                        ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/DeedVault.sol:79:23\n   │\n79 │     function bindGame(address game_) external onlyOwner {\n   │                       ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:116:9\n    │\n116 │         vault.listTile(slot, key, tier);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/SwarmopolyGame.sol:115:88\n    │\n115 │     function listTile(uint8 slot, PoolKey calldata key, uint8 tier) external onlyOwner nonReentrant {\n    │                                                                                        ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/DeedVault.sol:171:13\n    │\n171 │         try poolManager.unlock(data) returns (bytes memory result) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/DeedVault.sol:131:9\n    │\n131 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:157:9\n    │\n157 │         emit DeedBought(id, holder, slot, out, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:166:73\n    │\n166 │         if (_tiles[slot].token == address(0) || amount == 0 || amount > uint256(uint128(type(int128).max))) {\n    │                                                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:166:81\n    │\n166 │         if (_tiles[slot].token == address(0) || amount == 0 || amount > uint256(uint128(type(int128).max))) {\n    │                                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:176:51\n    │\n176 │             if (quoting && reason.length == 36 && bytes4(reason) == QuoteResult.selector) {\n    │                                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:196:30\n    │\n196 │                 zeroForOne, -int256(amount), zeroForOne ? 4295128740 : 1461446703485210103287273052203988822378723970341\n    │                              ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:200:21\n    │\n200 │         int128 d0 = int128(delta >> 128);\n    │                     ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:201:21\n    │\n201 │         int128 d1 = int128(delta);\n    │                     ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:204:23\n    │\n204 │         if (input != -int256(amount) || output <= 0) revert SwapFailed();\n    │                       ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:205:23\n    │\n205 │         uint256 out = uint256(output);\n    │                       ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:219:9\n    │\n219 │         emit RentCredited(slot, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/DeedVault.sol:243:9\n    │\n243 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:255:9\n    │\n255 │         emit RentClaimed(id, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:261:30\n    │\n261 │         if (d.shares == 0 || block.timestamp < d.redeemAt) revert Invalid();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:271:9\n    │\n271 │         emit Redeemed(id, assets);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:277:35\n    │\n277 │             currentSeason == 0 || block.timestamp >= seasonEnd[currentSeason] || _tiles[slot].token == address(0)\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:310:13\n    │\n310 │         if (block.timestamp < seasonEnd[id] || s.remaining == 0) revert Invalid();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:133:9\n    │\n133 │         vault.beginSeason(currentSeason, end);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/SwarmopolyGame.sol:122:9\n    │\n122 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:134:9\n    │\n134 │         emit SeasonStarted(currentSeason, buyIn, end, rollBond, maxBuy);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:128:29\n    │\n128 │                 || maxBuy > uint256(uint128(type(int128).max))\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:128:37\n    │\n128 │                 || maxBuy > uint256(uint128(type(int128).max))\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SwarmopolyGame.sol:142:40\n    │\n142 │             if (rents[i] > 1000 ether) revert Invalid();\n    │                                        ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:171:13\n    │\n171 │             pot.credit(amount);\n    │             ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:416:17\n    │\n416 │                 emit ScoreChanged(currentSeason, who, score);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:426:9\n    │\n426 │         emit ScoreChanged(currentSeason, who, score);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:174:9\n    │\n174 │         emit Joined(currentSeason, msg.sender);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:197:9\n    │\n197 │         pot.credit(amount);\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:211:13\n    │\n211 │         if (block.timestamp < p.nextRoll) revert TooEarly();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:213:13\n    │\n213 │         if (block.timestamp + REVEAL_SECONDS >= seasons[currentSeason].end) revert Inactive();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_players` is updated\n    ╭▸ src/SwarmopolyGame.sol:347:9\n    │\n347 │         pot.pay(address(this), amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:312:17\n    │\n312 │                 vault.creditRent(slot, holders);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:316:13\n    │\n316 │             pot.credit(potAmount);\n    │             ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:318:9\n    │\n318 │         vault.awardSponsor(slot, who);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:231:62\n    │\n231 │         if (block.number > r.entropyBlock + REVEAL_BLOCKS || block.timestamp > r.deadline) revert Inactive();\n    │                                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:247:9\n    │\n247 │         emit Rolled(msg.sender, die1, die2, p.position);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:240:22\n    │\n240 │         uint8 die1 = uint8(entropy % 6 + 1);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:241:22\n    │\n241 │         uint8 die2 = uint8((entropy / 6) % 6 + 1);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:245:22\n    │\n245 │         p.position = uint8(destination % 40);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:338:13\n    │\n338 │             pot.credit(amount);\n    │             ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:254:63\n    │\n254 │         if (block.number <= r.entropyBlock + REVEAL_BLOCKS && block.timestamp <= r.deadline) revert TooEarly();\n    │                                                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:262:9\n    │\n262 │         emit RollExpired(who, r.exposure);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:278:17\n    │\n278 │             if (p.parkingDay != day) {\n    │                 ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:354:41\n    │\n354 │         if (rollsPaused || !p.jailed || block.timestamp < p.nextRoll) revert Invalid();\n    │                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:366:9\n    │\n366 │         emit JailLeft(msg.sender, true);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:395:33\n    │\n395 │         (amount, scoreAmount) = vault.claimRent(id, msg.sender);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:397:61\n    │\n397 │             _players[msg.sender].season == currentSeason && block.timestamp < seasons[currentSeason].end\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:449:9\n    │\n449 │         pot.reserve(total);\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SwarmopolyGame.sol:447:13\n    │\n447 │             total += amount;\n    │             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:437:35\n    │\n437 │         if (currentSeason == 0 || block.timestamp < s.end || s.finalized) revert Inactive();\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:450:9\n    │\n450 │         emit SeasonFinalized(currentSeason, total);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:457:9\n    │\n457 │         pot.payReserved(msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:458:9\n    │\n458 │         emit PrizeClaimed(id, msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":33716,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 28 tests for test/Swarmopoly.t.sol:SwarmopolyTest\n[PASS] testApplicationRuntimeBoundsAndNoEscapeOpcodes() (gas: 10373522)\n[PASS] testBindingsAndOwnerRestrictions() (gas: 222247)\n[PASS] testBoardAndImmutableListing() (gas: 334957)\n[PASS] testBuyQuoteSlippageAndOnePurchasePerLanding() (gas: 1782410)\n[PASS] testCommitFutureHashWrongSecretReplayAndCooldown() (gas: 682245)\n[PASS] testDonationCannotChangeSharesAndLockWeights() (gas: 2038685)\n[PASS] testExpiryLocksWorstCostAllowsExcessAndAnyoneResolves() (gas: 788244)\n[PASS] testFinalizeFullTopTenPullPrizesAndCarry() (gas: 4891809)\n[PASS] testFuzzRentAccounting(uint96) (runs: 256, μ: 1757462, ~: 1757337)\nLogs:\n  Bound result 125844733\n\n[PASS] testFuzzWithholdingNeverImprovesBalance(uint96) (runs: 256, μ: 714341, ~: 689940)\nLogs:\n  Bound result 99000000000000008533\n\n[PASS] testGoSalaryOnceChanceAndParkingDaily() (gas: 2032215)\n[PASS] testJoinDepositWithdrawalAndFailedTokenRollback() (gas: 404663)\n[PASS] testNoReturnERC20AndFailureIsolation() (gas: 2119338)\n[PASS] testOldRentCannotScoreInNewSeasonAndClaimSurvivesRedeem() (gas: 2357810)\n[PASS] testParameterCapsAndSeasonImmutability() (gas: 548510)\n[PASS] testParkingCannotPayTwiceSameDay() (gas: 3060628)\n[PASS] testPartialInputAndInvalidSettlementRevertAtomically() (gas: 1257305)\n[PASS] testPausedRollCanRevealAndNoETH() (gas: 589450)\n[PASS] testPendingRollCannotHoldSeasonOpenOrAffectFinalScores() (gas: 1164988)\n[PASS] testReentrantTokenCannotWithdrawOrJoinDuringDeposit() (gas: 396882)\n[PASS] testRentBankruptcyAndNextSeasonReset() (gas: 1131791)\n[PASS] testRentEightyTwentyClaimAndRedeemWhilePaused() (gas: 2025574)\n[PASS] testRevealBlockWindowLastBlockAndTimestampExpiry() (gas: 948753)\n[PASS] testSponsorFundsSeparateFromAssetsAndPullClaims() (gas: 1112984)\n[PASS] testSponsorTokenFailureDoesNotBlockLanding() (gas: 1103586)\n[PASS] testSwapOppositeCurrencyOrdering() (gas: 1253116)\n[PASS] testTaxJailBailAndSkip() (gas: 1612997)\n[PASS] testWeightedRentAndNoRetroactiveRewards() (gas: 3312785)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 315.78ms (670.02ms CPU time)\n\nRan 25 tests for test/Adversarial.t.sol:AdversarialTest\n[PASS] testBindingRejectsEOAUnauthorizedAndWrongDependencies() (gas: 756357)\n[PASS] testCallbackFromManagerWithoutAnActiveSwapIsRejected() (gas: 38108)\n[PASS] testCommitDomainSeparationAndNonceReplay() (gas: 780830)\n[PASS] testConstructorRejectsZeroOwnerAndMismatchedDependencies() (gas: 6275)\n[PASS] testEmptySeasonDoesNotReserveUnassignedPrizes() (gas: 780449)\n[PASS] testEveryBoardSlotMatchesClassicBoardAndStartsVacant() (gas: 9548999)\n[PASS] testFuzzDepositWithdrawRoundtripBeforeJoining(uint128,uint8) (runs: 1000, μ: 807856, ~: 706292)\nLogs:\n  Bound result 30000000000000000000\n  Bound result 5\n\n[PASS] testFuzzEveryPrizeRankMatchesFixedWeightsAndRounding(uint96) (runs: 1000, μ: 4869286, ~: 4869203)\nLogs:\n  Bound result 212948335254449628195219\n\n[PASS] testFuzzQuoteIsReadOnlyAndMatchesPurchase(uint96) (runs: 1000, μ: 1487299, ~: 1487174)\nLogs:\n  Bound result 91605360762472167444\n\n[PASS] testFuzzWeightedRentConservationAndExactMaturities(uint96,uint96,uint8) (runs: 1000, μ: 3199431, ~: 3167065)\nLogs:\n  Bound result 22548008836135846683\n  Bound result 864\n\n[PASS] testInternalAccountingNeverReadsVaultTokenBalance() (gas: 1786721)\n[PASS] testInvalidSignedSwapDeltasRevertBeforeAnySettlement() (gas: 2788361)\n[PASS] testManagerReturningWithoutCallbackCannotMintDeeds() (gas: 800641)\n[PASS] testOwnerCannotMoveCustodyThroughGameOnlyEntrypoints() (gas: 741359)\n[PASS] testPendingCommitZeroCommitAndInsufficientBond() (gas: 478551)\n[PASS] testPoolValidationRejectsInvalidKeysAtomically() (gas: 822215)\n[PASS] testQuoteRejectsZeroUnlistedAndOverInt128Amounts() (gas: 97548)\n[PASS] testReentrantWithdrawalHasFundsButIsBlockedDuringDeposit() (gas: 604291)\n[PASS] testRentExactlyCoveredDoesNotBankruptAndOneWeiShortDoes() (gas: 1215229)\n[PASS] testRevealExactlyAtDeadlineAndMissingBlockhash() (gas: 533635)\n[PASS] testRolloverPreservesOldPrizesAndOutstandingSponsorAwards() (gas: 2378178)\n[PASS] testSponsorTopupCompetitionPartialLastAwardAndExactEnd() (gas: 1845841)\n[PASS] testWrongHolderCannotClaimRedeemOrTransferDeed() (gas: 1512330)\n[PASS] test_RevertedBuyRestoresWalletAllowanceAndLandingRight() (gas: 1215660)\n[PASS] test_RevertedRedemptionAndSponsorWithdrawalPreserveClaims() (gas: 2483387)\nSuite result: ok. 25 passed; 0 failed; 0 skipped; finished in 335.48ms (1.24s CPU time)\n\nRan 1 test for test/Invariants.t.sol:AccountingInvariants\n[PASS]\nAccountingInvariants invariants:\n[PASS] invariant_gamePotRentAndVaultSolvent\n[PASS] invariant_rentNeverCreatesMoney\n[PASS] invariant_sharesAndWeightsMatchDeeds\n[PASS] invariant_topTenUniqueAndSorted\n AccountingInvariants invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------+-----------------+-------+---------+----------╮\n| Contract    | Selector        | Calls | Reverts | Discards |\n+============================================================+\n| GameHandler | claimRedeem     | 1661  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | depositWithdraw | 1630  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | newSeason       | 1640  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | rollBuy         | 1600  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | sponsorAndClaim | 1661  | 0       | 0        |\n╰-------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 67446800404087584920\n  Bound result 31535999\n  Bound result 4033\n  Bound result 10000000000000000000\n  Bound result 93064980476413887169\n  Bound result 12628\n  Bound result 2131173431\n  Bound result 51642038968818432769\n  Bound result 2149\n  Bound result 4593\n  Bound result 11623\n  Bound result 9319\n  Bound result 998380600\n  Bound result 3971142178\n  Bound result 2835717308\n  Bound result 1697\n  Bound result 627158262306702859\n  Bound result 201887376774950434\n  Bound result 400522441586927790\n  Bound result 3714247997\n  Bound result 70122300383511271773\n  Bound result 998380600\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.91s (2.90s CPU time)\n\nRan 2 tests for test/StatefulAccounting.t.sol:StatefulAccountingTest\n[PASS]\nStatefulAccountingTest invariants:\n[PASS] invariantAllCustodyAndLiabilitiesReconcile\n[PASS] invariantLeaderboardContainsExactlyTheBestTen\n StatefulAccountingTest invariants (runs: 256, calls: 24576, reverts: 0)\n\n╭-------------------+-------------------------+-------+---------+----------╮\n| Contract          | Selector                | Calls | Reverts | Discards |\n+==========================================================================+\n| AccountingHandler | claimOrRedeem           | 1873  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | claimSponsorOrRemainder | 1890  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | commit                  | 1870  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | depositOrWithdraw       | 1953  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | donate                  | 1889  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | fund                    | 1846  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | newSeason               | 1920  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | pause                   | 1876  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | prize                   | 1903  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | resolve                 | 1814  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | rollAndBuy              | 3799  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | sponsor                 | 1943  | 0       | 0        |\n╰-------------------+-------------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 5000000000000000000\n  Bound result 5000000000000000000\n  Bound result 12206\n  Bound result 8424\n  Bound result 6094\n  Bound result 17231\n  Bound result 1911\n  Bound result 13245\n  Bound result 10753\n  Bound result 13055\n  Bound result 7640619312890421122\n  Bound result 9723365979153535348\n  Bound result 16180\n  Bound result 15538\n  Bound result 8969879577642105225\n  Bound result 165\n  Bound result 154964875099\n  Bound result 17264\n  Bound result 451376909894638140\n  Bound result 52107\n  Bound result 16228\n  Bound result 2695\n  Bound result 16525\n  Bound result 15594\n  Bound result 5463132793890036340\n  Bound result 228\n  Bound result 14426\n\n[PASS] testHandlerExercisesRollBuyExpireRedeemAndRollover() (gas: 30871482)\nLogs:\n  Bound result 5000000000000000000\n  Bound result 5000000000000000000\n  Bound result 2000000000000000000\n  Bound result 2000000000000000000\n  Bound result 2000000000000000000\n  Bound result 2000000000000000000\n  Bound result 2000000000000000000\n  Bound result 2000000000000000000\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 33.63s (33.64s CPU time)\n\nRan 4 test suites in 33.63s (37.19s CPU time): 56 tests passed, 0 failed, 0 skipped (56 total tests)\n","passed":true},{"durationMs":43,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DeedVault.awardSponsor(uint8,address)\",\"DeedVault.beginSeason(uint256,uint256)\",\"DeedVault.bindGame(address)\",\"DeedVault.buy(uint8,address,uint256,uint256,uint8)\",\"DeedVault.claimRent(uint256,address)\",\"DeedVault.claimSponsor(uint8)\",\"DeedVault.creditRent(uint8,uint256)\",\"DeedVault.listTile(uint8,(address,address,uint24,int24,address),uint8)\",\"DeedVault.quote(uint8,uint256)\",\"DeedVault.redeem(uint256)\",\"DeedVault.sponsorTile(uint8,uint256,uint256)\",\"DeedVault.unlockCallback(bytes)\",\"DeedVault.withdrawSponsor(uint256,uint8)\",\"SeasonPot.bindGame(address)\",\"SeasonPot.credit(uint256)\",\"SeasonPot.pay(address,uint256)\",\"SeasonPot.payReserved(address,uint256)\",\"SeasonPot.reserve(uint256)\",\"SwarmopolyGame.buyDeed(uint8,uint256,uint256)\",\"SwarmopolyGame.buyDeed(uint8,uint256,uint256,uint8)\",\"SwarmopolyGame.claimPrize(uint256)\",\"SwarmopolyGame.claimRent(uint256)\",\"SwarmopolyGame.commitRoll(bytes32)\",\"SwarmopolyGame.deposit(uint256)\",\"SwarmopolyGame.expireRoll(address)\",\"SwarmopolyGame.finalizeSeason()\",\"SwarmopolyGame.fundPot(uint256)\",\"SwarmopolyGame.joinSeason()\",\"SwarmopolyGame.listTile(uint8,(address,address,uint24,int24,address),uint8)\",\"SwarmopolyGame.payBail()\",\"SwarmopolyGame.revealRoll(bytes32)\",\"SwarmopolyGame.setParams(uint256,uint256,uint256[8])\",\"SwarmopolyGame.setPaused(bool,bool)\",\"SwarmopolyGame.skipJailRoll()\",\"SwarmopolyGame.startSeason(uint256,uint256,uint256,uint256)\",\"SwarmopolyGame.withdraw(uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":24,\"docs/DEPLOYMENT.md\":52,\"docs/REVIEW.md\":43,\"docs/RULES.md\":45,\"docs/TESTING.md\":25,\"docs/check-results.json\":24,\"docs/network-verification.json\":14,\"foundry.toml\":21,\"launch.json\":9,\"scripts/configure-site.mjs\":20,\"scripts/export-abi.py\":7,\"scripts/verify-network.py\":34,\"site/abi.json\":1,\"site/app.js\":245,\"site/config.json\":13,\"site/index.html\":15,\"site/package.json\":1,\"site/rules.html\":1,\"site/style.css\":2,\"site/vendor/ethers.LICENSE.md\":21,\"site/vendor/ethers.min.js\":1,\"src/DeedVault.sol\":317,\"src/Owned.sol\":17,\"src/SeasonPot.sol\":60,\"src/SwarmopolyGame.sol\":460,\"src/interfaces/IV4.sol\":35,\"test/Adversarial.t.sol\":603,\"test/CONTRIBUTOR_TESTS.md\":49,\"test/Invariants.t.sol\":178,\"test/StatefulAccounting.t.sol\":159,\"test/Swarmopoly.t.sol\":679,\"test/helpers/AccountingHandler.sol\":284,\"test/mocks/MockERC20.sol\":60,\"test/mocks/MockPoolManager.sol\":66,\"test/site.test.mjs\":51},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"d3fb034cbcfbce8728e12df1e1a9dac0dc95ff4c39c91f197001d4b5a2105ea8","verifiedTreeHash":"d9daa817bd96bdfd9f531058dca72506e0824986","verifierVersion":"0.1.0+be003835"},{"checks":[{"durationMs":20202,"exitCode":0,"name":"build","output":"Compiling 35 files with Solc 0.8.26\nSolc 0.8.26 finished in 20.08s\nCompiler run successful!\nwarning[missing-events-access-control]: `swapContext` is changed without an event but is used for access control\n    ╭▸ src/DeedVault.sol:172:9\n    │\n172 │         swapContext = keccak256(data);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `swapContext` is changed without an event but is used for access control\n    ╭▸ src/DeedVault.sol:191:9\n    │\n191 │         swapContext = bytes32(0);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:105:35\n    │\n105 │         if (currentSeason == 0 || block.timestamp >= seasons[currentSeason].end || seasons[currentSeason].finalized) {\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SeasonPot.sol:28:23\n   │\n28 │     function bindGame(address game_) external onlyOwner {\n   │                       ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/SeasonPot.sol:43:64\n   │\n43 │     function pay(address to, uint256 amount) external onlyGame nonReentrant {\n   │                                                                ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/SeasonPot.sol:55:72\n   │\n55 │     function payReserved(address to, uint256 amount) external onlyGame nonReentrant {\n   │                                                                        ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:116:9\n    │\n116 │         vault.listTile(slot, key, tier);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/SwarmopolyGame.sol:115:88\n    │\n115 │     function listTile(uint8 slot, PoolKey calldata key, uint8 tier) external onlyOwner nonReentrant {\n    │                                                                                        ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/DeedVault.sol:79:23\n   │\n79 │     function bindGame(address game_) external onlyOwner {\n   │                       ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/DeedVault.sol:173:13\n    │\n173 │         try poolManager.unlock(data) returns (bytes memory result) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/DeedVault.sol:133:9\n    │\n133 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:159:9\n    │\n159 │         emit DeedBought(id, holder, slot, out, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:133:9\n    │\n133 │         vault.beginSeason(currentSeason, end);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/SwarmopolyGame.sol:122:9\n    │\n122 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:134:9\n    │\n134 │         emit SeasonStarted(currentSeason, buyIn, end, rollBond, maxBuy);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:128:29\n    │\n128 │                 || maxBuy > uint256(uint128(type(int128).max))\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:128:37\n    │\n128 │                 || maxBuy > uint256(uint128(type(int128).max))\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:168:73\n    │\n168 │         if (_tiles[slot].token == address(0) || amount == 0 || amount > uint256(uint128(type(int128).max))) {\n    │                                                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:168:81\n    │\n168 │         if (_tiles[slot].token == address(0) || amount == 0 || amount > uint256(uint128(type(int128).max))) {\n    │                                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:178:51\n    │\n178 │             if (quoting && reason.length == 36 && bytes4(reason) == QuoteResult.selector) {\n    │                                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:198:30\n    │\n198 │                 zeroForOne, -int256(amount), zeroForOne ? 4295128740 : 1461446703485210103287273052203988822378723970341\n    │                              ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:202:21\n    │\n202 │         int128 d0 = int128(delta >> 128);\n    │                     ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:203:21\n    │\n203 │         int128 d1 = int128(delta);\n    │                     ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:206:23\n    │\n206 │         if (input != -int256(amount) || output <= 0) revert SwapFailed();\n    │                       ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:207:23\n    │\n207 │         uint256 out = uint256(output);\n    │                       ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:221:9\n    │\n221 │         emit RentCredited(slot, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/DeedVault.sol:245:9\n    │\n245 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:257:9\n    │\n257 │         emit RentClaimed(id, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:263:30\n    │\n263 │         if (d.shares == 0 || block.timestamp < d.redeemAt) revert Invalid();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:273:9\n    │\n273 │         emit Redeemed(id, assets);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:279:35\n    │\n279 │             currentSeason == 0 || block.timestamp >= seasonEnd[currentSeason] || _tiles[slot].token == address(0)\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SwarmopolyGame.sol:142:40\n    │\n142 │             if (rents[i] > 1000 ether) revert Invalid();\n    │                                        ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:312:13\n    │\n312 │         if (block.timestamp < seasonEnd[id] || s.remaining == 0) revert Invalid();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:170:13\n    │\n170 │             pot.credit(amount);\n    │             ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:416:17\n    │\n416 │                 emit ScoreChanged(currentSeason, who, score);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:426:9\n    │\n426 │         emit ScoreChanged(currentSeason, who, score);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:173:9\n    │\n173 │         emit Joined(currentSeason, msg.sender);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:196:9\n    │\n196 │         pot.credit(amount);\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:210:13\n    │\n210 │         if (block.timestamp < p.nextRoll) revert TooEarly();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:212:13\n    │\n212 │         if (block.timestamp + REVEAL_SECONDS >= seasons[currentSeason].end) revert Inactive();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_players` is updated\n    ╭▸ src/SwarmopolyGame.sol:346:9\n    │\n346 │         pot.pay(address(this), amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:311:17\n    │\n311 │                 vault.creditRent(slot, holders);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:315:13\n    │\n315 │             pot.credit(potAmount);\n    │             ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:317:9\n    │\n317 │         vault.awardSponsor(slot, who);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:230:62\n    │\n230 │         if (block.number > r.entropyBlock + REVEAL_BLOCKS || block.timestamp > r.deadline) revert Inactive();\n    │                                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:246:9\n    │\n246 │         emit Rolled(msg.sender, die1, die2, p.position);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:239:22\n    │\n239 │         uint8 die1 = uint8(entropy % 6 + 1);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:240:22\n    │\n240 │         uint8 die2 = uint8((entropy / 6) % 6 + 1);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:244:22\n    │\n244 │         p.position = uint8(destination % 40);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:337:13\n    │\n337 │             pot.credit(amount);\n    │             ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:253:63\n    │\n253 │         if (block.number <= r.entropyBlock + REVEAL_BLOCKS && block.timestamp <= r.deadline) revert TooEarly();\n    │                                                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:261:9\n    │\n261 │         emit RollExpired(who, r.exposure);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:277:17\n    │\n277 │             if (p.parkingDay != day) {\n    │                 ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:353:41\n    │\n353 │         if (rollsPaused || !p.jailed || block.timestamp < p.nextRoll) revert Invalid();\n    │                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:365:9\n    │\n365 │         emit JailLeft(msg.sender, true);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:394:33\n    │\n394 │         (amount, scoreAmount) = vault.claimRent(id, msg.sender);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:396:61\n    │\n396 │             _players[msg.sender].season == currentSeason && block.timestamp < seasons[currentSeason].end\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:449:9\n    │\n449 │         pot.reserve(total);\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SwarmopolyGame.sol:447:13\n    │\n447 │             total += amount;\n    │             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:437:35\n    │\n437 │         if (currentSeason == 0 || block.timestamp < s.end || s.finalized) revert Inactive();\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:450:9\n    │\n450 │         emit SeasonFinalized(currentSeason, total);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:457:9\n    │\n457 │         pot.payReserved(msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:458:9\n    │\n458 │         emit PrizeClaimed(id, msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":2232,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 11 tests for test/RevisionRegression.t.sol:RevisionRegressionTest\n[PASS] testCooldownPersistsWhenJoiningNewSeason() (gas: 1300129)\n[PASS] testEmptyListingCanBeCorrected() (gas: 1547002)\n[PASS] testExhaustedDustSponsorCanBeReplaced() (gas: 1806744)\n[PASS] testFundedSponsorCannotBeReplacedOrRateChanged() (gas: 683414)\n[PASS] testObservedDustDeedReceivesHolderShare() (gas: 1552381)\n[PASS] testObservedMissedRevealPenalty() (gas: 530311)\n[PASS] testObservedParkingPaysEachPlayerAndDownsideIsBalance() (gas: 2920964)\n[PASS] testRelistAfterRedemptionPreservesOldRentAndNewDeedBaseline() (gas: 4957981)\n[PASS] testSponsorLiabilitiesBlockRelistingAcrossSeasons() (gas: 1743753)\n[PASS] testZeroRentClaimDoesNotRankButPositiveScoreDoes() (gas: 5366345)\n[PASS] testZeroScoreJoinersLeaveAllPrizeBudgetAvailable() (gas: 2058507)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 37.28ms (12.16ms CPU time)\n\nRan 28 tests for test/Swarmopoly.t.sol:SwarmopolyTest\n[PASS] testApplicationRuntimeBoundsAndNoEscapeOpcodes() (gas: 10408031)\n[PASS] testBindingsAndOwnerRestrictions() (gas: 222247)\n[PASS] testBoardAndFundedListingRestrictions() (gas: 1205377)\n[PASS] testBuyQuoteSlippageAndOnePurchasePerLanding() (gas: 1782410)\n[PASS] testCommitFutureHashWrongSecretReplayAndCooldown() (gas: 682245)\n[PASS] testDonationCannotChangeSharesAndLockWeights() (gas: 2038685)\n[PASS] testExpiryLocksWorstCostAllowsExcessAndAnyoneResolves() (gas: 788244)\n[PASS] testFinalizeFullTopTenPullPrizesAndCarry() (gas: 9069268)\n[PASS] testFuzzRentAccounting(uint96) (runs: 256, μ: 1790554, ~: 1790612)\nLogs:\n  Bound result 7021\n\n[PASS] testFuzzWithholdingNeverImprovesBalance(uint96) (runs: 256, μ: 716333, ~: 689934)\nLogs:\n  Bound result 24677216999724971842\n\n[PASS] testGoSalaryOnceChanceAndParkingDaily() (gas: 2062275)\n[PASS] testJoinDepositWithdrawalAndFailedTokenRollback() (gas: 404663)\n[PASS] testNoReturnERC20AndFailureIsolation() (gas: 2149374)\n[PASS] testOldRentCannotScoreInNewSeasonAndClaimSurvivesRedeem() (gas: 2231257)\n[PASS] testParameterCapsAndSeasonImmutability() (gas: 481076)\n[PASS] testParkingCannotPayTwiceSameDay() (gas: 2986306)\n[PASS] testPartialInputAndInvalidSettlementRevertAtomically() (gas: 1257305)\n[PASS] testPausedRollCanRevealAndNoETH() (gas: 589450)\n[PASS] testPendingRollCannotHoldSeasonOpenOrAffectFinalScores() (gas: 1043454)\n[PASS] testReentrantTokenCannotWithdrawOrJoinDuringDeposit() (gas: 396882)\n[PASS] testRentBankruptcyAndNextSeasonReset() (gas: 1010279)\n[PASS] testRentEightyTwentyClaimAndRedeemWhilePaused() (gas: 2055588)\n[PASS] testRevealBlockWindowLastBlockAndTimestampExpiry() (gas: 948753)\n[PASS] testSponsorFundsSeparateFromAssetsAndPullClaims() (gas: 1112958)\n[PASS] testSponsorTokenFailureDoesNotBlockLanding() (gas: 1103560)\n[PASS] testSwapOppositeCurrencyOrdering() (gas: 1259003)\n[PASS] testTaxJailBailAndSkip() (gas: 1612997)\n[PASS] testWeightedRentAndNoRetroactiveRewards() (gas: 3285384)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 44.07ms (112.42ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:AccountingInvariants\n[PASS]\nAccountingInvariants invariants:\n[PASS] invariant_gamePotRentAndVaultSolvent\n[PASS] invariant_rentNeverCreatesMoney\n[PASS] invariant_sharesAndWeightsMatchDeeds\n[PASS] invariant_topTenUniqueAndSorted\n AccountingInvariants invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------+-----------------+-------+---------+----------╮\n| Contract    | Selector        | Calls | Reverts | Discards |\n+============================================================+\n| GameHandler | claimRedeem     | 1641  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | depositWithdraw | 1586  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | newSeason       | 1662  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | rollBuy         | 1647  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | sponsorAndClaim | 1656  | 0       | 0        |\n╰-------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 418248036\n  Bound result 10275\n  Bound result 4161\n  Bound result 1\n  Bound result 18446744073709551617\n  Bound result 1348796960\n  Bound result 6222208766053095130\n  Bound result 1\n  Bound result 959916971345282054\n  Bound result 150105648\n  Bound result 10639\n  Bound result 12153\n  Bound result 4386\n  Bound result 11635\n  Bound result 4042\n  Bound result 1696297413982942794\n  Bound result 7327\n  Bound result 3550\n  Bound result 29\n  Bound result 10274\n  Bound result 1822\n  Bound result 202\n  Bound result 998380600\n  Bound result 10793\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.15s (2.15s CPU time)\n\nRan 3 test suites in 2.16s (2.24s CPU time): 40 tests passed, 0 failed, 0 skipped (40 total tests)\n","passed":true},{"durationMs":41,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DeedVault.awardSponsor(uint8,address)\",\"DeedVault.beginSeason(uint256,uint256)\",\"DeedVault.bindGame(address)\",\"DeedVault.buy(uint8,address,uint256,uint256,uint8)\",\"DeedVault.claimRent(uint256,address)\",\"DeedVault.claimSponsor(uint8)\",\"DeedVault.creditRent(uint8,uint256)\",\"DeedVault.listTile(uint8,(address,address,uint24,int24,address),uint8)\",\"DeedVault.quote(uint8,uint256)\",\"DeedVault.redeem(uint256)\",\"DeedVault.sponsorTile(uint8,uint256,uint256)\",\"DeedVault.unlockCallback(bytes)\",\"DeedVault.withdrawSponsor(uint256,uint8)\",\"SeasonPot.bindGame(address)\",\"SeasonPot.credit(uint256)\",\"SeasonPot.pay(address,uint256)\",\"SeasonPot.payReserved(address,uint256)\",\"SeasonPot.reserve(uint256)\",\"SwarmopolyGame.buyDeed(uint8,uint256,uint256)\",\"SwarmopolyGame.buyDeed(uint8,uint256,uint256,uint8)\",\"SwarmopolyGame.claimPrize(uint256)\",\"SwarmopolyGame.claimRent(uint256)\",\"SwarmopolyGame.commitRoll(bytes32)\",\"SwarmopolyGame.deposit(uint256)\",\"SwarmopolyGame.expireRoll(address)\",\"SwarmopolyGame.finalizeSeason()\",\"SwarmopolyGame.fundPot(uint256)\",\"SwarmopolyGame.joinSeason()\",\"SwarmopolyGame.listTile(uint8,(address,address,uint24,int24,address),uint8)\",\"SwarmopolyGame.payBail()\",\"SwarmopolyGame.revealRoll(bytes32)\",\"SwarmopolyGame.setParams(uint256,uint256,uint256[8])\",\"SwarmopolyGame.setPaused(bool,bool)\",\"SwarmopolyGame.skipJailRoll()\",\"SwarmopolyGame.startSeason(uint256,uint256,uint256,uint256)\",\"SwarmopolyGame.withdraw(uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":24,\"docs/DEPLOYMENT.md\":52,\"docs/REVIEW.md\":45,\"docs/RULES.md\":45,\"docs/TESTING.md\":33,\"docs/check-results.json\":26,\"docs/network-verification.json\":14,\"foundry.toml\":21,\"launch.json\":9,\"scripts/configure-site.mjs\":20,\"scripts/export-abi.py\":7,\"scripts/verify-network.py\":34,\"site/abi.json\":1,\"site/app.js\":245,\"site/config.json\":13,\"site/index.html\":15,\"site/package.json\":1,\"site/rules.html\":1,\"site/style.css\":2,\"site/vendor/ethers.LICENSE.md\":21,\"site/vendor/ethers.min.js\":1,\"src/DeedVault.sol\":319,\"src/Owned.sol\":17,\"src/SeasonPot.sol\":60,\"src/SwarmopolyGame.sol\":460,\"src/interfaces/IV4.sol\":35,\"test/Invariants.t.sol\":179,\"test/RevisionRegression.t.sol\":295,\"test/Swarmopoly.t.sol\":691,\"test/mocks/MockERC20.sol\":60,\"test/mocks/MockPoolManager.sol\":66,\"test/site.test.mjs\":51},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":3432,"exitCode":0,"name":"slither","output":"[high/medium] weak-prng at src/SwarmopolyGame.sol:226: SwarmopolyGame.revealRoll(bytes32) (src/SwarmopolyGame.sol#226-247) uses a weak PRNG: \"p.position = uint8(destination % 40) (src/SwarmopolyGame.sol#244)\"\n[high/medium] weak-prng at src/SwarmopolyGame.sol:264: SwarmopolyGame._land(address,uint256,bool) (src/SwarmopolyGame.sol#264-319) uses a weak PRNG: \"card = (entropy >> 16) % 5 (src/SwarmopolyGame.sol#285)\"\n[high/medium] weak-prng at src/SwarmopolyGame.sol:226: SwarmopolyGame.revealRoll(bytes32) (src/SwarmopolyGame.sol#226-247) uses a weak PRNG: \"die2 = uint8((entropy / 6) % 6 + 1) (src/SwarmopolyGame.sol#240)\"\n[high/medium] weak-prng at src/SwarmopolyGame.sol:226: SwarmopolyGame.revealRoll(bytes32) (src/SwarmopolyGame.sol#226-247) uses a weak PRNG: \"die1 = uint8(entropy % 6 + 1) (src/SwarmopolyGame.sol#239)\"\n[high/high] uninitialized-state at src/SwarmopolyGame.sol:95: SwarmopolyGame._leaders (src/SwarmopolyGame.sol#62) is never initialized. It is used in:\n[medium/high] incorrect-equality at src/SwarmopolyGame.sol:392: SwarmopolyGame.claimRent(uint256) (src/SwarmopolyGame.sol#392-399) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/DeedVault.sol:130: Reentrancy in DeedVault.buy(uint8,address,uint256,uint256,uint8) (src/DeedVault.sol#130-160):\n[medium/medium] reentrancy-no-eth at src/SwarmopolyGame.sol:226: Reentrancy in SwarmopolyGame.revealRoll(bytes32) (src/SwarmopolyGame.sol#226-247):\n[medium/medium] reentrancy-no-eth at src/SwarmopolyGame.sol:264: Reentrancy in SwarmopolyGame._land(address,uint256,bool) (src/SwarmopolyGame.sol#264-319):\n[medium/medium] uninitialized-local at src/SwarmopolyGame.sol:441: SwarmopolyGame.finalizeSeason().total (src/SwarmopolyGame.sol#441) is a local variable never initialized\n[low/medium] events-maths at src/DeedVault.sol:121: DeedVault.beginSeason(uint256,uint256) (src/DeedVault.sol#121-127) should emit an event for: \n[low/medium] reentrancy-benign at src/SwarmopolyGame.sol:392: Reentrancy in SwarmopolyGame.claimRent(uint256) (src/SwarmopolyGame.sol#392-399):\n[low/medium] reentrancy-benign at src/DeedVault.sol:130: Reentrancy in DeedVault.buy(uint8,address,uint256,uint256,uint8) (src/DeedVault.sol#130-160):\n[low/medium] reentrancy-benign at src/SwarmopolyGame.sol:156: Reentrancy in SwarmopolyGame.joinSeason() (src/SwarmopolyGame.sol#156-174):\n[low/medium] reentrancy-benign at src/SwarmopolyGame.sol:341: Reentrancy in SwarmopolyGame._reward(address,uint256) (src/SwarmopolyGame.sol#341-348):\n[low/medium] reentrancy-benign at src/DeedVault.sol:167: Reentrancy in DeedVault._swap(uint8,uint256,bool) (src/DeedVault.sol#167-185):\n[low/medium] timestamp at src/SwarmopolyGame.sol:119: SwarmopolyGame.startSeason(uint256,uint256,uint256,uint256) (src/SwarmopolyGame.sol#119-135) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:104: SwarmopolyGame._active() (src/SwarmopolyGame.sol#104-108) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:392: SwarmopolyGame.claimRent(uint256) (src/SwarmopolyGame.sol#392-399) uses timestamp for comparisons\n[low/medium] timestamp at src/DeedVault.sol:309: DeedVault.withdrawSponsor(uint256,uint8) (src/DeedVault.sol#309-318) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:332: SwarmopolyGame._toPot(address,uint256) (src/SwarmopolyGame.sol#332-339) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:156: SwarmopolyGame.joinSeason() (src/SwarmopolyGame.sol#156-174) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:350: SwarmopolyGame.skipJailRoll() (src/SwarmopolyGame.sol#350-357) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:435: SwarmopolyGame.finalizeSeason() (src/SwarmopolyGame.sol#435-451) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:264: SwarmopolyGame._land(address,uint256,bool) (src/SwarmopolyGame.sol#264-319) uses timestamp for comparisons\n[low/medium] timestamp at src/DeedVault.sol:260: DeedVault.redeem(uint256) (src/DeedVault.sol#260-274) uses timestamp for comparisons\n[low/medium] timestamp at src/DeedVault.sol:277: DeedVault.sponsorTile(uint8,uint256,uint256) (src/DeedVault.sol#277-290) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:203: SwarmopolyGame.commitRoll(bytes32) (src/SwarmopolyGame.sol#203-224) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:226: SwarmopolyGame.revealRoll(bytes32) (src/SwarmopolyGame.sol#226-247) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:327: SwarmopolyGame._tax(address) (src/SwarmopolyGame.sol#327-330) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmopolyGame.sol:250: SwarmopolyGame.expireRoll(address) (src/SwarmopolyGame.sol#250-262) uses timestamp for comparisons","passed":true},{"durationMs":344,"exitCode":0,"name":"aderyn","output":"[high] eth-send-unchecked-address at src/DeedVault.sol:300: ETH transferred without address checks (8 places)\n[high] reentrancy-state-change at src/DeedVault.sol:81: Reentrancy: State change after external call (4 places)\n[low] centralization-risk at src/DeedVault.sol:10: Centralization Risk (9 places)\n[low] costly-loop at src/DeedVault.sol:124: Costly operations inside loop (4 places)\n[low] large-numeric-literal at src/DeedVault.sol:109: Large Numeric Literal (2 places)\n[low] literal-instead-of-constant at src/DeedVault.sol:89: Literal Instead of Constant (44 places)\n[low] non-reentrant-not-first at src/DeedVault.sol:133: `nonReentrant` is Not the First Modifier (6 places)\n[low] require-revert-in-loop at src/SwarmopolyGame.sol:141: Loop Contains `require`/`revert` (2 places)\n[low] state-change-without-event at src/DeedVault.sol:121: State Change Without Event (7 places)\n[low] uninitialized-local-variable at src/SwarmopolyGame.sol:214: Uninitialized Local Variable (3 places)","passed":true},{"durationMs":4531,"exitCode":0,"name":"proof 6213ce0dfefa","output":"Compiling 33 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.01s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-87930ab3/Proof_6213ce0dfefa.t.sol:ZeroScorePrizeTest\n[PASS] testJoinersWithZeroScoreArePaidPrizes() (gas: 912347)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 833.01µs (246.75µs CPU time)\n\nRan 1 test suite in 2.30ms (833.01µs 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":"d77c76c03b6faf451deada73682a8f1b43806cf2f5bed348ccc8f8b792571a68","verifiedTreeHash":"89543324630cba1ec2e147d2af1922da996fe650","verifierVersion":"0.1.0+be003835"},{"checks":[{"durationMs":34959,"exitCode":0,"name":"build","output":"Compiling 38 files with Solc 0.8.26\nSolc 0.8.26 finished in 34.83s\nCompiler run successful!\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:105:35\n    │\n105 │         if (currentSeason == 0 || block.timestamp >= seasons[currentSeason].end || seasons[currentSeason].finalized) {\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[missing-events-access-control]: `swapContext` is changed without an event but is used for access control\n    ╭▸ src/DeedVault.sol:172:9\n    │\n172 │         swapContext = keccak256(data);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `swapContext` is changed without an event but is used for access control\n    ╭▸ src/DeedVault.sol:191:9\n    │\n191 │         swapContext = bytes32(0);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:116:9\n    │\n116 │         vault.listTile(slot, key, tier);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/SwarmopolyGame.sol:115:88\n    │\n115 │     function listTile(uint8 slot, PoolKey calldata key, uint8 tier) external onlyOwner nonReentrant {\n    │                                                                                        ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/SeasonPot.sol:28:23\n   │\n28 │     function bindGame(address game_) external onlyOwner {\n   │                       ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/SeasonPot.sol:43:64\n   │\n43 │     function pay(address to, uint256 amount) external onlyGame nonReentrant {\n   │                                                                ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n   ╭▸ src/SeasonPot.sol:55:72\n   │\n55 │     function payReserved(address to, uint256 amount) external onlyGame nonReentrant {\n   │                                                                        ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:133:9\n    │\n133 │         vault.beginSeason(currentSeason, end);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/SwarmopolyGame.sol:122:9\n    │\n122 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:134:9\n    │\n134 │         emit SeasonStarted(currentSeason, buyIn, end, rollBond, maxBuy);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:128:29\n    │\n128 │                 || maxBuy > uint256(uint128(type(int128).max))\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:128:37\n    │\n128 │                 || maxBuy > uint256(uint128(type(int128).max))\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/DeedVault.sol:79:23\n   │\n79 │     function bindGame(address game_) external onlyOwner {\n   │                       ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SwarmopolyGame.sol:142:40\n    │\n142 │             if (rents[i] > 1000 ether) revert Invalid();\n    │                                        ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/DeedVault.sol:173:13\n    │\n173 │         try poolManager.unlock(data) returns (bytes memory result) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/DeedVault.sol:133:9\n    │\n133 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:159:9\n    │\n159 │         emit DeedBought(id, holder, slot, out, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:168:73\n    │\n168 │         if (_tiles[slot].token == address(0) || amount == 0 || amount > uint256(uint128(type(int128).max))) {\n    │                                                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:168:81\n    │\n168 │         if (_tiles[slot].token == address(0) || amount == 0 || amount > uint256(uint128(type(int128).max))) {\n    │                                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:178:51\n    │\n178 │             if (quoting && reason.length == 36 && bytes4(reason) == QuoteResult.selector) {\n    │                                                   ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:198:30\n    │\n198 │                 zeroForOne, -int256(amount), zeroForOne ? 4295128740 : 1461446703485210103287273052203988822378723970341\n    │                              ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:202:21\n    │\n202 │         int128 d0 = int128(delta >> 128);\n    │                     ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:203:21\n    │\n203 │         int128 d1 = int128(delta);\n    │                     ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:206:23\n    │\n206 │         if (input != -int256(amount) || output <= 0) revert SwapFailed();\n    │                       ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DeedVault.sol:207:23\n    │\n207 │         uint256 out = uint256(output);\n    │                       ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:221:9\n    │\n221 │         emit RentCredited(slot, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:170:13\n    │\n170 │             pot.credit(amount);\n    │             ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:416:17\n    │\n416 │                 emit ScoreChanged(currentSeason, who, score);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:426:9\n    │\n426 │         emit ScoreChanged(currentSeason, who, score);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:173:9\n    │\n173 │         emit Joined(currentSeason, msg.sender);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/DeedVault.sol:245:9\n    │\n245 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:257:9\n    │\n257 │         emit RentClaimed(id, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:196:9\n    │\n196 │         pot.credit(amount);\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:263:30\n    │\n263 │         if (d.shares == 0 || block.timestamp < d.redeemAt) revert Invalid();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DeedVault.sol:273:9\n    │\n273 │         emit Redeemed(id, assets);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:210:13\n    │\n210 │         if (block.timestamp < p.nextRoll) revert TooEarly();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:212:13\n    │\n212 │         if (block.timestamp + REVEAL_SECONDS >= seasons[currentSeason].end) revert Inactive();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:279:35\n    │\n279 │             currentSeason == 0 || block.timestamp >= seasonEnd[currentSeason] || _tiles[slot].token == address(0)\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_players` is updated\n    ╭▸ src/SwarmopolyGame.sol:346:9\n    │\n346 │         pot.pay(address(this), amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DeedVault.sol:312:13\n    │\n312 │         if (block.timestamp < seasonEnd[id] || s.remaining == 0) revert Invalid();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:311:17\n    │\n311 │                 vault.creditRent(slot, holders);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:315:13\n    │\n315 │             pot.credit(potAmount);\n    │             ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:317:9\n    │\n317 │         vault.awardSponsor(slot, who);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:230:62\n    │\n230 │         if (block.number > r.entropyBlock + REVEAL_BLOCKS || block.timestamp > r.deadline) revert Inactive();\n    │                                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:246:9\n    │\n246 │         emit Rolled(msg.sender, die1, die2, p.position);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:239:22\n    │\n239 │         uint8 die1 = uint8(entropy % 6 + 1);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:240:22\n    │\n240 │         uint8 die2 = uint8((entropy / 6) % 6 + 1);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmopolyGame.sol:244:22\n    │\n244 │         p.position = uint8(destination % 40);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:337:13\n    │\n337 │             pot.credit(amount);\n    │             ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:253:63\n    │\n253 │         if (block.number <= r.entropyBlock + REVEAL_BLOCKS && block.timestamp <= r.deadline) revert TooEarly();\n    │                                                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:261:9\n    │\n261 │         emit RollExpired(who, r.exposure);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:277:17\n    │\n277 │             if (p.parkingDay != day) {\n    │                 ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:353:41\n    │\n353 │         if (rollsPaused || !p.jailed || block.timestamp < p.nextRoll) revert Invalid();\n    │                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:365:9\n    │\n365 │         emit JailLeft(msg.sender, true);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:394:33\n    │\n394 │         (amount, scoreAmount) = vault.claimRent(id, msg.sender);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:396:61\n    │\n396 │             _players[msg.sender].season == currentSeason && block.timestamp < seasons[currentSeason].end\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:449:9\n    │\n449 │         pot.reserve(total);\n    │         ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SwarmopolyGame.sol:447:13\n    │\n447 │             total += amount;\n    │             ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SwarmopolyGame.sol:437:35\n    │\n437 │         if (currentSeason == 0 || block.timestamp < s.end || s.finalized) revert Inactive();\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:450:9\n    │\n450 │         emit SeasonFinalized(currentSeason, total);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/SwarmopolyGame.sol:457:9\n    │\n457 │         pot.payReserved(msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmopolyGame.sol:458:9\n    │\n458 │         emit PrizeClaimed(id, msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":25932,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 28 tests for test/Swarmopoly.t.sol:SwarmopolyTest\n[PASS] testApplicationRuntimeBoundsAndNoEscapeOpcodes() (gas: 10408031)\n[PASS] testBindingsAndOwnerRestrictions() (gas: 222247)\n[PASS] testBoardAndFundedListingRestrictions() (gas: 1205377)\n[PASS] testBuyQuoteSlippageAndOnePurchasePerLanding() (gas: 1782410)\n[PASS] testCommitFutureHashWrongSecretReplayAndCooldown() (gas: 682245)\n[PASS] testDonationCannotChangeSharesAndLockWeights() (gas: 2038685)\n[PASS] testExpiryLocksWorstCostAllowsExcessAndAnyoneResolves() (gas: 788244)\n[PASS] testFinalizeFullTopTenPullPrizesAndCarry() (gas: 9069268)\n[PASS] testFuzzRentAccounting(uint96) (runs: 256, μ: 1790430, ~: 1790631)\nLogs:\n  Bound result 1\n\n[PASS] testFuzzWithholdingNeverImprovesBalance(uint96) (runs: 256, μ: 716182, ~: 689940)\nLogs:\n  Bound result 1000000000000000000\n\n[PASS] testGoSalaryOnceChanceAndParkingDaily() (gas: 2062275)\n[PASS] testJoinDepositWithdrawalAndFailedTokenRollback() (gas: 404663)\n[PASS] testNoReturnERC20AndFailureIsolation() (gas: 2149374)\n[PASS] testOldRentCannotScoreInNewSeasonAndClaimSurvivesRedeem() (gas: 2231257)\n[PASS] testParameterCapsAndSeasonImmutability() (gas: 481076)\n[PASS] testParkingCannotPayTwiceSameDay() (gas: 2986306)\n[PASS] testPartialInputAndInvalidSettlementRevertAtomically() (gas: 1257305)\n[PASS] testPausedRollCanRevealAndNoETH() (gas: 589450)\n[PASS] testPendingRollCannotHoldSeasonOpenOrAffectFinalScores() (gas: 1043454)\n[PASS] testReentrantTokenCannotWithdrawOrJoinDuringDeposit() (gas: 396882)\n[PASS] testRentBankruptcyAndNextSeasonReset() (gas: 1010279)\n[PASS] testRentEightyTwentyClaimAndRedeemWhilePaused() (gas: 2055588)\n[PASS] testRevealBlockWindowLastBlockAndTimestampExpiry() (gas: 948753)\n[PASS] testSponsorFundsSeparateFromAssetsAndPullClaims() (gas: 1112958)\n[PASS] testSponsorTokenFailureDoesNotBlockLanding() (gas: 1103560)\n[PASS] testSwapOppositeCurrencyOrdering() (gas: 1259003)\n[PASS] testTaxJailBailAndSkip() (gas: 1612997)\n[PASS] testWeightedRentAndNoRetroactiveRewards() (gas: 3285384)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 475.35ms (720.18ms CPU time)\n\nRan 13 tests for test/RevisionRegression.t.sol:RevisionRegressionTest\n[PASS] testCooldownPersistsWhenJoiningNewSeason() (gas: 1299506)\n[PASS] testEmptyListingCanBeCorrected() (gas: 1546303)\n[PASS] testExhaustedDustSponsorCanBeReplaced() (gas: 1805515)\n[PASS] testFundedSponsorCannotBeReplacedOrRateChanged() (gas: 682965)\n[PASS] testObservedDustDeedReceivesHolderShare() (gas: 1551519)\n[PASS] testObservedMissedRevealPenalty() (gas: 529917)\n[PASS] testObservedParkingPaysEachPlayerAndDownsideIsBalance() (gas: 2918506)\n[PASS] testOldSeasonPendingRollMustBeResolvedBeforeRejoining() (gas: 1399996)\n[PASS] testParkingDayPersistsAcrossSameDaySeasonRollover() (gas: 2839042)\n[PASS] testRelistAfterRedemptionPreservesOldRentAndNewDeedBaseline() (gas: 4955717)\n[PASS] testSponsorLiabilitiesBlockRelistingAcrossSeasons() (gas: 1742856)\n[PASS] testZeroRentClaimDoesNotRankButPositiveScoreDoes() (gas: 5364392)\n[PASS] testZeroScoreJoinersLeaveAllPrizeBudgetAvailable() (gas: 2056905)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 475.40ms (14.91ms CPU time)\n\nRan 25 tests for test/Adversarial.t.sol:AdversarialTest\n[PASS] testBindingRejectsEOAUnauthorizedAndWrongDependencies() (gas: 756357)\n[PASS] testCallbackFromManagerWithoutAnActiveSwapIsRejected() (gas: 38108)\n[PASS] testCommitDomainSeparationAndNonceReplay() (gas: 780830)\n[PASS] testConstructorRejectsZeroOwnerAndMismatchedDependencies() (gas: 6275)\n[PASS] testEmptySeasonDoesNotReserveUnassignedPrizes() (gas: 713215)\n[PASS] testEveryBoardSlotMatchesClassicBoardAndStartsVacant() (gas: 9548999)\n[PASS] testFuzzDepositWithdrawRoundtripBeforeJoining(uint128,uint8) (runs: 1000, μ: 793923, ~: 706208)\nLogs:\n  Bound result 2592000\n  Bound result 8\n\n[PASS] testFuzzEveryPrizeRankMatchesFixedWeightsAndRounding(uint96) (runs: 1000, μ: 9134620, ~: 9134542)\nLogs:\n  Bound result 2835717307\n\n[PASS] testFuzzQuoteIsReadOnlyAndMatchesPurchase(uint96) (runs: 1000, μ: 1487285, ~: 1487174)\nLogs:\n  Bound result 8106\n\n[PASS] testFuzzWeightedRentConservationAndExactMaturities(uint96,uint96,uint8) (runs: 1000, μ: 3160439, ~: 3102967)\nLogs:\n  Bound result 85630361116448364200\n  Bound result 14264337593543950335\n\n[PASS] testInternalAccountingNeverReadsVaultTokenBalance() (gas: 1816714)\n[PASS] testInvalidSignedSwapDeltasRevertBeforeAnySettlement() (gas: 2788361)\n[PASS] testManagerReturningWithoutCallbackCannotMintDeeds() (gas: 800641)\n[PASS] testOwnerCannotMoveCustodyThroughGameOnlyEntrypoints() (gas: 741359)\n[PASS] testPendingCommitZeroCommitAndInsufficientBond() (gas: 478551)\n[PASS] testPoolValidationRejectsInvalidKeysAtomically() (gas: 867238)\n[PASS] testQuoteRejectsZeroUnlistedAndOverInt128Amounts() (gas: 97548)\n[PASS] testReentrantWithdrawalHasFundsButIsBlockedDuringDeposit() (gas: 604291)\n[PASS] testRentExactlyCoveredDoesNotBankruptAndOneWeiShortDoes() (gas: 1215229)\n[PASS] testRevealExactlyAtDeadlineAndMissingBlockhash() (gas: 533635)\n[PASS] testRolloverPreservesOldPrizesAndOutstandingSponsorAwards() (gas: 2738716)\n[PASS] testSponsorTopupCompetitionPartialLastAwardAndExactEnd() (gas: 1850065)\n[PASS] testWrongHolderCannotClaimRedeemOrTransferDeed() (gas: 1512330)\n[PASS] test_RevertedBuyRestoresWalletAllowanceAndLandingRight() (gas: 1215660)\n[PASS] test_RevertedRedemptionAndSponsorWithdrawalPreserveClaims() (gas: 2483361)\nSuite result: ok. 25 passed; 0 failed; 0 skipped; finished in 475.47ms (1.91s CPU time)\n\nRan 1 test for test/Invariants.t.sol:AccountingInvariants\n[PASS]\nAccountingInvariants invariants:\n[PASS] invariant_gamePotRentAndVaultSolvent\n[PASS] invariant_rentNeverCreatesMoney\n[PASS] invariant_sharesAndWeightsMatchDeeds\n[PASS] invariant_topTenUniqueAndSorted\n AccountingInvariants invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------+-----------------+-------+---------+----------╮\n| Contract    | Selector        | Calls | Reverts | Discards |\n+============================================================+\n| GameHandler | claimRedeem     | 1717  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | depositWithdraw | 1601  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | newSeason       | 1653  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | rollBuy         | 1640  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| GameHandler | sponsorAndClaim | 1581  | 0       | 0        |\n╰-------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 2557\n  Bound result 7124\n  Bound result 2929\n  Bound result 5516\n  Bound result 6239\n  Bound result 9853\n  Bound result 13135\n  Bound result 8388608\n  Bound result 1256351161\n  Bound result 6106\n  Bound result 67194\n  Bound result 13673\n  Bound result 7000000001248035095\n  Bound result 3617\n  Bound result 224\n  Bound result 4209\n  Bound result 5064\n  Bound result 1580\n  Bound result 64746284112241586\n  Bound result 12832\n  Bound result 4826\n  Bound result 2514000704\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.19s (2.19s CPU time)\n\nRan 3 tests for test/StatefulAccounting.t.sol:StatefulAccountingTest\n[PASS]\nStatefulAccountingTest invariants:\n[PASS] invariantAllCustodyAndLiabilitiesReconcile\n[PASS] invariantLeaderboardContainsExactlyTheBestTen\n StatefulAccountingTest invariants (runs: 256, calls: 24576, reverts: 0)\n\n╭-------------------+-------------------------+-------+---------+----------╮\n| Contract          | Selector                | Calls | Reverts | Discards |\n+==========================================================================+\n| AccountingHandler | claimOrRedeem           | 1911  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | claimSponsorOrRemainder | 1907  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | commit                  | 1822  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | depositOrWithdraw       | 1866  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | donate                  | 1824  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | fund                    | 1869  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | newSeason               | 1940  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | pause                   | 1852  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | prize                   | 1939  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | resolve                 | 1972  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | rollAndBuy              | 3735  | 0       | 0        |\n|-------------------+-------------------------+-------+---------+----------|\n| AccountingHandler | sponsor                 | 1939  | 0       | 0        |\n╰-------------------+-------------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 5000000000000000000\n  Bound result 5000000000000000000\n  Bound result 17404\n  Bound result 17292\n  Bound result 20045\n  Bound result 2726\n  Bound result 15973\n  Bound result 16968\n  Bound result 5096\n  Bound result 951\n  Bound result 949795543542075837\n  Bound result 22774\n  Bound result 1355203833\n  Bound result 16613\n  Bound result 1916\n  Bound result 1\n  Bound result 20478\n  Bound result 10035\n  Bound result 3277\n  Bound result 4264337593543950335\n  Bound result 77\n  Bound result 4212183393\n  Bound result 2339916\n  Bound result 5895075785\n  Bound result 6192\n  Bound result 9459909182100763174\n  Bound result 2278709903\n  Bound result 10622\n  Bound result 29000000000000037583\n  Bound result 2278709903\n  Bound result 20314\n  Bound result 1146975845\n  Bound result 19453\n  Bound result 10361\n  Bound result 66824\n  Bound result 12024\n\n[PASS] testHandlerExercisesRollBuyExpireRedeemAndRollover() (gas: 30251213)\nLogs:\n  Bound result 5000000000000000000\n  Bound result 5000000000000000000\n  Bound result 2000000000000000000\n  Bound result 2000000000000000000\n  Bound result 2000000000000000000\n  Bound result 2000000000000000000\n  Bound result 2000000000000000000\n  Bound result 2000000000000000000\n\n[PASS] testLeaderboardOracleCoversEmptyPartialAndOverflowingRankings() (gas: 17737889)\nLogs:\n  Bound result 5000000000000000000\n  Bound result 5000000000000000000\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 25.85s (25.88s CPU time)\n\nRan 5 test suites in 25.85s (29.47s CPU time): 70 tests passed, 0 failed, 0 skipped (70 total tests)\n","passed":true},{"durationMs":41,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DeedVault.awardSponsor(uint8,address)\",\"DeedVault.beginSeason(uint256,uint256)\",\"DeedVault.bindGame(address)\",\"DeedVault.buy(uint8,address,uint256,uint256,uint8)\",\"DeedVault.claimRent(uint256,address)\",\"DeedVault.claimSponsor(uint8)\",\"DeedVault.creditRent(uint8,uint256)\",\"DeedVault.listTile(uint8,(address,address,uint24,int24,address),uint8)\",\"DeedVault.quote(uint8,uint256)\",\"DeedVault.redeem(uint256)\",\"DeedVault.sponsorTile(uint8,uint256,uint256)\",\"DeedVault.unlockCallback(bytes)\",\"DeedVault.withdrawSponsor(uint256,uint8)\",\"SeasonPot.bindGame(address)\",\"SeasonPot.credit(uint256)\",\"SeasonPot.pay(address,uint256)\",\"SeasonPot.payReserved(address,uint256)\",\"SeasonPot.reserve(uint256)\",\"SwarmopolyGame.buyDeed(uint8,uint256,uint256)\",\"SwarmopolyGame.buyDeed(uint8,uint256,uint256,uint8)\",\"SwarmopolyGame.claimPrize(uint256)\",\"SwarmopolyGame.claimRent(uint256)\",\"SwarmopolyGame.commitRoll(bytes32)\",\"SwarmopolyGame.deposit(uint256)\",\"SwarmopolyGame.expireRoll(address)\",\"SwarmopolyGame.finalizeSeason()\",\"SwarmopolyGame.fundPot(uint256)\",\"SwarmopolyGame.joinSeason()\",\"SwarmopolyGame.listTile(uint8,(address,address,uint24,int24,address),uint8)\",\"SwarmopolyGame.payBail()\",\"SwarmopolyGame.revealRoll(bytes32)\",\"SwarmopolyGame.setParams(uint256,uint256,uint256[8])\",\"SwarmopolyGame.setPaused(bool,bool)\",\"SwarmopolyGame.skipJailRoll()\",\"SwarmopolyGame.startSeason(uint256,uint256,uint256,uint256)\",\"SwarmopolyGame.withdraw(uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":24,\"docs/DEPLOYMENT.md\":52,\"docs/REVIEW.md\":45,\"docs/RULES.md\":45,\"docs/TESTING.md\":33,\"docs/check-results.json\":26,\"docs/network-verification.json\":14,\"foundry.toml\":21,\"launch.json\":9,\"scripts/configure-site.mjs\":20,\"scripts/export-abi.py\":7,\"scripts/verify-network.py\":34,\"site/abi.json\":1,\"site/app.js\":245,\"site/config.json\":13,\"site/index.html\":15,\"site/package.json\":1,\"site/rules.html\":1,\"site/style.css\":2,\"site/vendor/ethers.LICENSE.md\":21,\"site/vendor/ethers.min.js\":1,\"src/DeedVault.sol\":319,\"src/Owned.sol\":17,\"src/SeasonPot.sol\":60,\"src/SwarmopolyGame.sol\":460,\"src/interfaces/IV4.sol\":35,\"test/Adversarial.t.sol\":609,\"test/CONTRIBUTOR_TESTS.md\":56,\"test/Invariants.t.sol\":179,\"test/RevisionRegression.t.sol\":383,\"test/StatefulAccounting.t.sol\":189,\"test/Swarmopoly.t.sol\":691,\"test/helpers/AccountingHandler.sol\":284,\"test/mocks/MockERC20.sol\":60,\"test/mocks/MockPoolManager.sol\":66,\"test/site.test.mjs\":51},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"f7c98dfbdcfa13a5cf556857e8111c7478da465d37b1f2a280d1f24c757ab94b","verifiedTreeHash":"15aef2f9a5e7007d1fae79afd297b660606144f5","verifierVersion":"0.1.0+be003835"}]}