{"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":"552c8576-d8c2-49d7-8ef4-83ef509cb22b","kind":"impl_tests_review","nodes":[{"acceptedSubmissionHash":"abf9e6c7c974989b0f0618b2ce009efd0910bc56c0c10d10b7443384035ac8e5","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"d37537abb20af46832b24602788d7895021ecaac20c6934313dc4f8aba4ad047","skillId":"implement-contract","tools":[]},"key":"impl","kind":"code","role":"implement","skillHash":"d37537abb20af46832b24602788d7895021ecaac20c6934313dc4f8aba4ad047","skillId":"implement-contract","state":"accepted"},{"acceptedSubmissionHash":"1779f5648fcb044fde3d717d500a34e0e5b9952cad5cc31dc90a2ae04cfcf2ef","dependsOn":["impl","tests"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"bb2e2ca4f24330611eb3ddab46c77184c1d8fb1f0f835523814d6c2994792243","dependsOn":["impl","tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"6b037a7b6601e883cf8a906c1520c0624817d42d8310b65c2f43679204608af3","skillId":"adversarial-review","tools":[]},"key":"review","kind":"code","role":"review","skillHash":"6b037a7b6601e883cf8a906c1520c0624817d42d8310b65c2f43679204608af3","skillId":"adversarial-review","state":"accepted"},{"acceptedSubmissionHash":"8a4d56ddb4b3d6320b5eb52be5e5bca4a9740bd102a0dc995c35b389d3f93920","dependsOn":["impl"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","tools":[]},"key":"tests","kind":"code","role":"tests","skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Token name: Work\nToken symbol: WORK\n\nRobinhood Chain 4663, Uniswap v4 hook launch paired with IMD (0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127). B = 0xc9EAFE33A510a3a3d95A94c4f85AdaF6a3EA12a0 (EOA). Bounded owner changes emit events; every parameter has a view. Not upgradeable.\n\nDEPLOY SHAPE (launch.json deploys only a no-arg token and the hook): src/Work.sol = plain ERC-20 Work/WORK, 18 decimals, constructor() mints 1,000,000,000 to msg.sender, nothing else. src/WorkHook.sol constructor(IPoolManager manager, address token, address owner, int24 tickSpacing) creates WorkersNFT with new (passing token, owner, address(this) as hook), exposed as workers(). launch.json hook constructorArgs [\"$poolManager\",\"$token\",\"0xc9eafe33a510a3a3d95a94c4f85adaf6a3ea12a0\",\"60\"], pool fee 12500, tickSpacing 60. Hook initcode < 49,152 bytes, every runtime < 24,576. foundry.toml: optimizer true, runs 200, via_ir true, bytecode_hash \"none\", solc 0.8.26, cancun. No linked libraries (all library functions internal). Tests use vm.getBlockTimestamp()/vm.getBlockNumber().\n\n1) WorkHook: fee 0.75% on buys and sells (on top of the 1.25% pool fee), owner may set 0-8.75%. During the NFT mint 100% to B; after it ends stakerShareBps (default 10000, owner 9000-10000) goes to WorkersNFT stakers, rest to B; all to B if nothing staked. Keeps a 30-min TWAP for buybacks.\n\n2) WorkersNFT (ERC-721 \"Workers\", max 3,333). setCap lowers to >= minted, never up; endMint() ends minting forever; pause() stops minting and WORK accrual for at most 24 h, next pause needs 24 h unpaused; claims, activation, unstake, transfers and burns always work.\n- Mint by PoW: keccak256(abi.encode(DOMAIN, chainid, this, keccak256(abi.encode(anchorHash, prevWork)), msg.sender, nonce)) < target; anchor = ArbSys block hash <= 250 blocks old; 1 mint per block; retarget every 8 mints toward 10 s; target doubles after 300 s idle; 9 epochs of 370 (+3), floor +1 bit per epoch, owner floor +-2.\n- Paid in IMD (transferFrom, no ETH): [0.25,0.5,0.75,1.25,1.75,2.5,3.25,4,5] IMD per epoch, never above 5; owner scales 0.5x-1x only.\n- id = pool[hash % remaining] (swap-and-pop, no reserves, no allowlist, no free or team mint). Overclocked if hash beats target by >= 4 bits: tokenURI baseURI+\"oc/\"+id+\".json\" else baseURI+id+\".json\"; baseURI ipfs://bafybeibmkvtnptsqztdvsm5sh3avormdnfyarbxpgdlcj6bmg37qchhqve/ (owner may update). ERC-4906 events. ERC-2981 5% to B, max 10%.\n\n3) Money in IMD, each mint: 65% stakers, 25% buyback balance, 10% B (no stakers: that 65% to buyback).\n- Staking: NFT escrowed in the contract 7/14/30 days = 1x/2x/4x, no early exit; claim IMD (mint share + hook fees) by weight, payouts capped at the reserve; unstake returns by plain transfer. Unstaked workers transfer normally (never blocked).\n- buyback(amount): anyone, amount <= 100 IMD and <= balance, once per 60 s, IMD->WORK in the pool with TWAP minimum output, WORK sent to 0x...dEaD.\n- Supply: 10% swarm, 70% pool, 20% to B; B deposits 100M WORK mining pool + 50M burn pool via fundPools().\n\n4) After the mint:\n- activate(id): one time, fee to B (default 0.15 IMD, owner 0-0.5), stays with the token.\n- Passive WORK, no hashing: 8 eras from mint end, era k lasts 7*2^k days and emits 12.5M, split among activated unburned workers (accumulator); claimWork(ids) by holder or staker; unclaimed moves with the token. WORK scheduled while paused or with no activated worker is carried and released at the next unpaused update. After all eras releaseMiningRemainder() (owner, not paused) sends the rest to B.\n- burn(id): unstaked worker for burnPool / minted (fixed at mint end); burned workers stop earning.\n\nSafety: sweep(token) to B for strays but never staker IMD, buyback balance, mining or burn pool. No minting outside PoW, no owner path to user funds.\n\nTests: Foundry incl. fuzz on work checks, retarget, id picking, splits, escrow, hook split, activation, accrual and carry, burn, buyback amount, pause scope, sweep limits; then an adversarial review.","parentJobId":null,"planHash":"6300f086ba3a342597222ec15e7c63300b4cdbdd77de57e35b9c8cbcbe0b6c59","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"552c8576-d8c2-49d7-8ef4-83ef509cb22b","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-930-src-work-sol-src-workersnft-sol-src-work"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51353","feedbackHash":"36741e240f5dbb249d27d12cd57c7710a02b28bc1431825b7f2beb9f2120fb67","nodeKey":"impl","submissionHash":"abf9e6c7c974989b0f0618b2ce009efd0910bc56c0c10d10b7443384035ac8e5","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51360","feedbackHash":"5231e4420e6eb040c64c7d176fce3965f65cc6a2a21a9b9722218691fcd1e85b","nodeKey":"manifest","submissionHash":"1779f5648fcb044fde3d717d500a34e0e5b9952cad5cc31dc90a2ae04cfcf2ef","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51512","feedbackHash":"1c9aec68c636a2ef9246d25cc41e0bdd2ee522484eba23a94a1b7b949d50ee5f","nodeKey":"review","submissionHash":"bb2e2ca4f24330611eb3ddab46c77184c1d8fb1f0f835523814d6c2994792243","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52198","feedbackHash":"96c8a05141ba1ffddc2da2ccb58b3289cebca5c7f672c5e775eb8051789496c8","nodeKey":"tests","submissionHash":"8a4d56ddb4b3d6320b5eb52be5e5bca4a9740bd102a0dc995c35b389d3f93920","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"d03013ebfd428ad37919cc7c72c996771d2f46dca7acc2cf724504a865945513","state":"completed","submissions":[{"artifacts":[],"attempt":2,"bundleHash":"60c03f11ec58cdd911d7150876e02aa77e99e2ab7594e784c3dd81fd99fbbc51","device":"5f74c0ba32743d88","findings":[],"hash":"1779f5648fcb044fde3d717d500a34e0e5b9952cad5cc31dc90a2ae04cfcf2ef","nodeId":"8d05ef54-e9a4-4242-8a95-f0626aa17b41","outcome":"completed","summary":"Created [launch.json](/home/seat/.identitymd/work/552c8576-d8c2-49d7-8ef4-83ef509cb22b/8d05ef54-e9a4-4242-8a95-f0626aa17b41/launch.json) with matching constructor arguments, permissions, required pool settings, and tick-zero initial price.\n\nSchema and bytecode-size checks pass. `forge build` and all 55 tests pass using explicit compiler settings; missing `foundry.toml` is noted.\n\nOnly `launch.json` changed.","treeHash":"6d5b25139194ca9152b98c5b1864effca9bfd6a7","usage":{"cachedInputTokens":762112,"inputTokens":61751,"model":"gpt-6-astra","outputTokens":8252,"runtime":"codex","turns":5,"wallClockMs":460256}},{"artifacts":[],"attempt":1,"bundleHash":"ae8bfc966da369b2d9495f79d5bea4975e5fd0d7a264cfba7d78dd586fb6c82b","device":"45733b6bb4e97b16","findings":[{"description":"The supplied tree has no foundry.toml. Default forge build therefore compiles without optimization or via-IR, rather than the required optimizer=true/runs=200/via_ir=true/bytecode_hash=none/solc=0.8.26/cancun profile. WorkersNFT runtime is 34,809 bytes (limit 24,576); WorkHook creation bytecode is 62,170 bytes, or 62,298 including its four constructor arguments (limit 49,152). Foundry test deployment bypasses these limits, so behavioral tests passing do not establish deployability. Protected configuration was not changed. An exploratory optimized via-IR Cancun build produces WorkersNFT runtime 19,162 bytes and WorkHook creation code 33,054 bytes, confirming that the missing required build settings are material.","line":1,"path":"foundry.toml","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\nimport {Test} from \"forge-std/Test.sol\";\nimport {Work} from \"src/Work.sol\";\nimport {WorkersNFT} from \"src/WorkersNFT.sol\";\nimport {WorkHook} from \"src/WorkHook.sol\";\n\ncontract DeploymentSizeProof is Test {\n    function test_WorkersRuntimeMustFitEIP170() public {\n        Work token = new Work();\n        WorkersNFT workers = new WorkersNFT(address(token), address(this), address(1));\n        assertLt(address(workers).code.length, 24_576, \"WorkersNFT runtime exceeds EIP-170\");\n    }\n    function test_HookInitcodeIncludingArgumentsMustFitEIP3860() public pure {\n        assertLt(type(WorkHook).creationCode.length + 128, 49_152, \"WorkHook initcode exceeds EIP-3860\");\n    }\n}","reproduction":"From the supplied tree, run forge build --sizes: WorkersNFT runtime=34809, WorkHook initcode=62170 before arguments, and the command reports the size violation. Place the attached source in test/scratch/DeploymentSizeProof.t.sol with forge-std available and run forge test --match-path test/scratch/DeploymentSizeProof.t.sol. Both assertions fail: expected runtime <24576 and initcode+128 <49152, actual 34809 and 62298. Locally reproduced with a scratch-only standard assertLt shim because the repository contains no forge-std dependency.","severity":"medium","title":"Missing compiler profile makes the default artifacts undeployable"},{"description":"The repository contains no launch.json, so it does not deliver the required reproducible deployment of the no-argument Work token and WorkHook with [$poolManager,$token,0xc9eafe33a510a3a3d95a94c4f85adaf6a3ea12a0,60], pool fee 12500, and tick spacing 60. Tests validate the constructors and pool-key constraints directly; they cannot substitute for the missing launch artifact. The manifest/configuration is outside the permitted edit paths.","line":1,"path":"launch.json","reproduction":"Run git ls-files launch.json and test -f launch.json in the supplied checkout: there is no tracked file and the existence check returns false. Expected: a launch.json carrying the specified token/hook deployment shape and pool parameters. Actual: no deployment manifest exists.","severity":"medium","title":"Required launch manifest is absent"}],"hash":"8a4d56ddb4b3d6320b5eb52be5e5bca4a9740bd102a0dc995c35b389d3f93920","nodeId":"0f9c4d9c-d681-4d35-8762-969e597e69df","outcome":"completed","summary":"Implemented adversarial, fuzz, and stateful invariant tests in all three allowed files.\n\n`forge build` and `forge test` pass: **55 tests, zero failures**.\n\nReported two deployment defects in `.imd-findings.json`: missing compiler settings produce oversized artifacts, and `launch.json` is absent. Included a reproduced failing size proof.\n\nIntegration coverage uses offline stand-ins; live fork validation remains outstanding.","treeHash":"6ee5f4f71ff4068eca0ae3d6108ffe36d3d0bcae","usage":{"cachedInputTokens":2483712,"inputTokens":108047,"model":"gpt-6-astra","outputTokens":35227,"runtime":"codex","turns":7,"wallClockMs":748394}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"cf3b0a0c089b1430","findings":[],"hash":"a61281ee75ddceab89577e55e82d58ac6b5ca1daf9d68ed7ec7a0581cc9515f5","nodeId":"8d05ef54-e9a4-4242-8a95-f0626aa17b41","outcome":"failed","summary":"the task produced no changes; the agent's last message was: Blocked by the workspace runtime: every command fails with `bwrap: setting up uid map: Permission denied`.\n\nI could not inspect the hook’s permissions; `forge build` and `forge test` both failed before execution. No files were changed. Restore command execution so I can create and validate `launch.json` against the actual contracts.","treeHash":null,"usage":{"cachedInputTokens":86400,"inputTokens":19747,"model":"gpt-6-astra","outputTokens":1938,"runtime":"codex","turns":4,"wallClockMs":105167}},{"artifacts":[],"attempt":1,"bundleHash":"8983447be53dad9d1df4dea8151a30b576c9df602823b90c912834e2b4836a15","device":"60f85cbc35c28a7e","findings":[],"hash":"abf9e6c7c974989b0f0618b2ce009efd0910bc56c0c10d10b7443384035ac8e5","nodeId":"70a75a00-199e-4ef7-9740-38810715f6f5","outcome":"completed","summary":"Implemented all three allowed contracts without external dependencies.\n\n- Clean offline `forge build` passed with the requested settings.\n- `forge test`: **75 passed**, including **6,912 fuzz cases** and real Uniswap v4 integration.\n- All runtime and initcode size limits passed.\n- Adversarial review completed.\n\nHook deployment requires CREATE2 address flags `0x30cc`.","treeHash":"0db4e0fcffaadbf46190324d6fad1bd1964b56ec","usage":{"cachedInputTokens":1790848,"inputTokens":128850,"model":"gpt-6-astra","outputTokens":41623,"runtime":"codex","turns":8,"wallClockMs":2463317}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"8c9c4986ca881107","findings":[{"citation":"resolved","description":"executeBuyback charges feeBps on the buyback amount and distributes it (to stakers after mint end, else to B) before swapping only amount - fee. The brief describes buyback(amount) as 'IMD->WORK in the pool with TWAP minimum output' and lists the hook fee as applying to 'buys and sells' by users; it does not say the protocol's own buyback is taxed. The effect is that up to 8.75% (owner-settable) of every buyback is diverted from burning WORK to stakers/B, and before mint end 100% of that skim goes to B. The code comment states this is intentional ('Charge the same buy fee'), so this is a scope decision for the requester rather than a bug, but it reduces the buyback's stated purpose and is not covered by the brief.","line":374,"path":"src/WorkHook.sol","reproduction":"Mint one worker (ALICE), stake it, endMint, initialize the pool at tick 0, fund the mock manager with WORK, warp +1800s. amount = nft.buybackBalance(); call nft.buyback(amount). Expected (per brief): pool receives `amount` IMD. Actual: FeeDistributed(amount*75/10000, amount*75/10000, 0) is emitted, the pool receives amount - amount*75/10000 IMD, and nft.stakerReserve() == amount*75/10000. Verified in scratch test ProbeHook.test_BuybackSkimsHookFee.","severity":"low","snippet":"        _distributeFee(fee);","title":"Protocol buyback pays the 0.75% hook fee out of the buyback reserve"},{"citation":"resolved","description":"For exact-input IMD buys and exact-output IMD sells the hook takes its fee in beforeSwap and cannot refund it, so afterSwap requires the pool's IMD delta to equal the full grossed amount. Consequently a swap whose sqrtPriceLimitX96 is reached before the full amount is consumed, or that exhausts pool liquidity, reverts instead of partially filling as Uniswap v4 normally does. Routers and aggregators that set tight price limits will see 'Partial IMD fill' failures on this pool. This is documented in a code comment and is a defensible trade-off, but it is a user-visible liveness restriction not stated in the brief; the alternative (charge the fee on the actual filled amount in afterSwap for all directions, taking from the unspecified side) would change the fee base and needs a scope decision.","line":243,"path":"src/WorkHook.sol","reproduction":"Initialize the pool at tick 0. params = exact-input, IMD specified, amountSpecified = -1e18. Drive the mock pool to fill only 0.9e18 IMD: manager.trade(key, params, -0.9e18, 1e18, \"\"). Expected under standard v4 semantics: swap completes for 0.9e18 with fee on the filled amount. Actual: revert 'Partial IMD fill'. Verified in scratch test ProbeHook.test_PartialFillReverts; the shipped test test_PartialSpecifiedFillRevertsAtomicallyAndUnspecifiedChargesActual asserts the same behaviour as intended.","severity":"low","snippet":"            require(_absolute(int256(imdDelta)) == expected, \"Partial IMD fill\");","title":"Any partial fill of an IMD-specified swap reverts the whole swap"},{"citation":"resolved","description":"activate only checks mintEnded. After releaseMiningRemainder() sets miningClosed, _updateMining returns immediately and no WORK can ever accrue to a newly activated worker, yet activate still pulls activationFee (0.15 IMD default, up to 0.5) from the caller and forwards it to B. A holder who activates late pays for nothing. Adding `!miningClosed` to the require (or making the fee zero once closed) would preserve the design.","line":547,"path":"src/WorkersNFT.sol","reproduction":"Mint a and b, endMint, fundPools, ALICE activates a, warp to mintEndTime + MINING_DURATION, B calls releaseMiningRemainder() (miningClosed == true). BOB calls activate(b): succeeds, BOB's IMD balance drops by 0.15e18, and pendingWork(b) stays 0 forever (checked after +365 days). Verified in scratch test ProbeNFT.test_ActivateAfterMiningClosedPaysForNothing.","severity":"low","snippet":"        require(mintEnded && holderOf(id) == msg.sender && !activated[id], \"Cannot activate\");","title":"activate() still charges the fee after mining is closed, with no possible benefit"},{"citation":"resolved","description":"`remaining` is always MAX_SUPPLY - minted, so after setCap(n) the swap-and-pop pool still contains all 3,333 IDs and the collection ends with n tokens scattered across 1..3333 (e.g. cap 3 can yield IDs 1200, 2987, 41). The brief says 'id = pool[hash % remaining]' without defining remaining; the code deliberately documents the full-pool reading. If the intended collection after a cap cut is IDs 1..cap (contiguous metadata, 'max 3,333' as an upper bound), this is a deviation and the metadata set under baseURI must cover all 3,333 IDs regardless of cap. Scope question for the requester; no funds impact.","line":421,"path":"src/WorkersNFT.sol","reproduction":"B calls setCap(3). Perform three PoW mints with seeds 0,1,2. Expected (contiguous reading): every id <= 3. Actual: at least one minted id > 3 (asserted true in scratch test ProbeNFT.test_IdsAboveLoweredCap).","severity":"low","snippet":"        uint256 remaining = MAX_SUPPLY - minted;","title":"Lowering the cap does not shrink the ID pool: IDs above the cap are minted"},{"citation":"resolved","description":"Each _addIMD floors amount*1e27/totalWeight, so the sum of all claimable amounts is strictly less than stakerReserve by up to totalWeight-1 wei per deposit. The remainder stays inside stakerReserve, is never claimable (claimIMD is capped per account by its own credit) and is excluded from sweepable(). Over thousands of hook-fee deposits this is still negligible value (wei), so info only.","line":491,"path":"src/WorkersNFT.sol","reproduction":"Mint a (ALICE) and b (BOB), endMint, stake a for 7 days (weight 1) and b for 30 days (weight 4). Mint 7 wei IMD to the NFT and have the hook call notifyHookFees(7). ALICE claims 1 wei, BOB claims 5 wei; stakerReserve() == 1 and sweepable(IMD) == 0 afterwards, with no path to release the 1 wei. Verified in scratch test ProbeNFT.test_StakerDustLocked.","severity":"info","snippet":"        accIMDPerWeight += amount * REWARD_PRECISION / totalWeight;","title":"Rounding dust in staker IMD rewards is permanently locked"},{"citation":"resolved","description":"burnPerWorker = 50M / minted truncates, leaving 50M % minted wei in burnPool forever, and the allocation of workers that are never burned also stays reserved forever. sweep() correctly excludes burnPool (as the brief requires) and no owner function releases it, unlike the mining remainder. This matches the brief literally ('never ... burn pool'); flagged only so the requester confirms the permanent lock is intended.","line":664,"path":"src/WorkersNFT.sol","reproduction":"Shipped test test_BurnFixedPayoutAndStopsEarning: after all three workers burn, burnPool() == 50_000_000e18 % 3 == 2 wei, and sweepable(work) == 0; no function can move those 2 wei. With 3,333 minted and 1,000 never burned, 50M/3333*1000 ≈ 15.0M WORK remains locked in the contract permanently.","severity":"info","snippet":"        if (token == work) reserved = miningPool + burnPool;","title":"Burn-pool remainder and never-burned allocations have no release path"},{"citation":"resolved","description":"EIP-721 does not forbid approving the current owner or oneself as operator; OpenZeppelin v5 accepts both. Some generic tooling that resets approvals by approving the owner will revert here. No security impact.","line":209,"path":"src/WorkersNFT.sol","reproduction":"ALICE owns id. vm.prank(ALICE); nft.approve(ALICE, id) reverts 'Self approval'; nft.setApprovalForAll(ALICE, true) from ALICE reverts 'Self approval'. An OZ ERC721 succeeds on both.","severity":"info","snippet":"        require(to != holder, \"Self approval\");","title":"ERC-721 deviation: approve(currentOwner) and setApprovalForAll(self) revert"},{"citation":"resolved","description":"TestPoolManager.trade receives the pool delta from the test itself (HookFixture._trade computes it from the hook's own fee formula), so the 'full fill' assertion in afterSwap and the beforeSwap/afterSwap delta signs are only checked against the test's model of v4, not against v4's Hooks.beforeSwap/afterSwap arithmetic (amountToSwap += hookDeltaSpecified; swapDelta -= hookDelta) or real take/settle accounting. I traced those paths by hand against v4-core v4.0.0 and found them consistent, and quoteAtTick is only value-checked at tick 0 in the default currency ordering (non-zero ticks are checked by inequality only). The brief's Robinhood/Nitro ArbSys behaviour is mocked via vm.etch + vm.mockCall. A fork test on a v4 deployment remains the only way to close this gap.","line":23,"path":"test/WorkHook.t.sol","reproduction":"Shipped WorkHookTest never calls hook.quoteAtTick with a non-zero tick in the IMD-as-currency1 ordering; scratch test ProbeHook.test_QuoteDirectionDefaultOrdering adds the value check (quoteAtTick(100, 1e18) == mulDiv(1e18, 2^192, sqrtP(100)^2) < 1e18) and passes, so the code is correct but the shipped suite would not catch an inverted direction in that ordering.","severity":"info","snippet":"// It models settlement, not concentrated-liquidity price discovery. A live fork remains owed.","title":"Hook tests rely on a self-consistent PoolManager mock; no fork or real v4 settlement path"},{"citation":"resolved","description":"The brief requires solc 0.8.26, optimizer on, 200 runs, via_ir, bytecode_hash none and cancun, and the size limits were verified under exactly those settings. Nothing in the repository pins them; a verifier using different settings gets different bytecode hashes and sizes. Reviewers are forbidden from adding configuration, so this is reported as a service/configuration gap rather than a code defect.","line":32,"path":"launch.json","reproduction":"`ls foundry.toml` in the repository root -> no such file. Building with default forge settings (no via_ir, 200 runs, latest solc) yields different creation bytecode than the attested settings.","severity":"info","snippet":"  \"notes\": \"Build configuration gap: this checkout contains no foundry.toml.","title":"Checkout has no foundry.toml: compiler settings required by the brief are unpinned"}],"hash":"bb2e2ca4f24330611eb3ddab46c77184c1d8fb1f0f835523814d6c2994792243","nodeId":"056e18b0-2b28-4090-9e25-a4f690a1f37d","outcome":"completed","summary":"The audit is complete and `.imd-findings.json` is written at the repository root. The working tree is otherwise untouched (no source, test, or config changes; scratch probes lived only in `/tmp`).\n\n**Outcome: no critical, high, or medium defects.** The implementation holds against the brief and against Uniswap v4 semantics as far as I could trace without a fork. Nine low/info observations are recorded, each with a concrete reproduction.\n\n**What I verified**\n\n- All 55 shipped tests pass under the brief's compiler settings (0.8.26, via_ir, 200 runs, cancun). Fuzz ran at 1000 and invariants at 256x50 with fail-on-revert.\n- Sizes: hook initcode 32,972 B, runtimes 1,240 / 12,127 / 19,121 B. No linked libraries.\n- Hook flags 0x30cc match the permissions and launch.json. Constructor argument order matches the manifest.\n- Hand-traced v4 `BeforeSwapDelta` signs, `afterSwap` delta subtraction, hook self-swap callback exemption, take/settle netting, and the slot-6 tick read. All consistent with v4-core v4.0.0.\n- Custom `sqrtPriceAtTick` compared against the Uniswap TickMath algorithm across all 1.77M ticks. Max relative deviation is 4.6e-29.\n- Traced PoW formula, anchor window, one-mint-per-L2-block, retarget and idle doubling, epoch floors, swap-and-pop, the 65/25/10 split, MasterChef IMD accounting, mining accumulator with carry/pause/burn/remainder, escrow transfer rules, and sweep reserves. No path to user funds, no stuck-claim path, no accounting drift.\n\n**Findings (ranked)**\n\n1. Low: the protocol buyback pays the 0.75% hook fee from the buyback reserve. Intentional per a code comment, but not in the brief.\n2. Low: any partial fill of an IMD-specified swap reverts. Tight-price-limit routers will fail on this pool.\n3. Low: `activate` still charges the fee after mining is closed, with no possible benefit.\n4. Low: lowering the cap keeps the full 3,333-ID pool, so IDs above the cap are minted. Scope question.\n5. Info: staker reward rounding dust is permanently locked.\n6. Info: burn-pool residue and never-burned allocations have no release path.\n7. Info: `approve(owner)` and `setApprovalForAll(self)` revert, a minor ERC-721 deviation.\n8. Info: hook tests use a self-consistent PoolManager mock. No fork test exists, and non-zero-tick quotes are only inequality-checked in the default ordering.\n9. Info: the checkout has no foundry.toml, so the required compiler settings are unpinned.\n\nThe coverage record answers all 38 listed entry points plus 4 invariant rows. Six entry points reference a finding, all low or info, and the rest are marked `holds`. No proof files were attached because nothing reached high severity.","treeHash":null,"usage":{"cachedInputTokens":1588935,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":77696,"runtime":"claude","turns":20,"wallClockMs":1135260}}],"verification":[{"checks":[{"durationMs":897,"exitCode":0,"name":"build","output":"Compiling 6 files with Solc 0.8.26\nSolc 0.8.26 finished in 792.19ms\nCompiler run successful!\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n   ╭▸ src/WorkersNFT.sol:20:17\n   │\n20 │         require(IWorkToken(token).balanceOf(address(this)) == beforeBalance + amount, \"Short transfer\");\n   │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\nwarning[missing-events-arithmetic]: `feeBps` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkHook.sol:168:9\n    │\n168 │         feeBps = bps;\n    │         ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observationIndex` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkHook.sol:301:9\n    │\n301 │         observationIndex = (observationIndex + 1) % OBSERVATION_CAPACITY;\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observationCount` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkHook.sol:303:54\n    │\n303 │         if (observationCount < OBSERVATION_CAPACITY) observationCount++;\n    │                                                      ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `royaltyBps` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkersNFT.sol:289:9\n    │\n289 │         royaltyBps = bps;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `priceScaleBps` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkersNFT.sol:301:9\n    │\n301 │         priceScaleBps = bps;\n    │         ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `floorAdjustment` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkersNFT.sol:307:9\n    │\n307 │         floorAdjustment = adjustment;\n    │         ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `difficultyTarget` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkersNFT.sol:309:39\n    │\n309 │         if (difficultyTarget > limit) difficultyTarget = limit;\n    │                                       ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-access-control]: `totalWeight` is changed without an event but is used for access control\n    ╭▸ src/WorkersNFT.sol:470:9\n    │\n470 │         totalWeight += weight;\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `_owners` is changed without an event but is used for access control\n    ╭▸ src/WorkersNFT.sol:243:9\n    │\n243 │         _owners[id] = to;\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `activated` is changed without an event but is used for access control\n    ╭▸ src/WorkersNFT.sol:633:13\n    │\n633 │             activated[id] = false;\n    │             ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `_owners` is changed without an event but is used for access control\n    ╭▸ src/WorkersNFT.sol:636:16\n    │\n636 │         delete _owners[id];\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/WorkHook.sol:141:39\n    │\n141 │     constructor(IPoolManager manager, address token_, address owner_, int24 tickSpacing_) {\n    │                                       ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/WorkersNFT.sol:172:17\n    │\n172 │     constructor(address token, address owner_, address hook_) {\n    │                 ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/WorkersNFT.sol:265:13\n    │\n265 │             digits++;\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkersNFT.sol:269:39\n    │\n269 │             result[--digits] = bytes1(uint8(48 + n % 10));\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[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/WorkersNFT.sol:277:20\n    │\n277 │         return (B, salePrice / 10_000 * royaltyBps + salePrice % 10_000 * royaltyBps / 10_000);\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:202:39\n    │\n202 │         observations[0] = Observation(uint64(block.timestamp), 0, tick);\n    │                                       ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/WorkHook.sol:225:73\n    │\n225 │         return (IHooks.beforeSwap.selector, BeforeSwapDelta.wrap(int256(fee << 128)), 0);\n    │                                                                         ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/WorkHook.sol:213:9\n    │\n213 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:220:34\n    │\n220 │             require(specified <= uint256(uint128(type(int128).max)), \"Swap too large\");\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/WorkHook.sol:220:42\n    │\n220 │             require(specified <= uint256(uint128(type(int128).max)), \"Swap too large\");\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/WorkHook.sol:222:40\n    │\n222 │             require(specified + fee <= uint256(uint128(type(int128).max)), \"Delta too large\");\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/WorkHook.sol:222:48\n    │\n222 │             require(specified + fee <= uint256(uint128(type(int128).max)), \"Delta too large\");\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/WorkHook.sol:225:66\n    │\n225 │         return (IHooks.beforeSwap.selector, BeforeSwapDelta.wrap(int256(fee << 128)), 0);\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[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/WorkHook.sol:231:9\n    │\n231 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:248:44\n    │\n248 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\n    │                                            ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:248:51\n    │\n248 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\n    │                                                   ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:257:33\n    │\n257 │         return imdIsCurrency0 ? int128(BalanceDelta.unwrap(delta) >> 128) : int128(BalanceDelta.unwrap(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/WorkHook.sol:257:77\n    │\n257 │         return imdIsCurrency0 ? int128(BalanceDelta.unwrap(delta) >> 128) : int128(BalanceDelta.unwrap(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/WorkHook.sol:261:33\n    │\n261 │         return imdIsCurrency0 ? int128(BalanceDelta.unwrap(delta)) : int128(BalanceDelta.unwrap(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/WorkHook.sol:261:70\n    │\n261 │         return imdIsCurrency0 ? int128(BalanceDelta.unwrap(delta)) : int128(BalanceDelta.unwrap(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/WorkHook.sol:266:16\n    │\n266 │         return uint256(value < 0 ? -value : value);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/WorkHook.sol:285:9\n    │\n285 │         emit FeeDistributed(amount, stakerPart, amount - stakerPart);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/WorkHook.sol:280:13\n    │\n280 │         if (stakerPart != 0) {\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/WorkHook.sol:295:13\n    │\n295 │         if (previous.timestamp == block.timestamp) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:300:58\n    │\n300 │             previous.cumulative + int96(previous.tick) * int96(uint96(block.timestamp - previous.timestamp));\n    │                                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int96' 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/WorkHook.sol:300:64\n    │\n300 │             previous.cumulative + int96(previous.tick) * int96(uint96(block.timestamp - previous.timestamp));\n    │                                                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint96' 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/WorkHook.sol:302:54\n    │\n302 │         observations[observationIndex] = Observation(uint64(block.timestamp), cumulative, tick);\n    │                                                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/WorkHook.sol:322:16\n    │\n322 │         while (low < high) {\n    │                ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:328:64\n    │\n328 │         return int256(point.cumulative) + int256(point.tick) * int256(timestamp - point.timestamp);\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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/WorkHook.sol:332:32\n    │\n332 │         require(initialized && block.timestamp >= TWAP_WINDOW, \"TWAP unavailable\");\n    │                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:334:39\n    │\n334 │         int256 average = difference / int256(TWAP_WINDOW);\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/WorkHook.sol:335:44\n    │\n335 │         if (difference < 0 && difference % int256(TWAP_WINDOW) != 0) average--;\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/WorkHook.sol:336:16\n    │\n336 │         return int24(average);\n    │                ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' 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 `observations` is updated\n    ╭▸ src/WorkHook.sol:282:13\n    │\n282 │             workers.notifyHookFees(stakerPart);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_entered` is updated\n    ╭▸ src/WorkHook.sol:376:30\n    │\n376 │         workOut = abi.decode(poolManager.unlock(\"\"), (uint256));\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/WorkHook.sol:379:9\n    │\n379 │         emit BuybackExecuted(amount, workOut, minimum);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:390:35\n    │\n390 │                 amountSpecified: -int256(input),\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/WorkHook.sol:397:26\n    │\n397 │         require(paid == -int256(input) && received > 0, \"Incomplete buyback\");\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/WorkHook.sol:398:26\n    │\n398 │         uint256 output = uint256(uint128(received));\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/WorkHook.sol:398:34\n    │\n398 │         uint256 output = uint256(uint128(received));\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/WorkHook.sol:408:20\n    │\n408 │     function sweep(address asset) external nonReentrant returns (uint256 amount) {\n    │                    ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:422:21\n    │\n422 │         uint256 n = uint256(tick < 0 ? -int256(tick) : int256(tick));\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/WorkHook.sol:431:16\n    │\n431 │         return uint160((ratio >> 32) + (ratio & 0xffffffff == 0 ? 0 : 1));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' 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/WorkersNFT.sol:334:16\n    │\n334 │         return block.timestamp < pausedUntil;\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/WorkersNFT.sol:338:30\n    │\n338 │         require(!paused() && block.timestamp >= nextPauseAt, \"Pause cooldown\");\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkersNFT.sol:371:16\n    │\n371 │         return uint256(int256(BASE_FLOOR_BITS + epoch()) + floorAdjustment);\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/WorkersNFT.sol:371:24\n    │\n371 │         return uint256(int256(BASE_FLOOR_BITS + epoch()) + floorAdjustment);\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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/WorkersNFT.sol:382:13\n    │\n382 │         if (shifts >= 256 || difficultyTarget > (limit >> shifts)) return limit;\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/WorkersNFT.sol:382:30\n    │\n382 │         if (shifts >= 256 || difficultyTarget > (limit >> shifts)) return limit;\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/WorkersNFT.sol:384:16\n    │\n384 │         return value > limit ? limit : value;\n    │                ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_entered` is updated\n    ╭▸ src/WorkersNFT.sol:250:17\n    │\n250 │                 IWorkersReceiver(to).onERC721Received(msg.sender, from, id, data)\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/WorkersNFT.sol:434:32\n    │\n434 │             uint256 adjusted = target / 80 * bounded + target % 80 * bounded / 80;\n    │                                ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/WorkersNFT.sol:433:31\n    │\n433 │             uint256 bounded = elapsed < 20 ? 20 : (elapsed > 320 ? 320 : elapsed);\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/WorkersNFT.sol:433:52\n    │\n433 │             uint256 bounded = elapsed < 20 ? 20 : (elapsed > 320 ? 320 : elapsed);\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/WorkersNFT.sol:435:32\n    │\n435 │             difficultyTarget = adjusted == 0 ? 1 : adjusted;\n    │                                ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkersNFT.sol:472:40\n    │\n472 │         stakes[id] = Stake(msg.sender, uint64(unlockTime), weight);\n    │                                        ━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/WorkersNFT.sol:479:50\n    │\n479 │         require(position.staker == msg.sender && block.timestamp >= position.unlockTime, \"Locked or not staker\");\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_entered` is updated\n    ╭▸ src/WorkersNFT.sol:528:19\n    │\n528 │         workOut = IWorkBuyback(hook).executeBuyback(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/WorkersNFT.sol:523:35\n    │\n523 │         require(!hasBoughtBack || block.timestamp >= lastBuyback + BUYBACK_INTERVAL, \"Buyback cooldown\");\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/WorkersNFT.sol:529:9\n    │\n529 │         emit Buyback(amount, workOut);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/WorkersNFT.sol:583:35\n    │\n583 │             uint256 distributed = increment * activeWorkers;\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/WorkersNFT.sol:194:9\n    │\n194 │         require(account != address(0), \"Unknown worker\");\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/WorkersNFT.sol:605:13\n    │\n605 │             require(holderOf(id) == msg.sender, \"Not holder or staker\");\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/WorkersNFT.sol:649:58\n    │\n649 │     function releaseMiningRemainder() external onlyOwner nonReentrant returns (uint256 amount) {\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/WorkersNFT.sol:650:30\n    │\n650 │         require(mintEnded && block.timestamp >= mintEndTime + MINING_DURATION && !paused(), \"Schedule not finished\");\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/WorkersNFT.sol:666:26\n    │\n666 │         return balance > reserved ? balance - reserved : 0;\n    │                          ━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/WorkersNFT.sol:669:20\n    │\n669 │     function sweep(address token) external nonReentrant returns (uint256 amount) {\n    │                    ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\n","passed":true},{"durationMs":4427,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/WorkHook.t.sol:WorkHookReverseOrderTest\n[PASS] testFuzz_ReverseCurrencyOrdering(uint256,bool,bool) (runs: 1000, μ: 231769, ~: 238806)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 618.76ms (606.25ms CPU time)\n\nRan 1 test for test/Work.t.sol:WorkInvariantTest\n[PASS] invariant_FixedSupplyAndNoLostBalances() (runs: 256, calls: 12800, reverts: 0)\n\n╭---------------------+-------------------+-------+---------+----------╮\n| Contract            | Selector          | Calls | Reverts | Discards |\n+======================================================================+\n| WorkTransferHandler | delegatedTransfer | 6374  | 0       | 0        |\n|---------------------+-------------------+-------+---------+----------|\n| WorkTransferHandler | transfer          | 6426  | 0       | 0        |\n╰---------------------+-------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.06s (1.05s CPU time)\n\nRan 14 tests for test/WorkHook.t.sol:WorkHookTest\n[PASS] testFuzz_FeesAllSwapDirections(uint256,uint256,bool,bool) (runs: 1000, μ: 480462, ~: 487852)\n[PASS] testFuzz_PostMintHookSplit(uint256,uint256) (runs: 1000, μ: 2030379, ~: 2051618)\n[PASS] testFuzz_QuoteAndMinimumAtParity(uint256,uint256) (runs: 1000, μ: 312989, ~: 312556)\n[PASS] test_BuybackSettlesAndBurnsWorkWithFeesAndReentryProtection() (gas: 1745679)\n[PASS] test_BuybackSlippagePartialAndMissingSettlementRollback() (gas: 2864577)\n[PASS] test_ConstructorDeploysWorkersAndExactPermissionBits() (gas: 65508)\n[PASS] test_FeeTransferFailureRollsBackAndReentrantSweepIsRejected() (gas: 718101)\n[PASS] test_HookStraysGoOnlyToB() (gas: 270908)\n[PASS] test_InitializationAndCallbackAccessControl() (gas: 736366)\n[PASS] test_InvalidIntAmountsAndForeignPoolRevert() (gas: 547123)\n[PASS] test_OracleRingWrapKeepsThirtyMinutes() (gas: 148364027)\n[PASS] test_OwnerBoundsAndEvents() (gas: 569065)\n[PASS] test_PartialSpecifiedFillRevertsAtomicallyAndUnspecifiedChargesActual() (gas: 661929)\n[PASS] test_TwapWarmupWeightedTicksNegativeRoundingAndSameSecond() (gas: 643261)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 1.23s (2.23s CPU time)\n\nRan 3 tests for test/Work.t.sol:WorkTest\n[PASS] testFuzz_AllowanceAndTransfersConserveSupply(uint256,bool) (runs: 1000, μ: 235237, ~: 235390)\n[PASS] test_NoArgumentLaunchAndAllocation() (gas: 155917)\n[PASS] test_RevertsAreAtomicAndNoExtraMintOrUpgradeEntryPoints() (gas: 237227)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 1.24s (1.24s CPU time)\n\nRan 6 tests for test/WorkersNFT.t.sol:WorkersNFTBoundaryTest\n[PASS] testFuzz_BuybackMaximumBoundary(uint256) (runs: 1000, μ: 8257141, ~: 8256821)\n[PASS] testFuzz_EpochPricesFloorsAndScale(uint256,uint256,uint256) (runs: 1000, μ: 8081635, ~: 8085862)\n[PASS] test_ERC721TransferFailuresAreAtomicAndPlainTransferAllowed() (gas: 1822826)\n[PASS] test_PauseFreezesNewAccrualButAllowsExistingClaimAndActivationRollback() (gas: 2363630)\n[PASS] test_ReceiverReentrancyRejectedAndUnsafeMintAtomic() (gas: 2399121)\n[PASS] test_ZeroMintAndZeroCapEndPermanently() (gas: 479208)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 1.24s (1.53s CPU time)\n\nRan 24 tests for test/WorkersNFT.t.sol:WorkersNFTTest\n[PASS] testFuzz_AccrualFollowsTokenAndStaker(uint256) (runs: 1000, μ: 2534307, ~: 2527958)\n[PASS] testFuzz_BuybackBoundsCooldownAndRollback(uint256) (runs: 1000, μ: 1332458, ~: 1332823)\n[PASS] testFuzz_IdPickingUsesWholeRemainingPool(uint256) (runs: 1000, μ: 2995833, ~: 2957731)\n[PASS] testFuzz_MintSplitConservesIMD(uint256,uint256) (runs: 1000, μ: 1645231, ~: 1645275)\n[PASS] testFuzz_RetargetEightMintsAndIdleRecovery(uint256) (runs: 1000, μ: 4687347, ~: 4688376)\n[PASS] testFuzz_RoyaltyFullUintRange(uint256,uint256) (runs: 1000, μ: 45104, ~: 45267)\n[PASS] testFuzz_SweepCannotSpendAnyReserve(uint256) (runs: 1000, μ: 2797003, ~: 2797586)\n[PASS] testFuzz_WeightedClaimsConserveReserve(uint256) (runs: 1000, μ: 2004255, ~: 2004011)\n[PASS] testFuzz_WorkHashDomainAndThreshold(address,uint256,uint256) (runs: 1000, μ: 127359, ~: 127431)\n[PASS] test_ActivationPaymentOwnershipAndDuplicateClaims() (gas: 1536047)\n[PASS] test_AllAdminFunctionsRejectNonOwnerAndFundingOnlyB() (gas: 669300)\n[PASS] test_AllErasAndRemainderPreservesUnclaimedWork() (gas: 1745041)\n[PASS] test_BadStakeAndUnauthorizedRewards() (gas: 1125330)\n[PASS] test_BurnFixedPayoutAndStopsEarning() (gas: 2874046)\n[PASS] test_CarryWithoutActiveWorkerAndWhilePaused() (gas: 2220044)\n[PASS] test_EscrowClearsApprovalsAndExitNeedsNoReceiverOrIMDTransfer() (gas: 1229920)\n[PASS] test_L2BlockLimitAndProofCannotBeReplayedOrStolen() (gas: 1592986)\n[PASS] test_LateStakeCannotClaimPriorFeesAndUnstakeKeepsCredit() (gas: 2247086)\n[PASS] test_MetadataOverclockAndUpdates() (gas: 1751545)\n[PASS] test_NoActiveWorkersRemainderAndLateFunding() (gas: 1697787)\n[PASS] test_OwnerBoundsEventsAndPermanentCap() (gas: 1182712)\n[PASS] test_PauseScopeAndFullUnpausedCooldown() (gas: 1699628)\n[PASS] test_PaymentFailuresRollBackProofPoolAndBalances() (gas: 2014007)\n[PASS] test_WorkRejectsBadNonceStaleMissingAndFutureAnchors() (gas: 254569)\nSuite result: ok. 24 passed; 0 failed; 0 skipped; finished in 1.24s (9.66s CPU time)\n\nRan 4 tests for test/WorkHook.t.sol:WorkPriceMathTest\n[PASS] testFuzz_MulDivAgainstNonOverflowingProduct(uint128,uint128,uint128) (runs: 1000, μ: 1571, ~: 1572)\n[PASS] testFuzz_PricesMonotonicAcrossTickRange(int24) (runs: 1000, μ: 21779, ~: 21834)\n[PASS] test_FullWidthProductAndOverflow() (gas: 15859)\n[PASS] test_TickKnownVectorsAndBounds() (gas: 26459)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.24s (2.48s CPU time)\n\nRan 1 test for test/WorkHook.t.sol:WorkHookInvariantTest\n[PASS] invariant_FeesSettleWithoutStrandingOrSpendingReserves() (runs: 256, calls: 12800, reverts: 0)\n\n╭-----------------+-----------------+-------+---------+----------╮\n| Contract        | Selector        | Calls | Reverts | Discards |\n+================================================================+\n| WorkHookHandler | buyback         | 1594  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| WorkHookHandler | claim           | 1619  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| WorkHookHandler | donateAndSweep  | 1608  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| WorkHookHandler | endMint         | 1588  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| WorkHookHandler | ownerParameters | 1624  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| WorkHookHandler | stakeOrUnstake  | 1563  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| WorkHookHandler | timeAndOracle   | 1600  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| WorkHookHandler | trade           | 1604  | 0       | 0        |\n╰-----------------+-----------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.36s (2.33s CPU time)\n\nRan 1 test for test/WorkersNFT.t.sol:WorkersNFTInvariantTest\n[PASS]\nWorkersNFTInvariantTest invariants:\n[PASS] invariant_EscrowWeightsSupplyAndActivationAgree\n[PASS] invariant_ReservesCoverAllLiabilitiesAndConserveDeposits\n WorkersNFTInvariantTest invariants (runs: 256, calls: 12800, reverts: 0)\n\n╭----------------+------------------+-------+---------+----------╮\n| Contract       | Selector         | Calls | Reverts | Discards |\n+================================================================+\n| WorkersHandler | activateOrClaim  | 1107  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | burn             | 1060  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | buyback          | 1048  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | donateAndSweep   | 1070  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | endOrLowerCap    | 1146  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | feeAndClaim      | 1078  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | mint             | 1062  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | moveTime         | 1029  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | pauseOrUnpause   | 1035  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | releaseRemainder | 1018  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | stakeOrUnstake   | 1076  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | transfer         | 1071  | 0       | 0        |\n╰----------------+------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.36s (4.35s CPU time)\n\nRan 9 test suites in 4.36s (14.60s CPU time): 55 tests passed, 0 failed, 0 skipped (55 total tests)\n","passed":true},{"durationMs":36,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"Work.approve(address,uint256)\",\"Work.transfer(address,uint256)\",\"Work.transferFrom(address,address,uint256)\",\"WorkHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"WorkHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"WorkHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"WorkHook.executeBuyback(uint256)\",\"WorkHook.pokeOracle()\",\"WorkHook.setFeeBps(uint256)\",\"WorkHook.setStakerShareBps(uint256)\",\"WorkHook.sweep(address)\",\"WorkHook.unlockCallback(bytes)\",\"WorkersNFT.activate(uint256)\",\"WorkersNFT.approve(address,uint256)\",\"WorkersNFT.burn(uint256)\",\"WorkersNFT.buyback(uint256)\",\"WorkersNFT.claimIMD()\",\"WorkersNFT.claimWork(uint256[])\",\"WorkersNFT.endMint()\",\"WorkersNFT.fundPools()\",\"WorkersNFT.mint(uint256,uint256)\",\"WorkersNFT.notifyHookFees(uint256)\",\"WorkersNFT.pause()\",\"WorkersNFT.releaseMiningRemainder()\",\"WorkersNFT.safeTransferFrom(address,address,uint256)\",\"WorkersNFT.safeTransferFrom(address,address,uint256,bytes)\",\"WorkersNFT.setActivationFee(uint256)\",\"WorkersNFT.setApprovalForAll(address,bool)\",\"WorkersNFT.setBaseURI(string)\",\"WorkersNFT.setCap(uint256)\",\"WorkersNFT.setFloorAdjustment(int256)\",\"WorkersNFT.setPriceScaleBps(uint256)\",\"WorkersNFT.setRoyaltyBps(uint256)\",\"WorkersNFT.stake(uint256,uint256)\",\"WorkersNFT.sweep(address)\",\"WorkersNFT.transferFrom(address,address,uint256)\",\"WorkersNFT.unpause()\",\"WorkersNFT.unstake(uint256)\",\"WorkersNFT.updateMining()\"],\"files\":{\"launch.json\":33,\"src/Work.sol\":50,\"src/WorkHook.sol\":484,\"src/WorkersNFT.sol\":674,\"test/Work.t.sol\":180,\"test/WorkHook.t.sol\":722,\"test/WorkersNFT.t.sol\":1289},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"1779f5648fcb044fde3d717d500a34e0e5b9952cad5cc31dc90a2ae04cfcf2ef","verifiedTreeHash":"6d5b25139194ca9152b98c5b1864effca9bfd6a7","verifierVersion":"0.1.0+be003835"},{"checks":[{"durationMs":779,"exitCode":0,"name":"build","output":"Compiling 6 files with Solc 0.8.26\nSolc 0.8.26 finished in 689.49ms\nCompiler run successful!\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n   ╭▸ src/WorkersNFT.sol:20:17\n   │\n20 │         require(IWorkToken(token).balanceOf(address(this)) == beforeBalance + amount, \"Short transfer\");\n   │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\nwarning[missing-events-arithmetic]: `royaltyBps` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkersNFT.sol:289:9\n    │\n289 │         royaltyBps = bps;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `priceScaleBps` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkersNFT.sol:301:9\n    │\n301 │         priceScaleBps = bps;\n    │         ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `floorAdjustment` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkersNFT.sol:307:9\n    │\n307 │         floorAdjustment = adjustment;\n    │         ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `difficultyTarget` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkersNFT.sol:309:39\n    │\n309 │         if (difficultyTarget > limit) difficultyTarget = limit;\n    │                                       ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `feeBps` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkHook.sol:168:9\n    │\n168 │         feeBps = bps;\n    │         ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observationIndex` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkHook.sol:301:9\n    │\n301 │         observationIndex = (observationIndex + 1) % OBSERVATION_CAPACITY;\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observationCount` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkHook.sol:303:54\n    │\n303 │         if (observationCount < OBSERVATION_CAPACITY) observationCount++;\n    │                                                      ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-access-control]: `totalWeight` is changed without an event but is used for access control\n    ╭▸ src/WorkersNFT.sol:470:9\n    │\n470 │         totalWeight += weight;\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `_owners` is changed without an event but is used for access control\n    ╭▸ src/WorkersNFT.sol:243:9\n    │\n243 │         _owners[id] = to;\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `activated` is changed without an event but is used for access control\n    ╭▸ src/WorkersNFT.sol:633:13\n    │\n633 │             activated[id] = false;\n    │             ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `_owners` is changed without an event but is used for access control\n    ╭▸ src/WorkersNFT.sol:636:16\n    │\n636 │         delete _owners[id];\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/WorkersNFT.sol:172:17\n    │\n172 │     constructor(address token, address owner_, address hook_) {\n    │                 ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/WorkHook.sol:141:39\n    │\n141 │     constructor(IPoolManager manager, address token_, address owner_, int24 tickSpacing_) {\n    │                                       ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/WorkersNFT.sol:265:13\n    │\n265 │             digits++;\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkersNFT.sol:269:39\n    │\n269 │             result[--digits] = bytes1(uint8(48 + n % 10));\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[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/WorkersNFT.sol:277:20\n    │\n277 │         return (B, salePrice / 10_000 * royaltyBps + salePrice % 10_000 * royaltyBps / 10_000);\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:202:39\n    │\n202 │         observations[0] = Observation(uint64(block.timestamp), 0, tick);\n    │                                       ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/WorkHook.sol:225:73\n    │\n225 │         return (IHooks.beforeSwap.selector, BeforeSwapDelta.wrap(int256(fee << 128)), 0);\n    │                                                                         ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/WorkHook.sol:213:9\n    │\n213 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:220:34\n    │\n220 │             require(specified <= uint256(uint128(type(int128).max)), \"Swap too large\");\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/WorkHook.sol:220:42\n    │\n220 │             require(specified <= uint256(uint128(type(int128).max)), \"Swap too large\");\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/WorkHook.sol:222:40\n    │\n222 │             require(specified + fee <= uint256(uint128(type(int128).max)), \"Delta too large\");\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/WorkHook.sol:222:48\n    │\n222 │             require(specified + fee <= uint256(uint128(type(int128).max)), \"Delta too large\");\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/WorkHook.sol:225:66\n    │\n225 │         return (IHooks.beforeSwap.selector, BeforeSwapDelta.wrap(int256(fee << 128)), 0);\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[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/WorkHook.sol:231:9\n    │\n231 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:248:44\n    │\n248 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\n    │                                            ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:248:51\n    │\n248 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\n    │                                                   ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:257:33\n    │\n257 │         return imdIsCurrency0 ? int128(BalanceDelta.unwrap(delta) >> 128) : int128(BalanceDelta.unwrap(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/WorkHook.sol:257:77\n    │\n257 │         return imdIsCurrency0 ? int128(BalanceDelta.unwrap(delta) >> 128) : int128(BalanceDelta.unwrap(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/WorkHook.sol:261:33\n    │\n261 │         return imdIsCurrency0 ? int128(BalanceDelta.unwrap(delta)) : int128(BalanceDelta.unwrap(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/WorkHook.sol:261:70\n    │\n261 │         return imdIsCurrency0 ? int128(BalanceDelta.unwrap(delta)) : int128(BalanceDelta.unwrap(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/WorkHook.sol:266:16\n    │\n266 │         return uint256(value < 0 ? -value : value);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/WorkHook.sol:285:9\n    │\n285 │         emit FeeDistributed(amount, stakerPart, amount - stakerPart);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/WorkHook.sol:280:13\n    │\n280 │         if (stakerPart != 0) {\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/WorkHook.sol:295:13\n    │\n295 │         if (previous.timestamp == block.timestamp) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:300:58\n    │\n300 │             previous.cumulative + int96(previous.tick) * int96(uint96(block.timestamp - previous.timestamp));\n    │                                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int96' 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/WorkHook.sol:300:64\n    │\n300 │             previous.cumulative + int96(previous.tick) * int96(uint96(block.timestamp - previous.timestamp));\n    │                                                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint96' 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/WorkHook.sol:302:54\n    │\n302 │         observations[observationIndex] = Observation(uint64(block.timestamp), cumulative, tick);\n    │                                                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/WorkHook.sol:322:16\n    │\n322 │         while (low < high) {\n    │                ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:328:64\n    │\n328 │         return int256(point.cumulative) + int256(point.tick) * int256(timestamp - point.timestamp);\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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/WorkHook.sol:332:32\n    │\n332 │         require(initialized && block.timestamp >= TWAP_WINDOW, \"TWAP unavailable\");\n    │                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:334:39\n    │\n334 │         int256 average = difference / int256(TWAP_WINDOW);\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/WorkHook.sol:335:44\n    │\n335 │         if (difference < 0 && difference % int256(TWAP_WINDOW) != 0) average--;\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/WorkHook.sol:336:16\n    │\n336 │         return int24(average);\n    │                ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' 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 `observations` is updated\n    ╭▸ src/WorkHook.sol:282:13\n    │\n282 │             workers.notifyHookFees(stakerPart);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_entered` is updated\n    ╭▸ src/WorkHook.sol:376:30\n    │\n376 │         workOut = abi.decode(poolManager.unlock(\"\"), (uint256));\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/WorkHook.sol:379:9\n    │\n379 │         emit BuybackExecuted(amount, workOut, minimum);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:390:35\n    │\n390 │                 amountSpecified: -int256(input),\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/WorkHook.sol:397:26\n    │\n397 │         require(paid == -int256(input) && received > 0, \"Incomplete buyback\");\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/WorkHook.sol:398:26\n    │\n398 │         uint256 output = uint256(uint128(received));\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/WorkHook.sol:398:34\n    │\n398 │         uint256 output = uint256(uint128(received));\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/WorkHook.sol:408:20\n    │\n408 │     function sweep(address asset) external nonReentrant returns (uint256 amount) {\n    │                    ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:422:21\n    │\n422 │         uint256 n = uint256(tick < 0 ? -int256(tick) : int256(tick));\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/WorkHook.sol:431:16\n    │\n431 │         return uint160((ratio >> 32) + (ratio & 0xffffffff == 0 ? 0 : 1));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' 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/WorkersNFT.sol:334:16\n    │\n334 │         return block.timestamp < pausedUntil;\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/WorkersNFT.sol:338:30\n    │\n338 │         require(!paused() && block.timestamp >= nextPauseAt, \"Pause cooldown\");\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkersNFT.sol:371:16\n    │\n371 │         return uint256(int256(BASE_FLOOR_BITS + epoch()) + floorAdjustment);\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/WorkersNFT.sol:371:24\n    │\n371 │         return uint256(int256(BASE_FLOOR_BITS + epoch()) + floorAdjustment);\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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/WorkersNFT.sol:382:13\n    │\n382 │         if (shifts >= 256 || difficultyTarget > (limit >> shifts)) return limit;\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/WorkersNFT.sol:382:30\n    │\n382 │         if (shifts >= 256 || difficultyTarget > (limit >> shifts)) return limit;\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/WorkersNFT.sol:384:16\n    │\n384 │         return value > limit ? limit : value;\n    │                ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_entered` is updated\n    ╭▸ src/WorkersNFT.sol:250:17\n    │\n250 │                 IWorkersReceiver(to).onERC721Received(msg.sender, from, id, data)\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/WorkersNFT.sol:434:32\n    │\n434 │             uint256 adjusted = target / 80 * bounded + target % 80 * bounded / 80;\n    │                                ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/WorkersNFT.sol:433:31\n    │\n433 │             uint256 bounded = elapsed < 20 ? 20 : (elapsed > 320 ? 320 : elapsed);\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/WorkersNFT.sol:433:52\n    │\n433 │             uint256 bounded = elapsed < 20 ? 20 : (elapsed > 320 ? 320 : elapsed);\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/WorkersNFT.sol:435:32\n    │\n435 │             difficultyTarget = adjusted == 0 ? 1 : adjusted;\n    │                                ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkersNFT.sol:472:40\n    │\n472 │         stakes[id] = Stake(msg.sender, uint64(unlockTime), weight);\n    │                                        ━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/WorkersNFT.sol:479:50\n    │\n479 │         require(position.staker == msg.sender && block.timestamp >= position.unlockTime, \"Locked or not staker\");\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_entered` is updated\n    ╭▸ src/WorkersNFT.sol:528:19\n    │\n528 │         workOut = IWorkBuyback(hook).executeBuyback(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/WorkersNFT.sol:523:35\n    │\n523 │         require(!hasBoughtBack || block.timestamp >= lastBuyback + BUYBACK_INTERVAL, \"Buyback cooldown\");\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/WorkersNFT.sol:529:9\n    │\n529 │         emit Buyback(amount, workOut);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/WorkersNFT.sol:583:35\n    │\n583 │             uint256 distributed = increment * activeWorkers;\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/WorkersNFT.sol:194:9\n    │\n194 │         require(account != address(0), \"Unknown worker\");\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/WorkersNFT.sol:605:13\n    │\n605 │             require(holderOf(id) == msg.sender, \"Not holder or staker\");\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/WorkersNFT.sol:649:58\n    │\n649 │     function releaseMiningRemainder() external onlyOwner nonReentrant returns (uint256 amount) {\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/WorkersNFT.sol:650:30\n    │\n650 │         require(mintEnded && block.timestamp >= mintEndTime + MINING_DURATION && !paused(), \"Schedule not finished\");\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/WorkersNFT.sol:666:26\n    │\n666 │         return balance > reserved ? balance - reserved : 0;\n    │                          ━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/WorkersNFT.sol:669:20\n    │\n669 │     function sweep(address token) external nonReentrant returns (uint256 amount) {\n    │                    ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\n","passed":true},{"durationMs":3884,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/WorkHook.t.sol:WorkHookReverseOrderTest\n[PASS] testFuzz_ReverseCurrencyOrdering(uint256,bool,bool) (runs: 1000, μ: 231483, ~: 238806)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 300.03ms (289.31ms CPU time)\n\nRan 3 tests for test/Work.t.sol:WorkTest\n[PASS] testFuzz_AllowanceAndTransfersConserveSupply(uint256,bool) (runs: 1000, μ: 235119, ~: 235136)\n[PASS] test_NoArgumentLaunchAndAllocation() (gas: 155917)\n[PASS] test_RevertsAreAtomicAndNoExtraMintOrUpgradeEntryPoints() (gas: 237227)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 853.28ms (853.22ms CPU time)\n\nRan 6 tests for test/WorkersNFT.t.sol:WorkersNFTBoundaryTest\n[PASS] testFuzz_BuybackMaximumBoundary(uint256) (runs: 1000, μ: 8257141, ~: 8256821)\n[PASS] testFuzz_EpochPricesFloorsAndScale(uint256,uint256,uint256) (runs: 1000, μ: 8081707, ~: 8085862)\n[PASS] test_ERC721TransferFailuresAreAtomicAndPlainTransferAllowed() (gas: 1822826)\n[PASS] test_PauseFreezesNewAccrualButAllowsExistingClaimAndActivationRollback() (gas: 2363630)\n[PASS] test_ReceiverReentrancyRejectedAndUnsafeMintAtomic() (gas: 2399121)\n[PASS] test_ZeroMintAndZeroCapEndPermanently() (gas: 479208)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 868.07ms (1.71s CPU time)\n\nRan 1 test for test/Work.t.sol:WorkInvariantTest\n[PASS] invariant_FixedSupplyAndNoLostBalances() (runs: 256, calls: 12800, reverts: 0)\n\n╭---------------------+-------------------+-------+---------+----------╮\n| Contract            | Selector          | Calls | Reverts | Discards |\n+======================================================================+\n| WorkTransferHandler | delegatedTransfer | 6266  | 0       | 0        |\n|---------------------+-------------------+-------+---------+----------|\n| WorkTransferHandler | transfer          | 6534  | 0       | 0        |\n╰---------------------+-------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.03s (1.03s CPU time)\n\nRan 14 tests for test/WorkHook.t.sol:WorkHookTest\n[PASS] testFuzz_FeesAllSwapDirections(uint256,uint256,bool,bool) (runs: 1000, μ: 479718, ~: 487864)\n[PASS] testFuzz_PostMintHookSplit(uint256,uint256) (runs: 1000, μ: 2024662, ~: 2051595)\n[PASS] testFuzz_QuoteAndMinimumAtParity(uint256,uint256) (runs: 1000, μ: 312990, ~: 312556)\n[PASS] test_BuybackSettlesAndBurnsWorkWithFeesAndReentryProtection() (gas: 1745679)\n[PASS] test_BuybackSlippagePartialAndMissingSettlementRollback() (gas: 2864577)\n[PASS] test_ConstructorDeploysWorkersAndExactPermissionBits() (gas: 65508)\n[PASS] test_FeeTransferFailureRollsBackAndReentrantSweepIsRejected() (gas: 718101)\n[PASS] test_HookStraysGoOnlyToB() (gas: 270908)\n[PASS] test_InitializationAndCallbackAccessControl() (gas: 736366)\n[PASS] test_InvalidIntAmountsAndForeignPoolRevert() (gas: 547123)\n[PASS] test_OracleRingWrapKeepsThirtyMinutes() (gas: 148364027)\n[PASS] test_OwnerBoundsAndEvents() (gas: 569065)\n[PASS] test_PartialSpecifiedFillRevertsAtomicallyAndUnspecifiedChargesActual() (gas: 661929)\n[PASS] test_TwapWarmupWeightedTicksNegativeRoundingAndSameSecond() (gas: 643261)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 1.05s (1.48s CPU time)\n\nRan 4 tests for test/WorkHook.t.sol:WorkPriceMathTest\n[PASS] testFuzz_MulDivAgainstNonOverflowingProduct(uint128,uint128,uint128) (runs: 1000, μ: 1571, ~: 1572)\n[PASS] testFuzz_PricesMonotonicAcrossTickRange(int24) (runs: 1000, μ: 21835, ~: 21834)\n[PASS] test_FullWidthProductAndOverflow() (gas: 15859)\n[PASS] test_TickKnownVectorsAndBounds() (gas: 26459)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.05s (2.10s CPU time)\n\nRan 24 tests for test/WorkersNFT.t.sol:WorkersNFTTest\n[PASS] testFuzz_AccrualFollowsTokenAndStaker(uint256) (runs: 1000, μ: 2534019, ~: 2527958)\n[PASS] testFuzz_BuybackBoundsCooldownAndRollback(uint256) (runs: 1000, μ: 1332454, ~: 1332823)\n[PASS] testFuzz_IdPickingUsesWholeRemainingPool(uint256) (runs: 1000, μ: 2958610, ~: 2923139)\n[PASS] testFuzz_MintSplitConservesIMD(uint256,uint256) (runs: 1000, μ: 1645232, ~: 1645275)\n[PASS] testFuzz_RetargetEightMintsAndIdleRecovery(uint256) (runs: 1000, μ: 4687444, ~: 4688376)\n[PASS] testFuzz_RoyaltyFullUintRange(uint256,uint256) (runs: 1000, μ: 45084, ~: 45267)\n[PASS] testFuzz_SweepCannotSpendAnyReserve(uint256) (runs: 1000, μ: 2796929, ~: 2797586)\n[PASS] testFuzz_WeightedClaimsConserveReserve(uint256) (runs: 1000, μ: 2004268, ~: 2004011)\n[PASS] testFuzz_WorkHashDomainAndThreshold(address,uint256,uint256) (runs: 1000, μ: 127359, ~: 127431)\n[PASS] test_ActivationPaymentOwnershipAndDuplicateClaims() (gas: 1536047)\n[PASS] test_AllAdminFunctionsRejectNonOwnerAndFundingOnlyB() (gas: 669300)\n[PASS] test_AllErasAndRemainderPreservesUnclaimedWork() (gas: 1745041)\n[PASS] test_BadStakeAndUnauthorizedRewards() (gas: 1125330)\n[PASS] test_BurnFixedPayoutAndStopsEarning() (gas: 2874046)\n[PASS] test_CarryWithoutActiveWorkerAndWhilePaused() (gas: 2220044)\n[PASS] test_EscrowClearsApprovalsAndExitNeedsNoReceiverOrIMDTransfer() (gas: 1229920)\n[PASS] test_L2BlockLimitAndProofCannotBeReplayedOrStolen() (gas: 1592986)\n[PASS] test_LateStakeCannotClaimPriorFeesAndUnstakeKeepsCredit() (gas: 2247086)\n[PASS] test_MetadataOverclockAndUpdates() (gas: 1751545)\n[PASS] test_NoActiveWorkersRemainderAndLateFunding() (gas: 1697787)\n[PASS] test_OwnerBoundsEventsAndPermanentCap() (gas: 1182712)\n[PASS] test_PauseScopeAndFullUnpausedCooldown() (gas: 1699628)\n[PASS] test_PaymentFailuresRollBackProofPoolAndBalances() (gas: 2014007)\n[PASS] test_WorkRejectsBadNonceStaleMissingAndFutureAnchors() (gas: 254569)\nSuite result: ok. 24 passed; 0 failed; 0 skipped; finished in 1.05s (7.14s CPU time)\n\nRan 1 test for test/WorkHook.t.sol:WorkHookInvariantTest\n[PASS] invariant_FeesSettleWithoutStrandingOrSpendingReserves() (runs: 256, calls: 12800, reverts: 0)\n\n╭-----------------+-----------------+-------+---------+----------╮\n| Contract        | Selector        | Calls | Reverts | Discards |\n+================================================================+\n| WorkHookHandler | buyback         | 1587  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| WorkHookHandler | claim           | 1664  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| WorkHookHandler | donateAndSweep  | 1600  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| WorkHookHandler | endMint         | 1573  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| WorkHookHandler | ownerParameters | 1561  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| WorkHookHandler | stakeOrUnstake  | 1616  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| WorkHookHandler | timeAndOracle   | 1603  | 0       | 0        |\n|-----------------+-----------------+-------+---------+----------|\n| WorkHookHandler | trade           | 1596  | 0       | 0        |\n╰-----------------+-----------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.09s (2.05s CPU time)\n\nRan 1 test for test/WorkersNFT.t.sol:WorkersNFTInvariantTest\n[PASS]\nWorkersNFTInvariantTest invariants:\n[PASS] invariant_EscrowWeightsSupplyAndActivationAgree\n[PASS] invariant_ReservesCoverAllLiabilitiesAndConserveDeposits\n WorkersNFTInvariantTest invariants (runs: 256, calls: 12800, reverts: 0)\n\n╭----------------+------------------+-------+---------+----------╮\n| Contract       | Selector         | Calls | Reverts | Discards |\n+================================================================+\n| WorkersHandler | activateOrClaim  | 1083  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | burn             | 1104  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | buyback          | 1035  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | donateAndSweep   | 1071  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | endOrLowerCap    | 1060  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | feeAndClaim      | 1078  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | mint             | 1106  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | moveTime         | 1052  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | pauseOrUnpause   | 1079  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | releaseRemainder | 1085  | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | stakeOrUnstake   | 983   | 0       | 0        |\n|----------------+------------------+-------+---------+----------|\n| WorkersHandler | transfer         | 1064  | 0       | 0        |\n╰----------------+------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.82s (3.82s CPU time)\n\nRan 9 test suites in 3.83s (12.12s CPU time): 55 tests passed, 0 failed, 0 skipped (55 total tests)\n","passed":true},{"durationMs":37,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"Work.approve(address,uint256)\",\"Work.transfer(address,uint256)\",\"Work.transferFrom(address,address,uint256)\",\"WorkHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"WorkHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"WorkHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"WorkHook.executeBuyback(uint256)\",\"WorkHook.pokeOracle()\",\"WorkHook.setFeeBps(uint256)\",\"WorkHook.setStakerShareBps(uint256)\",\"WorkHook.sweep(address)\",\"WorkHook.unlockCallback(bytes)\",\"WorkersNFT.activate(uint256)\",\"WorkersNFT.approve(address,uint256)\",\"WorkersNFT.burn(uint256)\",\"WorkersNFT.buyback(uint256)\",\"WorkersNFT.claimIMD()\",\"WorkersNFT.claimWork(uint256[])\",\"WorkersNFT.endMint()\",\"WorkersNFT.fundPools()\",\"WorkersNFT.mint(uint256,uint256)\",\"WorkersNFT.notifyHookFees(uint256)\",\"WorkersNFT.pause()\",\"WorkersNFT.releaseMiningRemainder()\",\"WorkersNFT.safeTransferFrom(address,address,uint256)\",\"WorkersNFT.safeTransferFrom(address,address,uint256,bytes)\",\"WorkersNFT.setActivationFee(uint256)\",\"WorkersNFT.setApprovalForAll(address,bool)\",\"WorkersNFT.setBaseURI(string)\",\"WorkersNFT.setCap(uint256)\",\"WorkersNFT.setFloorAdjustment(int256)\",\"WorkersNFT.setPriceScaleBps(uint256)\",\"WorkersNFT.setRoyaltyBps(uint256)\",\"WorkersNFT.stake(uint256,uint256)\",\"WorkersNFT.sweep(address)\",\"WorkersNFT.transferFrom(address,address,uint256)\",\"WorkersNFT.unpause()\",\"WorkersNFT.unstake(uint256)\",\"WorkersNFT.updateMining()\"],\"files\":{\"src/Work.sol\":50,\"src/WorkHook.sol\":484,\"src/WorkersNFT.sol\":674,\"test/Work.t.sol\":180,\"test/WorkHook.t.sol\":722,\"test/WorkersNFT.t.sol\":1289},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"8a4d56ddb4b3d6320b5eb52be5e5bca4a9740bd102a0dc995c35b389d3f93920","verifiedTreeHash":"6ee5f4f71ff4068eca0ae3d6108ffe36d3d0bcae","verifierVersion":"0.1.0+be003835"},{"checks":[{"durationMs":232,"exitCode":0,"name":"build","output":"Compiling 3 files with Solc 0.8.26\nSolc 0.8.26 finished in 151.67ms\nCompiler run successful!\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n   ╭▸ src/WorkersNFT.sol:20:17\n   │\n20 │         require(IWorkToken(token).balanceOf(address(this)) == beforeBalance + amount, \"Short transfer\");\n   │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\nwarning[missing-events-arithmetic]: `feeBps` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkHook.sol:168:9\n    │\n168 │         feeBps = bps;\n    │         ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observationIndex` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkHook.sol:301:9\n    │\n301 │         observationIndex = (observationIndex + 1) % OBSERVATION_CAPACITY;\n    │         ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observationCount` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkHook.sol:303:54\n    │\n303 │         if (observationCount < OBSERVATION_CAPACITY) observationCount++;\n    │                                                      ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `royaltyBps` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkersNFT.sol:289:9\n    │\n289 │         royaltyBps = bps;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `priceScaleBps` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkersNFT.sol:301:9\n    │\n301 │         priceScaleBps = bps;\n    │         ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `floorAdjustment` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkersNFT.sol:307:9\n    │\n307 │         floorAdjustment = adjustment;\n    │         ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `difficultyTarget` is changed without an event but is used in arithmetic\n    ╭▸ src/WorkersNFT.sol:309:39\n    │\n309 │         if (difficultyTarget > limit) difficultyTarget = limit;\n    │                                       ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-access-control]: `totalWeight` is changed without an event but is used for access control\n    ╭▸ src/WorkersNFT.sol:470:9\n    │\n470 │         totalWeight += weight;\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `_owners` is changed without an event but is used for access control\n    ╭▸ src/WorkersNFT.sol:243:9\n    │\n243 │         _owners[id] = to;\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `activated` is changed without an event but is used for access control\n    ╭▸ src/WorkersNFT.sol:633:13\n    │\n633 │             activated[id] = false;\n    │             ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `_owners` is changed without an event but is used for access control\n    ╭▸ src/WorkersNFT.sol:636:16\n    │\n636 │         delete _owners[id];\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/WorkHook.sol:141:39\n    │\n141 │     constructor(IPoolManager manager, address token_, address owner_, int24 tickSpacing_) {\n    │                                       ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/WorkersNFT.sol:172:17\n    │\n172 │     constructor(address token, address owner_, address hook_) {\n    │                 ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/WorkersNFT.sol:265:13\n    │\n265 │             digits++;\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkersNFT.sol:269:39\n    │\n269 │             result[--digits] = bytes1(uint8(48 + n % 10));\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[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/WorkersNFT.sol:277:20\n    │\n277 │         return (B, salePrice / 10_000 * royaltyBps + salePrice % 10_000 * royaltyBps / 10_000);\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:202:39\n    │\n202 │         observations[0] = Observation(uint64(block.timestamp), 0, tick);\n    │                                       ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/WorkHook.sol:225:73\n    │\n225 │         return (IHooks.beforeSwap.selector, BeforeSwapDelta.wrap(int256(fee << 128)), 0);\n    │                                                                         ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/WorkHook.sol:213:9\n    │\n213 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:220:34\n    │\n220 │             require(specified <= uint256(uint128(type(int128).max)), \"Swap too large\");\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/WorkHook.sol:220:42\n    │\n220 │             require(specified <= uint256(uint128(type(int128).max)), \"Swap too large\");\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/WorkHook.sol:222:40\n    │\n222 │             require(specified + fee <= uint256(uint128(type(int128).max)), \"Delta too large\");\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/WorkHook.sol:222:48\n    │\n222 │             require(specified + fee <= uint256(uint128(type(int128).max)), \"Delta too large\");\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/WorkHook.sol:225:66\n    │\n225 │         return (IHooks.beforeSwap.selector, BeforeSwapDelta.wrap(int256(fee << 128)), 0);\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[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/WorkHook.sol:231:9\n    │\n231 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:248:44\n    │\n248 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\n    │                                            ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:248:51\n    │\n248 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\n    │                                                   ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:257:33\n    │\n257 │         return imdIsCurrency0 ? int128(BalanceDelta.unwrap(delta) >> 128) : int128(BalanceDelta.unwrap(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/WorkHook.sol:257:77\n    │\n257 │         return imdIsCurrency0 ? int128(BalanceDelta.unwrap(delta) >> 128) : int128(BalanceDelta.unwrap(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/WorkHook.sol:261:33\n    │\n261 │         return imdIsCurrency0 ? int128(BalanceDelta.unwrap(delta)) : int128(BalanceDelta.unwrap(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/WorkHook.sol:261:70\n    │\n261 │         return imdIsCurrency0 ? int128(BalanceDelta.unwrap(delta)) : int128(BalanceDelta.unwrap(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/WorkHook.sol:266:16\n    │\n266 │         return uint256(value < 0 ? -value : value);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/WorkHook.sol:285:9\n    │\n285 │         emit FeeDistributed(amount, stakerPart, amount - stakerPart);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/WorkHook.sol:280:13\n    │\n280 │         if (stakerPart != 0) {\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/WorkHook.sol:295:13\n    │\n295 │         if (previous.timestamp == block.timestamp) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:300:58\n    │\n300 │             previous.cumulative + int96(previous.tick) * int96(uint96(block.timestamp - previous.timestamp));\n    │                                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int96' 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/WorkHook.sol:300:64\n    │\n300 │             previous.cumulative + int96(previous.tick) * int96(uint96(block.timestamp - previous.timestamp));\n    │                                                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint96' 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/WorkHook.sol:302:54\n    │\n302 │         observations[observationIndex] = Observation(uint64(block.timestamp), cumulative, tick);\n    │                                                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/WorkHook.sol:322:16\n    │\n322 │         while (low < high) {\n    │                ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:328:64\n    │\n328 │         return int256(point.cumulative) + int256(point.tick) * int256(timestamp - point.timestamp);\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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/WorkHook.sol:332:32\n    │\n332 │         require(initialized && block.timestamp >= TWAP_WINDOW, \"TWAP unavailable\");\n    │                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:334:39\n    │\n334 │         int256 average = difference / int256(TWAP_WINDOW);\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/WorkHook.sol:335:44\n    │\n335 │         if (difference < 0 && difference % int256(TWAP_WINDOW) != 0) average--;\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/WorkHook.sol:336:16\n    │\n336 │         return int24(average);\n    │                ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' 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 `observations` is updated\n    ╭▸ src/WorkHook.sol:282:13\n    │\n282 │             workers.notifyHookFees(stakerPart);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_entered` is updated\n    ╭▸ src/WorkHook.sol:376:30\n    │\n376 │         workOut = abi.decode(poolManager.unlock(\"\"), (uint256));\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/WorkHook.sol:379:9\n    │\n379 │         emit BuybackExecuted(amount, workOut, minimum);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:390:35\n    │\n390 │                 amountSpecified: -int256(input),\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/WorkHook.sol:397:26\n    │\n397 │         require(paid == -int256(input) && received > 0, \"Incomplete buyback\");\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/WorkHook.sol:398:26\n    │\n398 │         uint256 output = uint256(uint128(received));\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/WorkHook.sol:398:34\n    │\n398 │         uint256 output = uint256(uint128(received));\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/WorkHook.sol:408:20\n    │\n408 │     function sweep(address asset) external nonReentrant returns (uint256 amount) {\n    │                    ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkHook.sol:422:21\n    │\n422 │         uint256 n = uint256(tick < 0 ? -int256(tick) : int256(tick));\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/WorkHook.sol:431:16\n    │\n431 │         return uint160((ratio >> 32) + (ratio & 0xffffffff == 0 ? 0 : 1));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' 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/WorkersNFT.sol:334:16\n    │\n334 │         return block.timestamp < pausedUntil;\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/WorkersNFT.sol:338:30\n    │\n338 │         require(!paused() && block.timestamp >= nextPauseAt, \"Pause cooldown\");\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkersNFT.sol:371:16\n    │\n371 │         return uint256(int256(BASE_FLOOR_BITS + epoch()) + floorAdjustment);\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/WorkersNFT.sol:371:24\n    │\n371 │         return uint256(int256(BASE_FLOOR_BITS + epoch()) + floorAdjustment);\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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/WorkersNFT.sol:382:13\n    │\n382 │         if (shifts >= 256 || difficultyTarget > (limit >> shifts)) return limit;\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/WorkersNFT.sol:382:30\n    │\n382 │         if (shifts >= 256 || difficultyTarget > (limit >> shifts)) return limit;\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/WorkersNFT.sol:384:16\n    │\n384 │         return value > limit ? limit : value;\n    │                ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_entered` is updated\n    ╭▸ src/WorkersNFT.sol:250:17\n    │\n250 │                 IWorkersReceiver(to).onERC721Received(msg.sender, from, id, data)\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/WorkersNFT.sol:434:32\n    │\n434 │             uint256 adjusted = target / 80 * bounded + target % 80 * bounded / 80;\n    │                                ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/WorkersNFT.sol:433:31\n    │\n433 │             uint256 bounded = elapsed < 20 ? 20 : (elapsed > 320 ? 320 : elapsed);\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/WorkersNFT.sol:433:52\n    │\n433 │             uint256 bounded = elapsed < 20 ? 20 : (elapsed > 320 ? 320 : elapsed);\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/WorkersNFT.sol:435:32\n    │\n435 │             difficultyTarget = adjusted == 0 ? 1 : adjusted;\n    │                                ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/WorkersNFT.sol:472:40\n    │\n472 │         stakes[id] = Stake(msg.sender, uint64(unlockTime), weight);\n    │                                        ━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/WorkersNFT.sol:479:50\n    │\n479 │         require(position.staker == msg.sender && block.timestamp >= position.unlockTime, \"Locked or not staker\");\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_entered` is updated\n    ╭▸ src/WorkersNFT.sol:528:19\n    │\n528 │         workOut = IWorkBuyback(hook).executeBuyback(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/WorkersNFT.sol:523:35\n    │\n523 │         require(!hasBoughtBack || block.timestamp >= lastBuyback + BUYBACK_INTERVAL, \"Buyback cooldown\");\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/WorkersNFT.sol:529:9\n    │\n529 │         emit Buyback(amount, workOut);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/WorkersNFT.sol:583:35\n    │\n583 │             uint256 distributed = increment * activeWorkers;\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/WorkersNFT.sol:194:9\n    │\n194 │         require(account != address(0), \"Unknown worker\");\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/WorkersNFT.sol:605:13\n    │\n605 │             require(holderOf(id) == msg.sender, \"Not holder or staker\");\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/WorkersNFT.sol:649:58\n    │\n649 │     function releaseMiningRemainder() external onlyOwner nonReentrant returns (uint256 amount) {\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/WorkersNFT.sol:650:30\n    │\n650 │         require(mintEnded && block.timestamp >= mintEndTime + MINING_DURATION && !paused(), \"Schedule not finished\");\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/WorkersNFT.sol:666:26\n    │\n666 │         return balance > reserved ? balance - reserved : 0;\n    │                          ━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/WorkersNFT.sol:669:20\n    │\n669 │     function sweep(address token) external nonReentrant returns (uint256 amount) {\n    │                    ━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\n","passed":true},{"durationMs":47,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\nWarning: No tests found in project! Forge looks for functions that start with `test`\n","passed":true},{"durationMs":22,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"Work.approve(address,uint256)\",\"Work.transfer(address,uint256)\",\"Work.transferFrom(address,address,uint256)\",\"WorkHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"WorkHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"WorkHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"WorkHook.executeBuyback(uint256)\",\"WorkHook.pokeOracle()\",\"WorkHook.setFeeBps(uint256)\",\"WorkHook.setStakerShareBps(uint256)\",\"WorkHook.sweep(address)\",\"WorkHook.unlockCallback(bytes)\",\"WorkersNFT.activate(uint256)\",\"WorkersNFT.approve(address,uint256)\",\"WorkersNFT.burn(uint256)\",\"WorkersNFT.buyback(uint256)\",\"WorkersNFT.claimIMD()\",\"WorkersNFT.claimWork(uint256[])\",\"WorkersNFT.endMint()\",\"WorkersNFT.fundPools()\",\"WorkersNFT.mint(uint256,uint256)\",\"WorkersNFT.notifyHookFees(uint256)\",\"WorkersNFT.pause()\",\"WorkersNFT.releaseMiningRemainder()\",\"WorkersNFT.safeTransferFrom(address,address,uint256)\",\"WorkersNFT.safeTransferFrom(address,address,uint256,bytes)\",\"WorkersNFT.setActivationFee(uint256)\",\"WorkersNFT.setApprovalForAll(address,bool)\",\"WorkersNFT.setBaseURI(string)\",\"WorkersNFT.setCap(uint256)\",\"WorkersNFT.setFloorAdjustment(int256)\",\"WorkersNFT.setPriceScaleBps(uint256)\",\"WorkersNFT.setRoyaltyBps(uint256)\",\"WorkersNFT.stake(uint256,uint256)\",\"WorkersNFT.sweep(address)\",\"WorkersNFT.transferFrom(address,address,uint256)\",\"WorkersNFT.unpause()\",\"WorkersNFT.unstake(uint256)\",\"WorkersNFT.updateMining()\"],\"files\":{\"src/Work.sol\":50,\"src/WorkHook.sol\":484,\"src/WorkersNFT.sol\":674},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":177,"exitCode":0,"name":"slither","output":"slither: no results at low impact or above","passed":true},{"durationMs":453,"exitCode":0,"name":"aderyn","output":"[high] eth-send-unchecked-address at src/WorkHook.sol:408: ETH transferred without address checks (4 places)\n[high] incorrect-caret-operator at src/WorkHook.sol:474: Incorrect use of caret operator\n[high] reentrancy-state-change at src/WorkHook.sol:368: Reentrancy: State change after external call (3 places)\n[high] unchecked-send at src/WorkHook.sol:281: Unchecked `bool success` value for ETH send (12 places)\n[high] unsafe-casting at src/WorkHook.sol:336: Unsafe Casting of integers (2 places)\n[low] centralization-risk at src/WorkHook.sol:165: Centralization Risk (12 places)\n[low] costly-loop at src/WorkersNFT.sol:603: Costly operations inside loop\n[low] division-before-multiplication at src/WorkersNFT.sol:277: Incorrect Order of Division and Multiplication (2 places)\n[low] large-numeric-literal at src/Work.sol:9: Large Numeric Literal (22 places)\n[low] literal-instead-of-constant at src/WorkHook.sol:172: Literal Instead of Constant (58 places)\n[low] missing-inheritance at src/Work.sol:5: Missing Inheritance\n[low] non-reentrant-not-first at src/WorkHook.sol:213: `nonReentrant` is Not the First Modifier (3 places)\n[low] require-revert-in-loop at src/WorkersNFT.sol:603: Loop Contains `require`/`revert`\n[low] state-change-without-event at src/WorkHook.sol:307: State Change Without Event (2 places)\n[low] unchecked-return at src/WorkersNFT.sol:202: Unchecked Return (3 places)\n[low] uninitialized-local-variable at src/WorkersNFT.sol:563: Uninitialized Local Variable (2 places)\n[low] unsafe-erc20-operation at src/WorkersNFT.sol:13: Unsafe ERC20 Operation","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"abf9e6c7c974989b0f0618b2ce009efd0910bc56c0c10d10b7443384035ac8e5","verifiedTreeHash":"0db4e0fcffaadbf46190324d6fad1bd1964b56ec","verifierVersion":"0.1.0+be003835"}]}