{"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":"05e9d60a-4559-4724-bdcf-c6a48f4d28ac","kind":"skill:build-contract-project","nodes":[{"acceptedSubmissionHash":"697ae96282503bb0c6c30d47cd652fc355b643c087058799dd55518179f8502e","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"6e3e328aa196f6965a2821b098b101e9ff09eef55cdaafaec1d6ea18007b96f6","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"6e3e328aa196f6965a2821b098b101e9ff09eef55cdaafaec1d6ea18007b96f6","skillId":"build-contract-project","state":"accepted"}],"objective":"Keel: a launchpad on Ethereum mainnet where trading fees pay for continued IMD swarm work on each launched project.\n\nThe website comes in a later job; emit events for every state change it needs (launch, fee accrued, share lowered, creator status, Reimbursed, RunVerified, close announced and cancelled, Closed, Claimed).\n\nContracts: Solidity 0.8.26, Foundry, OpenZeppelin 5, Uniswap v4. Mainnet PoolManager 0x000000000004444c5dc75cB358380D2e3dE08A90, IMD token 0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7. Constructor arguments are only addresses, uint256 and $owner/$contract references; deployment calls nothing but constructors; the owner finishes setup afterwards. Vault, factory and router: owner $owner, two-step transfer.\n\n1. KeelToken: EIP-1167 clone template. initialize(name, symbol, to) once; supply 1e27, plain transfers, no mint; balance checkpoints (Checkpoints.Trace208) with balanceAt(account, pastBlock).\n\n2. KeelVault: IMD fee accounting per launched token. Phases Building and Closed.\n- Each fee in basis points: team 1000 fixed; creatorBps chosen at launch in [0, 8000]; the token's basket gets the rest. The creator can only lower creatorBps (lowerCreatorShare). The owner can mark a creator inactive or active (setCreatorActive); while inactive the creator share goes to the basket. Both calls first call hook.distribute(token).\n- reimburse(token, jobRef, commit, amount), only the verifier: Building, creator active, jobRef never used, commit != 0, 0 < amount <= runPrice, basket >= amount. Pays the creator, never the caller. Events Reimbursed and RunVerified.\n- recordRun(token, jobRef, commit), only the verifier: Building, jobRef never used, commit != 0; reverts if the creator is active and basket >= runPrice. Moves no funds. Event RunVerified with amount 0. jobRef usage is shared with reimburse, across all tokens.\n- runPrice 0.5 IMD, owner may set it within [0.1, 2] IMD. Owner sets and revokes the verifier.\n- deposit(token, amount): anyone adds IMD to a Building basket.\n- Close: owner announces, may cancel, and closes no earlier than 7 days after the notice and 30 days after launch. Close first distributes pending fees, then snapshots at block.number - 1: poolAtClose = basket, circulating = totalSupply - balanceAt(PoolManager) - balanceAt(0x...dEaD). Each holder claims once poolAtClose * balanceAt(holder, snapshot block) / circulating; if circulating is 0 nothing is claimable. After close new fees go 10% team, 90% creator, and the creator may launch again.\n- One Building token per creator. claimCreator and claimTeam pay accrued balances to the creator and the owner.\n- Basket IMD leaves only through reimburse or claim. The vault balance always covers everything owed. ReentrancyGuard and checks-effects-interactions wherever IMD moves.\n- accrue(token, amount) only from the hook.\n- wire(factory) once, only the owner; requires factory.vault() == this and takes the hook from factory.hook(). Until wired nothing can register.\n\n3. KeelHook (v4 hook; permissions beforeInitialize, beforeAddLiquidity, beforeSwap, afterSwap, beforeSwapReturnDelta, afterSwapReturnDelta). Only the factory may initialize pools or add liquidity. Takes 1% of every swap in IMD, exact input and exact output, pending per token; distribute(token), by anyone, sends it to the vault via vault.accrue. The factory constructor deploys it with CREATE2, searching the salt on chain so the address carries exactly these permission bits; write the search gas-efficiently and measure its gas in tests.\n\n4. KeelFactory: launch(name, symbol, jobRef, projectRef, creatorBps, startTick, deadline, signature). EIP-712 permit by the signer over creator, keccak(jobRef), keccak(projectRef), name hash, symbol hash, startTick, deadline. One token per jobRef. Clones KeelToken, opens a TOKEN/IMD pool (fee 0, tick spacing 200) at startTick within owner-set bounds, places the whole supply as single-sided liquidity above the start tick, locks it forever, sends rounding dust to 0x...dEaD, registers the token in the vault with creator, keccak(jobRef), keccak(projectRef) and creatorBps. launch also takes a metadata string (JSON with description, image URL and links, at most 1024 bytes) that is only emitted in the Launched event. jobRef and projectRef are lowercase UUID strings (IMD job id and project id). Starts paused; owner sets signer, start tick bounds and pause.\n\n5. KeelRouter: exact-input buy and sell between ETH, IMD and launched tokens, routing ETH through an owner-set ETH/IMD v4 pool (default hookless, fee 10000, tick spacing 200); minimum output, deadline, quote views.\n\nTests (Foundry, unit and invariant): fee split and lower-only share; inactive creator stops reimburse but not recordRun; recordRun refused while reimburse is possible; shared jobRef; amount cap; close timing; snapshot excludes transfers in the close block; claims never exceed poolAtClose; one Building token per creator; bad permits rejected; vault balance covers everything owed.","parentJobId":null,"planHash":"0f4fe9223531b976ceedf8667f882deb1a85d8ab9718613398b4c2ad0408b6f1","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"05e9d60a-4559-4724-bdcf-c6a48f4d28ac","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1214-keel-launchpad-ethereum-mainnet"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"52208","feedbackHash":"e1f825b6b6bfd560d171002687f8922774cb015f96407ee6878e53c8f2e9e03c","nodeKey":"build_contract_project","submissionHash":"525e1356eef29da1dcfd6efe9dacb9616d17e1ff0e60cd7b905e489c18f1086d","tag1":"verification:checks","tag2":"acceptance-v2","value":0},{"agentId":"51241","feedbackHash":"dec1abe7cf498b9784ffc01c522308e8cac62f146e51a7367e0f6ea5440381c0","nodeKey":"build_contract_project","submissionHash":"697ae96282503bb0c6c30d47cd652fc355b643c087058799dd55518179f8502e","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"882d2e5ec0bfb93d68ee5e1bbb1472c980b9fae6d6113c2813e0d3cff6b98ac8","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"c3fafd045869784e3807c0a83c51a77f0a981d79aa8dd9d5c7b5149b62a03155","device":"9676188abd3783f2","findings":[],"hash":"525e1356eef29da1dcfd6efe9dacb9616d17e1ff0e60cd7b905e489c18f1086d","nodeId":"7af6173b-cee1-413e-8129-d2b1305622cc","outcome":"completed","summary":"Implemented all five Keel contracts, vendored dependencies, and deployment/operations documentation.\n\nVerified:\n- `forge build`\n- 75 passing tests, including offline parallel execution with an empty environment\n- `forge fmt --check`\n\nSetup, assumptions and responsibilities are documented in `README.md`; test and gas measurements are in `VALIDATION.md`.","treeHash":"7a1798e94310821c136a6bf9422f65eb14c77fc0","usage":{"cachedInputTokens":6287872,"inputTokens":185342,"model":"gpt-6-astra","outputTokens":88593,"runtime":"codex","turns":12,"wallClockMs":2173500}},{"artifacts":[],"attempt":2,"bundleHash":"f9bd4941468c30e60ca1d2229c04f9aa9fb106fe9625c03fbfb9f0c4c4bd41f0","device":"b6e7758e5e0349d3","findings":[],"hash":"697ae96282503bb0c6c30d47cd652fc355b643c087058799dd55518179f8502e","nodeId":"7af6173b-cee1-413e-8129-d2b1305622cc","outcome":"completed","summary":"Implemented all five contracts, vendored dependencies, deployment documentation, and unit/invariant tests. The router collects input from `msg.sender` before its callback, addressing the previous finding.\n\nVerified:\n\n- `forge build` — pass\n- `forge test` — 61 passing checks\n- `forge fmt --check` — pass\n\nSetup and assumptions are in `README.md`; gas measurements and validation details are in `docs/VALIDATION.md`.","treeHash":"c72dd5ec3153942bba81c7d0db9040df551a7929","usage":{"cachedInputTokens":5827968,"inputTokens":185400,"model":"gpt-6-astra","outputTokens":82814,"runtime":"codex","turns":10,"wallClockMs":1878236}}],"verification":[{"checks":[{"durationMs":39399,"exitCode":0,"name":"build","output":"Compiling 116 files with Solc 0.8.26\nSolc 0.8.26 finished in 39.25s\nCompiler run successful!\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/Uuid.sol:13:31\n   │\n13 │                 if (c != \"-\") revert InvalidUuid();\n   │                               ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n   ╭▸ src/libraries/Metadata.sol:26:17\n   │\n26 │             if (flag != 0) {\n   │                 ━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n   ╭▸ src/libraries/Metadata.sol:27:21\n   │\n27 │                 if (fields & flag != 0 || p == b.length) revert InvalidMetadata();\n   │                     ━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/Uuid.sol:15:17\n   │\n15 │                 revert InvalidUuid();\n   │                 ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[missing-events-access-control]: `signer` is changed without an event but is used for access control\n    ╭▸ src/KeelFactory.sol:105:9\n    │\n105 │         signer = signer_;\n    │         ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/Metadata.sol:49:45\n   │\n49 │         if (p == b.length || b[p++] != '\"') revert InvalidMetadata();\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/libraries/Metadata.sol:53:27\n   │\n53 │             if (c < 0x20) revert InvalidMetadata();\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/libraries/Metadata.sol:55:36\n   │\n55 │                 if (p == b.length) revert InvalidMetadata();\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/libraries/Metadata.sol:59:44\n   │\n59 │                         if (p == b.length) revert InvalidMetadata();\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/libraries/Metadata.sol:62:29\n   │\n62 │ …                     revert InvalidMetadata();\n   │                       ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[missing-events-access-control]: `_launchingToken` is changed without an event but is used for access control\n    ╭▸ src/KeelFactory.sol:187:9\n    │\n187 │         _launchingToken = token;\n    │         ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/Metadata.sol:68:21\n   │\n68 │                     revert InvalidMetadata();\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/libraries/Metadata.sol:72:9\n   │\n72 │         revert InvalidMetadata();\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/libraries/Metadata.sol:20:49\n   │\n20 │             if (p == b.length || b[p++] != \":\") revert InvalidMetadata();\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/libraries/Metadata.sol:27:58\n   │\n27 │                 if (fields & flag != 0 || p == b.length) revert InvalidMetadata();\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/libraries/Metadata.sol:29:47\n   │\n29 │                 if (flag != 4 && b[p] != '\"') revert InvalidMetadata();\n   │                                               ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[missing-events-access-control]: `_launchingToken` is changed without an event but is used for access control\n    ╭▸ src/KeelFactory.sol:208:9\n    │\n208 │         _launchingToken = address(0);\n    │         ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/Metadata.sol:30:62\n   │\n30 │                 if (flag == 4 && b[p] != \"{\" && b[p] != \"[\") revert InvalidMetadata();\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/libraries/Metadata.sol:76:42\n   │\n76 │         if (p == b.length || depth > 16) revert InvalidMetadata();\n   │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/V4Quote.sol:40:37\n   │\n40 │         if (amount == 0 || amount > uint256(uint128(type(int128).max))) revert InvalidAmount();\n   │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/Metadata.sol:87:57\n   │\n87 │                     if (p == b.length || b[p++] != \":\") revert InvalidMetadata();\n   │                                                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/V4Quote.sol:40:45\n   │\n40 │         if (amount == 0 || amount > uint256(uint128(type(int128).max))) revert InvalidAmount();\n   │                                             ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint128' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/Metadata.sol:91:36\n   │\n91 │                 if (p == b.length) revert InvalidMetadata();\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/libraries/Metadata.sol:93:36\n   │\n93 │                 if (b[p++] != \",\") revert InvalidMetadata();\n   │                                    ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/V4Quote.sol:66:18\n   │\n66 │                 -int256(s.remaining),\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[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/libraries/Metadata.sol:105:28\n    │\n105 │         if (p == b.length) revert InvalidMetadata();\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/libraries/Metadata.sol:109:43\n    │\n109 │             if (b[p] < \"1\" || b[p] > \"9\") revert InvalidMetadata();\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/libraries/Metadata.sol:124:25\n    │\n124 │         if (p == start) revert InvalidMetadata();\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/libraries/Metadata.sol:33:32\n   │\n33 │             if (p == b.length) revert InvalidMetadata();\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/libraries/Metadata.sol:38:32\n   │\n38 │             if (b[p++] != \",\") revert InvalidMetadata();\n   │                                ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/KeelFactory.sol:100:9\n    │\n100 │         emit HookDeployed(hook_, salt, attempts, searchGas);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/KeelFactory.sol:91:38\n   │\n91 │         bytes32 initHash = keccak256(abi.encodePacked(type(KeelHook).creationCode, abi.encode(address(this), vault_)));\n   │                                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[missing-events-access-control]: `_requestHash` is changed without an event but is used for access control\n    ╭▸ src/KeelRouter.sol:114:9\n    │\n114 │         _requestHash = bytes32(0);\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/KeelFactory.sol:103:24\n    │\n103 │     function setSigner(address signer_) external onlyOwner {\n    │                        ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/KeelRouter.sol:88:9\n   │\n88 │         emit EthPoolSet(key.toId(), key.fee, key.tickSpacing);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/KeelRouter.sol:74:67\n   │\n74 │         if (fee >= 1_000_000 || tickSpacing == 0 || tickSpacing > uint256(uint24(TickMath.MAX_TICK_SPACING))) {\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/KeelRouter.sol:74:75\n   │\n74 │         if (fee >= 1_000_000 || tickSpacing == 0 || tickSpacing > uint256(uint24(TickMath.MAX_TICK_SPACING))) {\n   │                                                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint24' 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/libraries/Metadata.sol:81:35\n   │\n81 │             bytes1 end = object ? bytes1(\"}\") : bytes1(\"]\");\n   │                                   ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' 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/KeelRouter.sol:80:13\n   │\n80 │             uint24(fee),\n   │             ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint24' 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/KeelRouter.sol:81:13\n   │\n81 │             int24(int256(tickSpacing)),\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[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/KeelRouter.sol:81:19\n   │\n81 │             int24(int256(tickSpacing)),\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/libraries/Metadata.sol:81:49\n   │\n81 │             bytes1 end = object ? bytes1(\"}\") : bytes1(\"]\");\n   │                                                 ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/KeelRouter.sol:106:32\n    │\n106 │         amountOut = abi.decode(poolManager.unlock(data), (uint256));\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/KeelRouter.sol:100:13\n    │\n100 │         if (block.timestamp > deadline) revert Expired();\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/KeelFactory.sol:152:13\n    │\n152 │         if (block.timestamp > deadline) revert Expired();\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/KeelRouter.sol:107:9\n    │\n107 │         emit Swapped(msg.sender, tokenIn, tokenOut, recipient, amountIn, amountOut);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[arbitrary-send-erc20]: `transferFrom` uses an arbitrary `from`; require it to equal `msg.sender` or `address(this)`\n    ╭▸ src/KeelRouter.sol:127:13\n    │\n127 │             IERC20(r.tokenIn).safeTransferFrom(r.payer, address(poolManager), r.amountIn);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-erc20\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/KeelFactory.sol:192:9\n    │\n192 │         emit LiquidityLocked(token, lower, KeelConstants.MAX_TICK, uint128(liquidity), dust);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/KeelFactory.sol:193:9\n    │\n193 │ ┏         emit Launched(\n194 │ ┃             token, msg.sender, jobHash, projectHash, jobRef, projectRef, creatorBps, startTick, key.toId(), metadata\n195 │ ┃         );\n    │ ┗━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/KeelFactory.sol:175:9\n    │\n175 │         poolManager.initialize(key, TickMath.getSqrtPriceAtTick(startTick));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelFactory.sol:184:43\n    │\n184 │         if (liquidity == 0 || liquidity > uint256(uint128(type(int128).max))) revert InvalidLiquidity();\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/KeelFactory.sol:184:51\n    │\n184 │         if (liquidity == 0 || liquidity > uint256(uint128(type(int128).max))) revert InvalidLiquidity();\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/KeelFactory.sol:186:34\n    │\n186 │         lockedLiquidity[token] = uint128(liquidity);\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[unused-return]: return value of an external call is not used\n    ╭▸ src/KeelFactory.sol:188:9\n    │\n188 │         poolManager.unlock(abi.encode(token));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelFactory.sol:192:68\n    │\n192 │         emit LiquidityLocked(token, lower, KeelConstants.MAX_TICK, uint128(liquidity), dust);\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-events-access-control]: `verifier` is changed without an event but is used for access control\n    ╭▸ src/KeelVault.sol:179:9\n    │\n179 │         verifier = verifier_; // zero explicitly revokes authorization\n    │         ━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/KeelVault.sol:103:56\n    │\n103 │     function wire(address factory_) external onlyOwner nonReentrant {\n    │                                                        ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelRouter.sol:148:41\n    │\n148 │         if (amountIn == 0 || amountIn > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelRouter.sol:148:49\n    │\n148 │         if (amountIn == 0 || amountIn > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/KeelFactory.sol:209:33\n    │\n209 │           (BalanceDelta delta,) = poolManager.modifyLiquidity(\n    │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n210 │ ┃             poolKey(token),\n211 │ ┃             ModifyLiquidityParams({\n212 │ ┃                 tickLower: lowerTick[token],\n    ‡ ┃\n217 │ ┃             \"\"\n218 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelFactory.sol:220:24\n    │\n220 │         uint256 owed = uint256(-int256(delta.amount0()));\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/KeelVault.sol:128:24\n    │\n128 │         p.creatorBps = uint16(creatorBps);\n    │                        ━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint16' 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/KeelVault.sol:156:21\n    │\n156 │         p.basket += basket;\n    │                     ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/KeelVault.sol:157:9\n    │\n157 │         emit FeeAccrued(token, amount, team, creator, basket);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/KeelVault.sol:164:9\n    │\n164 │         IKeelHook(hook).distribute(token);\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/KeelVault.sol:167:9\n    │\n167 │         emit CreatorShareLowered(token, previous, newBps);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelVault.sol:166:24\n    │\n166 │         p.creatorBps = uint16(newBps);\n    │                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint16' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/KeelVault.sol:172:9\n    │\n172 │         IKeelHook(hook).distribute(token);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/KeelVault.sol:170:78\n    │\n170 │     function setCreatorActive(address token, bool active) external onlyOwner nonReentrant {\n    │                                                                              ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/KeelVault.sol:174:9\n    │\n174 │         emit CreatorStatusChanged(token, p.creator, active);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/KeelVault.sol:177:26\n    │\n177 │     function setVerifier(address verifier_) external onlyOwner {\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/KeelFactory.sol:230:58\n    │\n230 │             bytes32 salt = keccak256(abi.encode(jobHash, nonce));\n    │                                                          ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelRouter.sol:163:41\n    │\n163 │         if (amountIn == 0 || amountIn > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelRouter.sol:163:49\n    │\n163 │         if (amountIn == 0 || amountIn > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelRouter.sol:168:30\n    │\n168 │                 zeroForOne, -int256(amountIn), zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1\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/KeelRouter.sol:174:31\n    │\n174 │         if (int256(input) != -int256(amountIn) || output <= 0) revert PartialFill();\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/KeelRouter.sol:175:16\n    │\n175 │         return uint256(uint128(output));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelRouter.sol:175:24\n    │\n175 │         return uint256(uint128(output));\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[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/KeelVault.sol:220:13\n    │\n220 │         if (imd.balanceOf(address(this)) != beforeBalance + amount) revert InvalidAmount();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/KeelVault.sol:227:9\n    │\n227 │ ┏         emit CloseAnnounced(\n228 │ ┃             token, block.timestamp, Math.max(block.timestamp + NOTICE_PERIOD, p.launchedAt + MIN_BUILDING_PERIOD)\n229 │ ┃         );\n    │ ┗━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/KeelVault.sol:246:9\n    │\n246 │         IKeelHook(hook).distribute(token);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/KeelHook.sol:103:77\n    │\n103 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 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/KeelVault.sol:239:54\n    │\n239 │     function close(address token) external onlyOwner nonReentrant {\n    │                                                      ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/KeelVault.sol:243:13\n    │\n243 │             block.timestamp < p.closeNotice + NOTICE_PERIOD || block.timestamp < p.launchedAt + MIN_BUILDING_PERIOD\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/KeelVault.sol:243:64\n    │\n243 │             block.timestamp < p.closeNotice + NOTICE_PERIOD || block.timestamp < p.launchedAt + MIN_BUILDING_PERIOD\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/KeelVault.sol:256:9\n    │\n256 │         emit Closed(token, snapshot, p.poolAtClose, circulating);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/KeelHook.sol:94:30\n   │\n94 │             uint256 amount = uint256(specified < 0 ? -specified : specified);\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/KeelHook.sol:97:86\n   │\n97 │                 (specified < 0 && fee >= amount) || (specified > 0 && amount + fee > uint256(uint128(type(int128).max)))\n   │                                                                                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/KeelHook.sol:97:94\n   │\n97 │                 (specified < 0 && fee >= amount) || (specified > 0 && amount + fee > uint256(uint128(type(int128).max)))\n   │                                                                                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint128' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelHook.sol:103:63\n    │\n103 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);\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/KeelHook.sol:103:70\n    │\n103 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 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[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/KeelVault.sol:277:9\n    │\n277 │         emit Claimed(token, msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelHook.sol:115:30\n    │\n115 │             uint256 amount = uint256(specified < 0 ? -specified : specified);\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/KeelHook.sol:118:56\n    │\n118 │             if (int256(delta.amount1()) != specified + int256(fee)) revert PartialFill();\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/KeelHook.sol:123:59\n    │\n123 │         uint256 feeAmount = imdDelta < 0 ? KeelFees.onNet(uint256(-imdDelta)) : KeelFees.onGross(uint256(imdDelta));\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/KeelHook.sol:123:98\n    │\n123 │         uint256 feeAmount = imdDelta < 0 ? KeelFees.onNet(uint256(-imdDelta)) : KeelFees.onGross(uint256(imdDelta));\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/KeelHook.sol:124:25\n    │\n124 │         if (feeAmount > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelHook.sol:124:33\n    │\n124 │         if (feeAmount > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelHook.sol:126:44\n    │\n126 │         return (IHooks.afterSwap.selector, int128(int256(feeAmount)));\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/KeelHook.sol:126:51\n    │\n126 │         return (IHooks.afterSwap.selector, int128(int256(feeAmount)));\n    │                                                   ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/KeelHook.sol:139:13\n    │\n139 │             poolManager.unlock(abi.encode(amount));\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/KeelHook.sol:142:9\n    │\n142 │         IKeelVault(vault).accrue(token, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/KeelHook.sol:163:9\n    │\n163 │         poolManager.burn(address(this), IMD.toId(), amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/KeelHook.sol:164:9\n    │\n164 │         poolManager.take(IMD, vault, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/KeelHook.sol:143:9\n    │\n143 │         emit Distributed(token, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/KeelHook.sol:139:13\n    │\n139 │             poolManager.unlock(abi.encode(amount));\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/KeelHook.sol:159:9\n    │\n159 │         emit FeePending(token, amount, pending[token]);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[environment-read-across-mutation]: `block.chainid` may be reused across `vm.chainId`\n    ╭▸ test/KeelFactory.t.sol:108:25\n    │\n108 │         uint256 chain = block.chainid;\n    │                         ━━━━━━━━━━━━━\n109 │         vm.chainId(chain + 1);\n    │         ───────────────────── `vm.chainId` changes this environment here\n    │\n    ├ help: capture it with `vm.getChainId()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":2015,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 4 tests for test/Metadata.t.sol:MetadataTest\n[PASS] testFuzzArbitraryDescriptionEscapedByHex(bytes32) (runs: 256, μ: 53516, ~: 53516)\n[PASS] testMalformedMetadata() (gas: 277878)\n[PASS] testMetadataExactly1024Bytes() (gas: 380233)\n[PASS] testMetadataValidOptionalFieldsAndEscapes() (gas: 124941)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 9.99ms (11.31ms CPU time)\n\nRan 6 tests for test/KeelSecurity.t.sol:KeelVaultSecurityTest\n[PASS] testClaimTransferFailureDoesNotConsumeEntitlement() (gas: 837638)\n[PASS] testDepositReentrancyRejectedAndAccountingConserved() (gas: 573188)\n[PASS] testPayoutReentrancyRejected() (gas: 640614)\n[PASS] testPendingDistributionFailurePreservesShare() (gas: 306935)\n[PASS] testReimbursementTransferFailureRollsBackJobAndBasket() (gas: 728586)\n[PASS] testShortDepositRejectedWithoutLeavingUnbackedLiabilities() (gas: 362934)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 26.41ms (1.58ms CPU time)\n\nRan 24 tests for test/KeelVault.t.sol:KeelVaultTest\n[PASS] testAccrueOnlyHookAndMustBeFunded() (gas: 580075)\n[PASS] testAmountCapAndInvalidRunDoNotConsumeJob() (gas: 737781)\n[PASS] testCloseBothTimeConstraintsAndCancellation() (gas: 522013)\n[PASS] testCloseSnapshotsPreviousBlockAndFlushesPending() (gas: 1369106)\n[PASS] testClosedFeeSplitIgnoresShareAndInactiveStatus() (gas: 1041111)\n[PASS] testDepositOnlyBuildingNoSplitAndZeroRejected() (gas: 735678)\n[PASS] testFeeSplitAndClaimsPayFixedRecipients() (gas: 758757)\n[PASS] testFuzzClaimsNeverExceedPool(uint256,uint256,uint256) (runs: 256, μ: 1219279, ~: 1217534)\nLogs:\n  Bound result 22219\n  Bound result 4\n  Bound result 6120\n\n[PASS] testFuzzFeeSplitConservesEveryWei(uint256,uint256) (runs: 256, μ: 550163, ~: 553906)\nLogs:\n  Bound result 1208\n  Bound result 101\n\n[PASS] testInactiveSplitsAtBoundaryAndCanReactivate() (gas: 971007)\n[PASS] testInactiveStopsReimburseButAllowsRecordRunWhenFunded() (gas: 762514)\n[PASS] testJobUsageSharedAcrossMethodsAndTokens() (gas: 512922)\n[PASS] testLowerOnlyDistributesAtPreviousShare() (gas: 1105841)\n[PASS] testOneBuildingTokenPerCreatorReleasedOnClose() (gas: 709852)\n[PASS] testOwnershipTwoStepAndTeamFollowsAcceptedOwner() (gas: 519400)\n[PASS] testRecordRunRefusedAtThresholdAndPermittedBelow() (gas: 472549)\n[PASS] testRegisterRejectsInvalidShareAndDuplicate() (gas: 521212)\n[PASS] testReimbursedAndRunVerifiedEventsIdentifyWorkAndAmount() (gas: 396418)\n[PASS] testRunPriceBoundsAndOwner() (gas: 229299)\n[PASS] testSnapshotExcludesManagerAndDeadAndLaterTransfers() (gas: 1259090)\n[PASS] testUnknownTokenAndCloseOwnerRestrictions() (gas: 205317)\n[PASS] testVerifierUnsetRevokedAndUnauthorized() (gas: 243772)\n[PASS] testWireOnceRejectMismatchedAndNoRegisterBeforeWire() (gas: 674544)\n[PASS] testZeroCirculatingAndRoundingRemainReserved() (gas: 768168)\nSuite result: ok. 24 passed; 0 failed; 0 skipped; finished in 26.48ms (49.26ms CPU time)\n\nRan 2 tests for test/KeelSecurity.t.sol:KeelRouterSecurityTest\n[PASS] testNativeRecipientCannotReenterRouter() (gas: 962760)\n[PASS] testRejectedNativeOutputRollsBackBothPoolsAndHookFees() (gas: 1522512)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 56.06ms (1.54ms CPU time)\n\nRan 12 tests for test/KeelFactory.t.sol:KeelFactoryTest\n[PASS] testBadSignerMalformedSignatureAndExpiredPermit() (gas: 380008)\n[PASS] testConstructorsWorkWithoutExternalContractsAndStartPaused() (gas: 102142)\n[PASS] testERC1271Signer() (gas: 1175732)\n[PASS] testFactoryAndRouterOwnershipRequireAcceptance() (gas: 272925)\n[PASS] testJobCannotLaunchTwiceAndCreatorCannotLaunchWhileBuilding() (gas: 1478645)\n[PASS] testMetadataBoundedAndSchemaValidated() (gas: 1099182)\n[PASS] testPauseSignerRevocationAndBounds() (gas: 450688)\n[PASS] testPermitCannotChangeCreatorNameSymbolJobProjectTickOrDeadline() (gas: 924408)\n[PASS] testPermitDomainRejectsDifferentChainAndFactory() (gas: 378804)\n[PASS] testTypedDigestMatchesIndependentEIP712Encoding() (gas: 17247)\n[PASS] testUnalignedNegativeTickLiquidityStartsStrictlyAbove() (gas: 957126)\n[PASS] testUuidMustBeLowercaseAndCanonical() (gas: 461301)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 56.14ms (19.03ms CPU time)\n\nRan 6 tests for test/KeelToken.t.sol:KeelTokenTest\n[PASS] testCheckpointsUseEndOfBlockAndPastOnly() (gas: 302181)\n[PASS] testCloneMetadataAndFixedSupply() (gas: 70815)\n[PASS] testFuzzCheckpoints(uint256,uint256) (runs: 256, μ: 306765, ~: 307717)\nLogs:\n  Bound result 524\n  Bound result 193\n\n[PASS] testImplementationAndCloneCannotInitializeAgain() (gas: 68969)\n[PASS] testRejectBadInitialization() (gas: 344074)\n[PASS] testSelfTransfersAndTransferFromArePlain() (gas: 274189)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 56.12ms (11.20ms CPU time)\n\nRan 18 tests for test/KeelIntegration.t.sol:KeelIntegrationTest\n[PASS] testAnyoneCanDistributeInsideExistingManagerUnlock() (gas: 860932)\n[PASS] testCloseSnapshotTradingAndCreatorCanLaunchAgain() (gas: 2478022)\n[PASS] testEthPoolMustBeConfiguredAndOwnerControlled() (gas: 615854)\n[PASS] testExactInputSellFeeIsIMDAndQuoteMatches() (gas: 590649)\n[PASS] testExactOutputBuyAndSellChargeOnlyIMD() (gas: 918532)\n[PASS] testExternalActorsCannotInitializeOrAddLiquidity() (gas: 292019)\n[PASS] testFirstBuyWithoutIMDReservesAndFeeDistribution() (gas: 684538)\n[PASS] testFuzzBuySellQuoteMatchesExecution(uint256,uint256) (runs: 256, μ: 853895, ~: 865314)\nLogs:\n  Bound result 159966426047972371821\n  Bound result 77\n\n[PASS] testFuzzExactOutputFeesPreserveRequestedAmount(uint256,uint256) (runs: 256, μ: 740269, ~: 740189)\nLogs:\n  Bound result 10183426696048847180168\n  Bound result 77\n\n[PASS] testHookBitsAndSaltSearchGas() (gas: 43555)\nLogs:\n  salt candidates: 15487\n  salt search gas: 1873996\n\n[PASS] testLaunchUsesRealManagerAndLocksAllSupply() (gas: 211073)\n[PASS] testLowerShareFlushesRealPendingFeesAtOldRate() (gas: 955502)\n[PASS] testNativeEthRoutingAndViewQuotes() (gas: 1588286)\n[PASS] testPartialFillRevertsAndDoesNotAccrue() (gas: 187208)\n[PASS] testQuoteIncludesProtocolFeesInBothDirections() (gas: 1490132)\n[PASS] testRouterSlippageDeadlineValueAndInvalidRoutes() (gas: 416655)\n[PASS] testTokenToTokenRoutingAccruesToBothProjects() (gas: 2119405)\n[PASS] testViewQuoteCrossesMultipleInitializedLiquidityTicks() (gas: 1405013)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 56.15ms (102.58ms CPU time)\n\nRan 1 test for test/KeelGas.t.sol:KeelGasTest\n[PASS] testConstructorSaltMiningGasSamples() (gas: 64051429)\nLogs:\n  candidate count: 10382\n  search gas: 1256291\n  CREATE execution gas: 5461579\n  candidate count: 69349\n  search gas: 8391298\n  CREATE execution gas: 12596610\n  candidate count: 30317\n  search gas: 3668426\n  CREATE execution gas: 7873738\n  candidate count: 44255\n  search gas: 5354924\n  CREATE execution gas: 9560236\n  candidate count: 9451\n  search gas: 1143640\n  CREATE execution gas: 5348952\n  candidate count: 18889\n  search gas: 2285638\n  CREATE execution gas: 6490950\n  candidate count: 23263\n  search gas: 2814892\n  CREATE execution gas: 7020204\n  candidate count: 38754\n  search gas: 4689303\n  CREATE execution gas: 8894627\n  maximum search gas (8 samples): 8391298\n  mean search gas (8 samples): 3700551\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 92.01ms (91.89ms CPU time)\n\nRan 1 test for test/KeelInvariant.t.sol:KeelVaultInvariantTest\n[PASS]\nKeelVaultInvariantTest invariants:\n[PASS] invariantClosedBasketsOnlyDecreaseByClaims\n[PASS] invariantVaultAlwaysCoversAndAccountsForEveryWei\n KeelVaultInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+------------+-------+---------+----------╮\n| Contract     | Selector   | Calls | Reverts | Discards |\n+========================================================+\n| VaultHandler | advance    | 657   | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| VaultHandler | claim      | 604   | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| VaultHandler | close      | 645   | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| VaultHandler | deposit    | 636   | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| VaultHandler | fee        | 617   | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| VaultHandler | gift       | 644   | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| VaultHandler | lowerShare | 627   | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| VaultHandler | notice     | 581   | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| VaultHandler | payouts    | 644   | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| VaultHandler | run        | 642   | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| VaultHandler | runPrice   | 626   | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| VaultHandler | status     | 616   | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| VaultHandler | transfer   | 653   | 0       | 0        |\n╰--------------+------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000\n  Bound result 4260\n  Bound result 4500000000000000000\n  Bound result 3732\n  Bound result 10000000000000000\n  Bound result 1624662\n  Bound result 666\n  Bound result 3\n  Bound result 710\n  Bound result 699999999999999998\n  Bound result 138\n  Bound result 1904\n  Bound result 466\n  Bound result 255\n  Bound result 40779902470294861373\n  Bound result 6210\n  Bound result 99\n  Bound result 1643\n  Bound result 4\n  Bound result 3200000\n  Bound result 918\n  Bound result 9698\n  Bound result 1500000000000000000\n  Bound result 101\n  Bound result 3689\n  Bound result 100000000000000000000\n  Bound result 11665\n  Bound result 7671\n  Bound result 65772452625379181\n  Bound result 93157381930271898760390948\n  Bound result 1900000000000006956\n  Bound result 6787\n  Bound result 3478\n  Bound result 1927\n  Bound result 8000\n  Bound result 995\n  Bound result 774\n  Bound result 10000000000000000\n  Bound result 1900000000000011827\n  Bound result 1378621\n  Bound result 1000\n  Bound result 999\n  Bound result 499999999999999999\n  Bound result 44829902470295871569\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.18s (1.18s CPU time)\n\nRan 1 test for test/KeelInvariant.t.sol:KeelTradingInvariantTest\n[PASS] invariantPendingFeesAreFullyBackedByManagerClaims() (runs: 128, calls: 8192, reverts: 0)\n\n╭----------------+------------+-------+---------+----------╮\n| Contract       | Selector   | Calls | Reverts | Discards |\n+==========================================================+\n| TradingHandler | buy        | 2718  | 0       | 0        |\n|----------------+------------+-------+---------+----------|\n| TradingHandler | distribute | 2748  | 0       | 0        |\n|----------------+------------+-------+---------+----------|\n| TradingHandler | sell       | 2726  | 0       | 0        |\n╰----------------+------------+-------+---------+----------╯\n\nLogs:\n  Bound result 99999999000000000801\n  Bound result 10000000000000000001\n  Bound result 99999999000000072349\n  Bound result 99999999000000003944\n  Bound result 99999999000000000021\n  Bound result 68278050135408434371\n  Bound result 99999999000000000071\n  Bound result 250000000000000000\n  Bound result 44202287172669614356\n  Bound result 996597370951458749555081874\n  Bound result 64892501778865485656\n  Bound result 99999999000000008563\n  Bound result 99999999000000007660\n  Bound result 99999999000000002140\n  Bound result 82014279864219093606\n  Bound result 99999999000000000037\n  Bound result 99999999000000001542\n  Bound result 996724118713851678985297585\n  Bound result 100000000000000000000\n  Bound result 99999999000000004596\n  Bound result 94300544978942505963\n  Bound result 99999999000000007402\n  Bound result 994636701195835980266719454\n  Bound result 54895583406605027873\n  Bound result 14491159848603495323\n  Bound result 962713243646694381730651933\n  Bound result 99999999000000000188\n  Bound result 2000000000000000000000\n  Bound result 99999999000000007041\n  Bound result 989480539751962408815203985\n  Bound result 4000000000000000000\n  Bound result 2000000000000000001\n  Bound result 10000000000000000001\n  Bound result 868154878778060700538925696\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.92s (1.91s CPU time)\n\nRan 10 test suites in 1.92s (3.48s CPU time): 75 tests passed, 0 failed, 0 skipped (75 total tests)\n","passed":true},{"durationMs":46,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"KeelFactory.acceptOwnership()\",\"KeelFactory.launch(string,string,string,string,uint256,int24,uint256,bytes,string)\",\"KeelFactory.setPaused(bool)\",\"KeelFactory.setSigner(address)\",\"KeelFactory.setStartTickBounds(int24,int24)\",\"KeelFactory.transferOwnership(address)\",\"KeelFactory.unlockCallback(bytes)\",\"KeelHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"KeelHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"KeelHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"KeelHook.distribute(address)\",\"KeelHook.unlockCallback(bytes)\",\"KeelRouter.acceptOwnership()\",\"KeelRouter.setEthPool(uint256,uint256)\",\"KeelRouter.swapExactInput(address,address,uint256,uint256,uint256,address)\",\"KeelRouter.transferOwnership(address)\",\"KeelRouter.unlockCallback(bytes)\",\"KeelToken.approve(address,uint256)\",\"KeelToken.initialize(string,string,address)\",\"KeelToken.transfer(address,uint256)\",\"KeelToken.transferFrom(address,address,uint256)\",\"KeelVault.acceptOwnership()\",\"KeelVault.accrue(address,uint256)\",\"KeelVault.announceClose(address)\",\"KeelVault.cancelClose(address)\",\"KeelVault.claim(address)\",\"KeelVault.claimCreator(address)\",\"KeelVault.claimTeam()\",\"KeelVault.close(address)\",\"KeelVault.deposit(address,uint256)\",\"KeelVault.lowerCreatorShare(address,uint256)\",\"KeelVault.recordRun(address,bytes32,bytes32)\",\"KeelVault.register(address,address,bytes32,bytes32,uint256)\",\"KeelVault.reimburse(address,bytes32,bytes32,uint256)\",\"KeelVault.setCreatorActive(address,bool)\",\"KeelVault.setRunPrice(uint256)\",\"KeelVault.setVerifier(address)\",\"KeelVault.transferOwnership(address)\",\"KeelVault.wire(address)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":24,\"LICENSE\":23,\"README.md\":160,\"VALIDATION.md\":60,\"foundry.toml\":22,\"remappings.txt\":4,\"src/KeelConstants.sol\":11,\"src/KeelFactory.sol\":253,\"src/KeelHook.sol\":179,\"src/KeelOwned.sol\":16,\"src/KeelRouter.sol\":186,\"src/KeelToken.sol\":54,\"src/KeelVault.sol\":320,\"src/interfaces/IKeel.sol\":22,\"src/libraries/KeelFees.sol\":13,\"src/libraries/Metadata.sol\":127,\"src/libraries/Uuid.sol\":20,\"src/libraries/V4Quote.sol\":108,\"test/KeelFactory.t.sol\":276,\"test/KeelGas.t.sol\":39,\"test/KeelIntegration.t.sol\":357,\"test/KeelInvariant.t.sol\":288,\"test/KeelSecurity.t.sol\":191,\"test/KeelToken.t.sol\":94,\"test/KeelVault.t.sol\":435,\"test/Metadata.t.sol\":61,\"test/helpers/Fixtures.sol\":305},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":7911,"exitCode":0,"name":"slither","output":"[high/high] arbitrary-send-erc20 at src/KeelRouter.sol:110: KeelRouter.unlockCallback(bytes) (src/KeelRouter.sol#110-133) uses arbitrary from in transferFrom: IERC20(r.tokenIn).safeTransferFrom(r.payer,address(poolManager),r.amountIn) (src/KeelRouter.sol#127)\n[medium/medium] uninitialized-local at src/KeelVault.sol:146: KeelVault.accrue(address,uint256).basket (src/KeelVault.sol#146) is a local variable never initialized\n[medium/medium] uninitialized-local at src/libraries/Metadata.sol:22: Metadata.validate(string).flag (src/libraries/Metadata.sol#22) is a local variable never initialized\n[medium/medium] uninitialized-local at src/KeelHook.sol:91: KeelHook.beforeSwap(address,PoolKey,SwapParams,bytes).fee (src/KeelHook.sol#91) is a local variable never initialized\n[medium/medium] uninitialized-local at src/libraries/V4Quote.sol:42: V4Quote.exactInput(IPoolManager,PoolKey,bool,uint256).s (src/libraries/V4Quote.sol#42) is a local variable never initialized\n[medium/medium] unused-return at src/KeelFactory.sol:204: KeelFactory.unlockCallback(bytes) (src/KeelFactory.sol#204-225) ignores return value by (delta,None) = poolManager.modifyLiquidity(poolKey(token),ModifyLiquidityParams({tickLower:lowerTick[token],tickUpper:KeelConstants.MAX_TICK,liquidityDelta:int256(uint256(lockedLiquidity[token])),salt:bytes32(0)}),) (src/KeelFactory.sol#209-218)\n[medium/medium] unused-return at src/KeelFactory.sol:139: KeelFactory.launch(string,string,string,string,uint256,int24,uint256,bytes,string) (src/KeelFactory.sol#139-196) ignores return value by poolManager.initialize(key,TickMath.getSqrtPriceAtTick(startTick)) (src/KeelFactory.sol#175)\n[medium/medium] unused-return at src/KeelFactory.sol:139: KeelFactory.launch(string,string,string,string,uint256,int24,uint256,bytes,string) (src/KeelFactory.sol#139-196) ignores return value by poolManager.unlock(abi.encode(token)) (src/KeelFactory.sol#188)\n[medium/medium] unused-return at src/libraries/V4Quote.sol:35: V4Quote.exactInput(IPoolManager,PoolKey,bool,uint256) (src/libraries/V4Quote.sol#35-83) ignores return value by (None,net) = manager.getTickLiquidity(id,next) (src/libraries/V4Quote.sol#73)\n[medium/medium] unused-return at src/KeelRouter.sol:73: KeelRouter.setEthPool(uint256,uint256) (src/KeelRouter.sol#73-89) ignores return value by (price,None,None,None) = StateLibrary.getSlot0(poolManager,key.toId()) (src/KeelRouter.sol#84)\n[medium/medium] unused-return at src/KeelToken.sol:48: KeelToken._update(address,address,uint256) (src/KeelToken.sol#48-53) ignores return value by _balancesAt[from].push(key,SafeCast.toUint208(balanceOf(from))) (src/KeelToken.sol#51)\n[medium/medium] unused-return at src/KeelHook.sol:129: KeelHook.distribute(address) (src/KeelHook.sol#129-144) ignores return value by poolManager.unlock(abi.encode(amount)) (src/KeelHook.sol#139)\n[medium/medium] unused-return at src/KeelToken.sol:48: KeelToken._update(address,address,uint256) (src/KeelToken.sol#48-53) ignores return value by _balancesAt[to].push(key,SafeCast.toUint208(balanceOf(to))) (src/KeelToken.sol#52)\n[low/medium] missing-zero-check at src/KeelVault.sol:177: KeelVault.setVerifier(address).verifier_ (src/KeelVault.sol#177) lacks a zero-check on :\n[low/medium] missing-zero-check at src/KeelFactory.sol:95: KeelFactory.constructor(address,address,address,uint256).hook_ (src/KeelFactory.sol#95) lacks a zero-check on :\n[low/medium] missing-zero-check at src/KeelFactory.sol:103: KeelFactory.setSigner(address).signer_ (src/KeelFactory.sol#103) lacks a zero-check on :\n[low/medium] reentrancy-benign at src/KeelHook.sol:129: Reentrancy in KeelHook.distribute(address) (src/KeelHook.sol#129-144):\n[low/medium] reentrancy-benign at src/KeelFactory.sol:139: Reentrancy in KeelFactory.launch(string,string,string,string,uint256,int24,uint256,bytes,string) (src/KeelFactory.sol#139-196):\n[low/medium] reentrancy-benign at src/KeelFactory.sol:139: Reentrancy in KeelFactory.launch(string,string,string,string,uint256,int24,uint256,bytes,string) (src/KeelFactory.sol#139-196):\n[low/medium] reentrancy-benign at src/KeelVault.sol:239: Reentrancy in KeelVault.close(address) (src/KeelVault.sol#239-257):\n[low/medium] reentrancy-events at src/KeelHook.sol:153: Reentrancy in KeelHook._collect(address,uint256) (src/KeelHook.sol#153-160):\n[low/medium] timestamp at src/KeelRouter.sol:92: KeelRouter.swapExactInput(address,address,uint256,uint256,uint256,address) (src/KeelRouter.sol#92-108) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelVault.sol:239: KeelVault.close(address) (src/KeelVault.sol#239-257) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelFactory.sol:139: KeelFactory.launch(string,string,string,string,uint256,int24,uint256,bytes,string) (src/KeelFactory.sol#139-196) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelVault.sol:117: KeelVault.register(address,address,bytes32,bytes32,uint256) (src/KeelVault.sol#117-135) uses timestamp for comparisons","passed":false}],"detail":"static analysis found arbitrary-send-erc20 at src/KeelRouter.sol:110: KeelRouter.unlockCallback(bytes) (src/KeelRouter.sol#110-133) uses arbitrary from in transferFrom: IERC20(r.tokenIn).safeTransferFrom(r.payer,address(poolManager),r.amountIn) (src/KeelRouter.sol#127)","evaluation":"checks","profile":"foundry","status":"rejected","submissionHash":"525e1356eef29da1dcfd6efe9dacb9616d17e1ff0e60cd7b905e489c18f1086d","verifiedTreeHash":"7a1798e94310821c136a6bf9422f65eb14c77fc0","verifierVersion":"0.1.0+fdeb4d4a"},{"checks":[{"durationMs":31501,"exitCode":0,"name":"build","output":"Compiling 115 files with Solc 0.8.26\nSolc 0.8.26 finished in 31.27s\nCompiler run successful!\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/libraries/Metadata.sol:112:43\n    │\n112 │         if (i == b.length || b[i] != '\"') revert InvalidMetadata();\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/libraries/Metadata.sol:117:34\n    │\n117 │             if (uint8(c) < 0x20) revert InvalidMetadata();\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/libraries/Metadata.sol:119:36\n    │\n119 │                 if (i == b.length) revert InvalidMetadata();\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/libraries/Metadata.sol:122:43\n    │\n122 │                     if (i + 4 > b.length) revert InvalidMetadata();\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/libraries/Metadata.sol:126:29\n    │\n126 │ …                     revert InvalidMetadata();\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/libraries/Metadata.sol:133:21\n    │\n133 │                     revert InvalidMetadata();\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/libraries/Metadata.sol:144:41\n    │\n144 │         if (lead < 0xc2 || lead > 0xf4) revert InvalidMetadata();\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/libraries/Metadata.sol:146:38\n    │\n146 │         if (start + size > b.length) revert InvalidMetadata();\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/libraries/Metadata.sol:151:11\n    │\n151 │         ) revert InvalidMetadata();\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/libraries/Metadata.sol:154:61\n    │\n154 │             if (continuation < 0x80 || continuation > 0xbf) revert InvalidMetadata();\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/libraries/Metadata.sol:139:9\n    │\n139 │         revert InvalidMetadata();\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/libraries/Metadata.sol:21:47\n   │\n21 │             if (i == b.length || b[i] != \":\") revert InvalidMetadata();\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/libraries/Metadata.sol:28:37\n   │\n28 │                 if (i == start + 2) revert InvalidMetadata();\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/libraries/Metadata.sol:48:60\n   │\n48 │         if (i == b.length || (b[i] != \"{\" && b[i] != \"[\")) revert InvalidMetadata();\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/libraries/Metadata.sol:56:51\n   │\n56 │                 if (i == b.length || b[i] != \":\") revert InvalidMetadata();\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/libraries/Metadata.sol:60:32\n   │\n60 │             if (i == b.length) revert InvalidMetadata();\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/libraries/Metadata.sol:62:30\n   │\n62 │             if (b[i] != \",\") revert InvalidMetadata();\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/libraries/Metadata.sol:65:9\n   │\n65 │         revert InvalidMetadata();\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/libraries/Metadata.sol:69:41\n   │\n69 │         if (i == b.length || depth > 8) revert InvalidMetadata();\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/libraries/Metadata.sol:79:55\n   │\n79 │                     if (i == b.length || b[i] != \":\") revert InvalidMetadata();\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/libraries/Metadata.sol:83:36\n   │\n83 │                 if (i == b.length) revert InvalidMetadata();\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/libraries/Metadata.sol:85:34\n   │\n85 │                 if (b[i] != \",\") revert InvalidMetadata();\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/libraries/Metadata.sol:88:13\n   │\n88 │             revert InvalidMetadata();\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/libraries/Metadata.sol:94:45\n   │\n94 │         if (i == b.length || !_digit(b[i])) revert InvalidMetadata();\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/libraries/Metadata.sol:99:49\n   │\n99 │             if (i == b.length || !_digit(b[i])) revert InvalidMetadata();\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/libraries/Metadata.sol:105:49\n    │\n105 │             if (i == b.length || !_digit(b[i])) revert InvalidMetadata();\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/libraries/Metadata.sol:35:36\n   │\n35 │             if (flags & flag != 0) revert InvalidMetadata();\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/libraries/Metadata.sol:38:32\n   │\n38 │             if (i == b.length) revert InvalidMetadata();\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/libraries/Metadata.sol:40:30\n   │\n40 │             if (b[i] != \",\") revert InvalidMetadata();\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/libraries/Metadata.sol:42:47\n   │\n42 │             if (i == b.length || b[i] == \"}\") revert InvalidMetadata();\n   │                                               ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/Metadata.sol:50:31\n   │\n50 │         bytes1 end = object ? bytes1(\"}\") : bytes1(\"]\");\n   │                               ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' 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/libraries/Metadata.sol:50:45\n   │\n50 │         bytes1 end = object ? bytes1(\"}\") : bytes1(\"]\");\n   │                                             ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' 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/libraries/Metadata.sol:73:35\n   │\n73 │             bytes1 end = object ? bytes1(\"}\") : bytes1(\"]\");\n   │                                   ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' 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/libraries/Metadata.sol:73:49\n   │\n73 │             bytes1 end = object ? bytes1(\"}\") : bytes1(\"]\");\n   │                                                 ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' 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/KeelHook.sol:175:9\n    │\n175 │         emit FeePending(token, amount, pending[token]);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelHook.sol:121:81\n    │\n121 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(SafeCast.toInt128(int256(fee)), 0), 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelHook.sol:136:49\n    │\n136 │                     != params.amountSpecified + int256(_specifiedFee(params.amountSpecified))\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/KeelHook.sol:143:51\n    │\n143 │         uint256 fee = imdDelta < 0 ? Math.ceilDiv(uint256(-imdDelta), 99) : uint256(imdDelta) / 100;\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/KeelHook.sol:143:77\n    │\n143 │         uint256 fee = imdDelta < 0 ? Math.ceilDiv(uint256(-imdDelta), 99) : uint256(imdDelta) / 100;\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/KeelHook.sol:145:62\n    │\n145 │         return (IHooks.afterSwap.selector, SafeCast.toInt128(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[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/KeelHook.sol:156:31\n    │\n156 │         bytes memory result = poolManager.unlock(abi.encode(amount));\n    │                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/KeelHook.sol:159:9\n    │\n159 │         IKeelVault(vault).accrue(token, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelVault.sol:141:25\n    │\n141 │             creatorBps: uint16(creatorBps),\n    │                         ━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint16' 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/KeelVault.sol:142:25\n    │\n142 │             launchedAt: uint64(block.timestamp),\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[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/KeelFactory.sol:105:9\n    │\n105 │         emit HookDeployed(deployed, salt, attempts);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/KeelFactory.sol:97:23\n   │\n97 │             keccak256(abi.encodePacked(type(KeelHook).creationCode, abi.encode(address(this), vault_)));\n   │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/KeelVault.sol:177:9\n    │\n177 │         emit FeeAccrued(token, amount, team, creator, basket);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/KeelVault.sol:184:9\n    │\n184 │         IKeelHook(hook).distribute(token);\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/KeelVault.sol:187:9\n    │\n187 │         emit CreatorShareLowered(token, previous, newBps);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelVault.sol:186:24\n    │\n186 │         p.creatorBps = uint16(newBps);\n    │                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint16' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/KeelVault.sol:192:9\n    │\n192 │         IKeelHook(hook).distribute(token);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/KeelVault.sol:190:78\n    │\n190 │     function setCreatorActive(address token, bool active) external onlyOwner nonReentrant {\n    │                                                                              ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/KeelVault.sol:194:9\n    │\n194 │         emit CreatorStatusChanged(token, active);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/KeelVault.sol:223:13\n    │\n223 │         if (imd.balanceOf(address(this)) != beforeBalance + amount) revert Insolvent();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/KeelVault.sol:254:9\n    │\n254 │ ┏         emit CloseAnnounced(\n255 │ ┃             token,\n256 │ ┃             block.timestamp,\n257 │ ┃             Math.max(block.timestamp + CLOSE_NOTICE, uint256(p.launchedAt) + MIN_LIFETIME)\n258 │ ┃         );\n    │ ┗━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelVault.sol:253:25\n    │\n253 │         p.closeNotice = uint64(block.timestamp);\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[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/KeelVault.sol:274:9\n    │\n274 │         IKeelHook(hook).distribute(token);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/KeelVault.sol:268:54\n    │\n268 │     function close(address token) external onlyOwner nonReentrant {\n    │                                                      ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/KeelVault.sol:271:35\n    │\n271 │             p.closeNotice == 0 || block.timestamp < uint256(p.closeNotice) + CLOSE_NOTICE\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/KeelVault.sol:272:20\n    │\n272 │                 || block.timestamp < uint256(p.launchedAt) + MIN_LIFETIME || block.number == 0\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/KeelVault.sol:284:9\n    │\n284 │         emit Closed(token, snapshot, p.poolAtClose, circulating);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/KeelVault.sol:304:9\n    │\n304 │         emit Claimed(token, msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/V4Quote.sol:56:33\n   │\n56 │             if (++steps > 4096) revert QuoteTooComplex();\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/KeelVault.sol:319:45\n    │\n319 │     function claimTeam() external onlyOwner nonReentrant returns (uint256 amount) {\n    │                                             ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/libraries/V4Quote.sol:75:37\n   │\n75 │                     if (gross == 0) revert InvalidQuote();\n   │                                     ━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/libraries/V4Quote.sol:40:37\n   │\n40 │         if (amount == 0 || amount > uint256(uint128(type(int128).max))) revert InvalidQuote();\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/libraries/V4Quote.sol:40:45\n   │\n40 │         if (amount == 0 || amount > uint256(uint128(type(int128).max))) revert InvalidQuote();\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/libraries/V4Quote.sol:66:18\n   │\n66 │                 -int256(s.remaining),\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/KeelFactory.sol:167:13\n    │\n167 │         if (block.timestamp > deadline) revert Expired();\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/KeelFactory.sol:231:9\n    │\n231 │ ┏         emit Launched(\n232 │ ┃             token,\n233 │ ┃             msg.sender,\n234 │ ┃             jobHash,\n    ‡ ┃\n245 │ ┃             metadata\n246 │ ┃         );\n    │ ┗━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/KeelFactory.sol:205:13\n    │\n205 │         if (IERC20(token).balanceOf(address(this)) != C.SUPPLY) revert InvalidLiquidity();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelFactory.sol:244:13\n    │\n244 │             uint128(liquidity),\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/KeelHook.sol:190:29\n    │\n190 │         return amount < 0 ? uint256(-amount) / 100 : Math.ceilDiv(uint256(amount), 99);\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/KeelHook.sol:190:67\n    │\n190 │         return amount < 0 ? uint256(-amount) / 100 : Math.ceilDiv(uint256(amount), 99);\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/KeelFactory.sol:278:26\n    │\n278 │         uint256 amount = uint256(-int256(delta.amount0()));\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[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/KeelFactory.sol:291:31\n    │\n291 │                 if (c != \"-\") revert InvalidUUID();\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/KeelFactory.sol:293:17\n    │\n293 │                 revert InvalidUUID();\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/KeelRouter.sol:68:78\n   │\n68 │     function setEthPool(uint256 fee, uint256 tickSpacing) external onlyOwner nonReentrant {\n   │                                                                              ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/KeelRouter.sol:82:9\n   │\n82 │         emit EthPoolSet(PoolId.unwrap(key.toId()), uint24(fee), int24(uint24(tickSpacing)));\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/KeelRouter.sol:69:65\n   │\n69 │         if (fee >= 1000000 || tickSpacing == 0 || tickSpacing > uint256(uint16(type(int16).max))) {\n   │                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/KeelRouter.sol:69:73\n   │\n69 │         if (fee >= 1000000 || tickSpacing == 0 || tickSpacing > uint256(uint16(type(int16).max))) {\n   │                                                                         ━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' 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/KeelRouter.sol:72:38\n   │\n72 │         PoolKey memory key = _ethKey(uint24(fee), int24(uint24(tickSpacing)));\n   │                                      ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint24' 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/KeelRouter.sol:72:51\n   │\n72 │         PoolKey memory key = _ethKey(uint24(fee), int24(uint24(tickSpacing)));\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[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/KeelRouter.sol:72:57\n   │\n72 │         PoolKey memory key = _ethKey(uint24(fee), int24(uint24(tickSpacing)));\n   │                                                         ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint24' 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/KeelRouter.sol:79:22\n   │\n79 │         ethPoolFee = uint24(fee);\n   │                      ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint24' 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/KeelRouter.sol:80:30\n   │\n80 │         ethPoolTickSpacing = int24(uint24(tickSpacing));\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[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/KeelRouter.sol:80:36\n   │\n80 │         ethPoolTickSpacing = int24(uint24(tickSpacing));\n   │                                    ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint24' 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/KeelRouter.sol:82:52\n   │\n82 │         emit EthPoolSet(PoolId.unwrap(key.toId()), uint24(fee), int24(uint24(tickSpacing)));\n   │                                                    ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint24' 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/KeelRouter.sol:82:65\n   │\n82 │         emit EthPoolSet(PoolId.unwrap(key.toId()), uint24(fee), int24(uint24(tickSpacing)));\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[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/KeelRouter.sol:82:71\n   │\n82 │         emit EthPoolSet(PoolId.unwrap(key.toId()), uint24(fee), int24(uint24(tickSpacing)));\n   │                                                                       ━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint24' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/KeelRouter.sol:114:32\n    │\n114 │         amountOut = abi.decode(poolManager.unlock(data), (uint256));\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/KeelRouter.sol:94:13\n   │\n94 │         if (block.timestamp > deadline) revert Expired();\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/KeelRouter.sol:116:9\n    │\n116 │         emit Swapped(msg.sender, tokenIn, tokenOut, recipient, amountIn, amountOut);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[incorrect-strict-equality]: dangerous strict equality check on an externally-influenced value\n    ╭▸ src/KeelRouter.sol:108:17\n    │\n108 │             if (IERC20(tokenIn).balanceOf(address(this)) != beforeBalance + amountIn) {\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/incorrect-strict-equality\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelRouter.sol:162:24\n    │\n162 │         if (amountIn > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelRouter.sol:162:32\n    │\n162 │         if (amountIn > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                                ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelRouter.sol:169:35\n    │\n169 │                 amountSpecified: -int256(amountIn),\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/KeelRouter.sol:176:31\n    │\n176 │         if (int256(input) != -int256(amountIn) || output <= 0) revert PartialFill();\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/KeelRouter.sol:177:16\n    │\n177 │         return uint256(uint128(output));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelRouter.sol:177:24\n    │\n177 │         return uint256(uint128(output));\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/KeelRouter.sol:203:41\n    │\n203 │         if (amountIn == 0 || amountIn > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/KeelRouter.sol:203:49\n    │\n203 │         if (amountIn == 0 || amountIn > uint256(uint128(type(int128).max))) revert InvalidAmount();\n    │                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.chainid` may be reused across `vm.chainId`\n   ╭▸ test/KeelFactory.t.sol:56:33\n   │\n56 │         uint256 originalChain = block.chainid;\n   │                                 ━━━━━━━━━━━━━\n57 │         vm.chainId(originalChain + 1);\n   │         ───────────────────────────── `vm.chainId` changes this environment here\n   │\n   ├ help: capture it with `vm.getChainId()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/KeelFactory.t.sol:77:63\n    │\n 77 │         factory.launch(\"n\", \"s\", JOB, PROJECT, 1000, -100000, block.timestamp, \"\", META);\n    │                                                               ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/helpers/KeelTestBase.sol:118:9\n    │\n118 │         vm.warp(block.timestamp + 31 days);\n    │         ────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/helpers/KeelTestBase.sol:118:17\n    │\n118 │         vm.warp(block.timestamp + 31 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/helpers/KeelTestBase.sol:102:62\n    │\n102 │         out = router.swapExactInput(C.IMD, token, amount, 1, block.timestamp, who);\n    │                                                              ━━━━━━━━━━━━━━━\n    ‡\n118 │         vm.warp(block.timestamp + 31 days);\n    │         ────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/KeelVault.t.sol:154:17\n    │\n154 │         vm.roll(block.number + 1);\n    │         ────────━━━━━━━━━━━━───── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/helpers/KeelTestBase.sol:102:62\n    │\n102 │         out = router.swapExactInput(C.IMD, token, amount, 1, block.timestamp, who);\n    │                                                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/KeelVault.t.sol:163:9\n    │\n163 │         vm.warp(block.timestamp + 30 days);\n    │         ────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/KeelVault.t.sol:163:17\n    │\n163 │         vm.warp(block.timestamp + 30 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/KeelVault.t.sol:227:64\n    │\n227 │         factory.launch(\"n\", \"s\", JOB2, PROJECT, 3000, -100000, block.timestamp, \"\", META);\n    │                                                                ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/helpers/KeelTestBase.sol:118:9\n    │\n118 │         vm.warp(block.timestamp + 31 days);\n    │         ────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":2431,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 4 tests for test/Metadata.t.sol:MetadataTest\n[PASS] testMalformedAndWrongSchemaRejected() (gas: 375897)\n[PASS] testMalformedUTF8RejectedAndUnicodeAccepted() (gas: 84369)\n[PASS] testMax1024BytesAndDepthLimit() (gas: 438512)\n[PASS] testRequiredFieldsObjectAndArrayLinksAndExtraJSON() (gas: 107463)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.03ms (1.70ms CPU time)\n\nRan 1 test for test/KeelSetup.t.sol:EmptyChainDeploymentTest\n[PASS] testConstructorsOnlyDeployOnEmptyChain() (gas: 74860)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.39ms (1.28ms CPU time)\n\nRan 2 tests for test/KeelSetup.t.sol:KeelSetupTest\n[PASS] testEmptyClaimsAndUnknownTokensRejected() (gas: 176460)\n[PASS] testRegistrationUnavailableUntilWireAndWireIsOwnerOnlyOnce() (gas: 302420)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 7.27ms (323.06µs CPU time)\n\nRan 9 tests for test/KeelFactory.t.sol:KeelFactoryTest\n[PASS] testConstructorDeploymentGasAndRuntimeSizes() (gas: 6085512)\nLogs:\n  Factory constructor gas (including mining + hook deployment): 6000580\n  Hook salt candidates: 9434\n\n[PASS] testFactoryStartsPausedAndSettingsRequired() (gas: 561080)\n[PASS] testLaunchValidationExpiryUUIDBoundsAndMetadata() (gas: 669989)\n[PASS] testNonAlignedNegativeStartTickRoundsStrictlyUp() (gas: 931200)\n[PASS] testOwnerControlsAndTwoStepTransferForAllThreeContracts() (gas: 430882)\n[PASS] testPermitRejectsChangedCreatorAndSignedFields() (gas: 1091087)\n[PASS] testPermitReplayRejectedAcrossChainAndFactory() (gas: 468814)\n[PASS] testSignerCanBe1271ContractAndCreatorChoosesUnsignedShare() (gas: 1160111)\n[PASS] testUniqueJobEvenWithDifferentCreatorAndNewPermit() (gas: 413282)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 8.35ms (16.61ms CPU time)\n\nRan 4 tests for test/KeelToken.t.sol:KeelTokenTest\n[PASS] testCheckpointsCollapseWithinBlockAndExcludeCurrentBlock() (gas: 501417)\n[PASS] testFuzzPlainTransferFromAndPastBalances(uint96) (runs: 256, μ: 281311, ~: 282059)\nLogs:\n  Bound result 105064954416244678981186365\n\n[PASS] testInvalidInitializationDoesNotConsumeClone() (gas: 353355)\n[PASS] testTemplateLockedCloneInitializesOnceAndFixedSupply() (gas: 161081)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 10.65ms (8.03ms CPU time)\n\nRan 11 tests for test/KeelIntegration.t.sol:KeelIntegrationTest\n[PASS] testBuySellQuoteAndFeeConservation() (gas: 1082086)\n[PASS] testCloseFlushesFeesThenNewFeesPayCreatorEvenInactive() (gas: 1819966)\n[PASS] testDistributionFailureRestoresPendingClaimsAndAccounting() (gas: 818529)\n[PASS] testFuzzFeeSplitConservesEveryWei(uint96,uint16,bool) (runs: 256, μ: 1560553, ~: 1563471)\nLogs:\n  Bound result 24123599\n  Bound result 16\n\n[PASS] testFuzzQuoteMatchesBuySell(uint96) (runs: 256, μ: 981574, ~: 981968)\nLogs:\n  Bound result 2403563036210273670531\n\n[PASS] testHookAndFactoryCallbacksRejectDirectCall() (gas: 123177)\n[PASS] testInactiveCreatorAndReactivationDistributeUnderPreviousStatus() (gas: 1455929)\n[PASS] testLaunchLocksSupplyAndMinesExactHookBits() (gas: 122940)\n[PASS] testLowerShareDistributesAtOldShare() (gas: 1117391)\n[PASS] testNoAllowanceTheftThroughCallback() (gas: 447966)\n[PASS] testRouterRejectsExpiredSlippageAndInvalidRoutes() (gas: 478887)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 93.85ms (165.09ms CPU time)\n\nRan 16 tests for test/KeelVault.t.sol:KeelVaultTest\n[PASS] testBuildingOnlyActionsAndUnauthorizedClaims() (gas: 525279)\n[PASS] testCloseAtExactThirtyDaysWithMatureNotice() (gas: 187679)\n[PASS] testCloseRequiresBothTimingThresholdsAndCancellationRestartsNotice() (gas: 465709)\n[PASS] testDeadBalanceExcludedAndClaimsRoundDown() (gas: 1344137)\n[PASS] testFuzzClaimsNeverExceedPool(uint96,uint16) (runs: 256, μ: 1218425, ~: 1218276)\nLogs:\n  Bound result 51056\n  Bound result 287\n\n[PASS] testIMDFailureRollsBackAccountingAndJobConsumption() (gas: 462635)\n[PASS] testInactiveStopsReimburseButAllowsRecordWithFundedBasket() (gas: 401127)\n[PASS] testOneBuildingTokenUntilCloseThenRelaunch() (gas: 1235572)\n[PASS] testPartialReimbursementBelowRunPriceAndRecordThreshold() (gas: 306518)\n[PASS] testReentrancyOnPayoutCannotWithdrawBasket() (gas: 479895)\n[PASS] testReimbursePaysCreatorAndEmitsBothEvents() (gas: 376178)\n[PASS] testRunPriceBoundsAndVerifierRevocation() (gas: 375651)\n[PASS] testRunRestrictionsAndAmountCap() (gas: 703711)\n[PASS] testSharedJobRefAcrossTokensAndRunMethods() (gas: 1326918)\n[PASS] testSnapshotExcludesTransferAndPurchaseInCloseBlock() (gas: 1530815)\n[PASS] testZeroCirculatingAndExcludedAddressesCannotClaim() (gas: 466991)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 93.90ms (56.75ms CPU time)\n\nRan 10 tests for test/KeelSwaps.t.sol:KeelSwapsTest\n[PASS] testETHAndIMDDirectRoutesAndValueValidation() (gas: 824135)\n[PASS] testETHUnconfiguredThenConfiguredBuyAndSellWithQuote() (gas: 1510606)\n[PASS] testExactInputSellFeeComesFromGrossIMDOutput() (gas: 778923)\n[PASS] testExactOutputBuyChargesIMDOnTop() (gas: 628401)\n[PASS] testExactOutputSellDeliversNetIMDAfterFee() (gas: 979144)\n[PASS] testOnlyFactoryCanInitializeAndAddLiquidityAndPositionCannotBeRemoved() (gas: 177081)\n[PASS] testQuotesIncludeProtocolFeeAndCrossMultipleRanges() (gas: 1623031)\n[PASS] testRouterTransferCallbackCannotReenter() (gas: 626443)\n[PASS] testSpecifiedIMDPartialFillRevertsAtomically() (gas: 216502)\n[PASS] testTokenToTokenRoutesThroughIMDAndAccruesToBothProjects() (gas: 2203936)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 383.52ms (7.98ms CPU time)\n\nRan 2 tests for test/KeelFactory.t.sol:HookMinerTest\n[PASS] testFuzzMinerAddressAndMeasuredGas(bytes32,uint32) (runs: 256, μ: 2391211, ~: 1803512)\n[PASS] testLogMinerGas() (gas: 794033)\nLogs:\n  Salt search gas: 784591\n  Salt candidates: 5322\n  Gas per candidate: 147\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 521.79ms (518.20ms CPU time)\n\nRan 2 tests for test/KeelInvariant.t.sol:KeelInvariantTest\n[PASS]\nKeelInvariantTest invariants:\n[PASS] invariantBalanceCoversEveryLiabilityAndFundsAreConserved\n[PASS] invariantClaimsNeverExceedFrozenPoolAndSharesOnlyFall\n[PASS] invariantHookPendingFullyBackedAndRouterHoldsNothing\n[PASS] invariantTokenSupplyAndKnownHoldersConserved\n KeelInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------+-----------------+-------+---------+----------╮\n| Contract    | Selector        | Calls | Reverts | Discards |\n+============================================================+\n| KeelHandler | advance         | 745   | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| KeelHandler | claim           | 655   | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| KeelHandler | deposit         | 694   | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| KeelHandler | distribute      | 657   | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| KeelHandler | lowerShare      | 685   | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| KeelHandler | noticeOrClose   | 646   | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| KeelHandler | run             | 730   | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| KeelHandler | setActive       | 663   | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| KeelHandler | setPrice        | 659   | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| KeelHandler | trade           | 701   | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| KeelHandler | transfer        | 699   | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| KeelHandler | withdrawAccrued | 658   | 0       | 0        |\n╰-------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1055239860\n  Bound result 6861\n  Bound result 8107728690312\n  Bound result 3053864353656\n  Bound result 1900000000000002271\n  Bound result 2091755963847013609\n  Bound result 99999999999000000011\n  Bound result 13044\n  Bound result 869068668\n  Bound result 518286068410774172\n  Bound result 950418\n  Bound result 1900000002813059832\n  Bound result 7258\n  Bound result 956154\n  Bound result 1900000418430673767\n  Bound result 827948876420946378\n  Bound result 958401\n  Bound result 2478893113\n  Bound result 950497\n  Bound result 999977456122630266\n  Bound result 341545\n  Bound result 7543\n  Bound result 10435\n  Bound result 0\n  Bound result 1900000000013453018\n  Bound result 950411\n  Bound result 1262903698036538255\n  Bound result 10601756628\n  Bound result 8978\n  Bound result 1900000000000012741\n  Bound result 1065910164745360289887238\n\n[PASS] testHandlerLifecycleExercisesPaymentsRecordsAndClosedClaims() (gas: 2026940)\nLogs:\n  Bound result 1000000000000000000\n  Bound result 500000000000000000\n  Bound result 100000000000000000000\n  Bound result 1036800\n  Bound result 1036800\n  Bound result 604800\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 2.29s (2.29s CPU time)\n\nRan 10 test suites in 2.29s (3.41s CPU time): 61 tests passed, 0 failed, 0 skipped (61 total tests)\n","passed":true},{"durationMs":52,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"KeelFactory.acceptOwnership()\",\"KeelFactory.launch(string,string,string,string,uint256,int24,uint256,bytes,string)\",\"KeelFactory.renounceOwnership()\",\"KeelFactory.revokeSigner()\",\"KeelFactory.setPaused(bool)\",\"KeelFactory.setSigner(address)\",\"KeelFactory.setStartTickBounds(int24,int24)\",\"KeelFactory.transferOwnership(address)\",\"KeelFactory.unlockCallback(bytes)\",\"KeelHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"KeelHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"KeelHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"KeelHook.distribute(address)\",\"KeelHook.unlockCallback(bytes)\",\"KeelRouter.acceptOwnership()\",\"KeelRouter.renounceOwnership()\",\"KeelRouter.setEthPool(uint256,uint256)\",\"KeelRouter.swapExactInput(address,address,uint256,uint256,uint256,address)\",\"KeelRouter.transferOwnership(address)\",\"KeelRouter.unlockCallback(bytes)\",\"KeelToken.approve(address,uint256)\",\"KeelToken.initialize(string,string,address)\",\"KeelToken.transfer(address,uint256)\",\"KeelToken.transferFrom(address,address,uint256)\",\"KeelVault.acceptOwnership()\",\"KeelVault.accrue(address,uint256)\",\"KeelVault.announceClose(address)\",\"KeelVault.cancelClose(address)\",\"KeelVault.claim(address)\",\"KeelVault.claimCreator(address)\",\"KeelVault.claimTeam()\",\"KeelVault.close(address)\",\"KeelVault.deposit(address,uint256)\",\"KeelVault.lowerCreatorShare(address,uint256)\",\"KeelVault.recordRun(address,bytes32,bytes32)\",\"KeelVault.register(address,address,bytes32,bytes32,uint256)\",\"KeelVault.reimburse(address,bytes32,bytes32,uint256)\",\"KeelVault.renounceOwnership()\",\"KeelVault.revokeVerifier()\",\"KeelVault.setCreatorActive(address,bool)\",\"KeelVault.setRunPrice(uint256)\",\"KeelVault.setVerifier(address)\",\"KeelVault.transferOwnership(address)\",\"KeelVault.wire(address)\"],\"files\":{\".gitignore\":4,\"README.md\":154,\"docs/VALIDATION.md\":73,\"docs/dependencies.json\":29,\"foundry.toml\":21,\"remappings.txt\":4,\"src/KeelFactory.sol\":297,\"src/KeelHook.sol\":192,\"src/KeelRouter.sol\":212,\"src/KeelToken.sol\":64,\"src/KeelVault.sol\":347,\"src/interfaces/IKeel.sol\":36,\"src/libraries/HookMiner.sol\":29,\"src/libraries/KeelConstants.sol\":13,\"src/libraries/Metadata.sol\":167,\"src/libraries/V4Quote.sol\":108,\"test/KeelFactory.t.sol\":243,\"test/KeelIntegration.t.sol\":204,\"test/KeelInvariant.t.sol\":291,\"test/KeelSetup.t.sol\":68,\"test/KeelSwaps.t.sol\":192,\"test/KeelToken.t.sol\":86,\"test/KeelVault.t.sol\":302,\"test/Metadata.t.sol\":78,\"test/helpers/KeelTestBase.sol\":128,\"test/helpers/MockIMD.sol\":45,\"test/helpers/V4Actor.sol\":64},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":10171,"exitCode":0,"name":"slither","output":"[medium/high] incorrect-equality at src/KeelToken.sol:57: KeelToken._checkpoint(address) (src/KeelToken.sol#57-63) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/KeelVault.sol:268: KeelVault.close(address) (src/KeelVault.sol#268-285) uses a dangerous strict equality:\n[low/medium] reentrancy-benign at src/KeelFactory.sol:152: Reentrancy in KeelFactory.launch(string,string,string,string,uint256,int24,uint256,bytes,string) (src/KeelFactory.sol#152-247):\n[low/medium] reentrancy-benign at src/KeelVault.sol:268: Reentrancy in KeelVault.close(address) (src/KeelVault.sol#268-285):\n[low/medium] reentrancy-benign at src/KeelFactory.sol:152: Reentrancy in KeelFactory.launch(string,string,string,string,uint256,int24,uint256,bytes,string) (src/KeelFactory.sol#152-247):\n[low/medium] timestamp at src/KeelRouter.sol:86: KeelRouter.swapExactInput(address,address,uint256,uint256,uint256,address) (src/KeelRouter.sol#86-117) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelVault.sol:268: KeelVault.close(address) (src/KeelVault.sol#268-285) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelVault.sol:123: KeelVault.register(address,address,bytes32,bytes32,uint256) (src/KeelVault.sol#123-155) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelFactory.sol:152: KeelFactory.launch(string,string,string,string,uint256,int24,uint256,bytes,string) (src/KeelFactory.sol#152-247) uses timestamp for comparisons","passed":true},{"durationMs":1017,"exitCode":0,"name":"aderyn","output":"[high] abi-encode-packed-hash-collision at src/KeelFactory.sol:97: `abi.encodePacked()` Hash Collision\n[high] contract-locks-ether at src/KeelRouter.sol:22: Contract locks Ether without a withdraw function\n[high] incorrect-shift-order at src/libraries/HookMiner.sol:18: Incorrect Assembly Shift Parameter Order\n[high] reentrancy-state-change at src/KeelFactory.sol:164: Reentrancy: State change after external call (16 places)\n[low] centralization-risk at src/KeelFactory.sol:29: Centralization Risk (17 places)\n[low] large-numeric-literal at src/KeelRouter.sol:36: Large Numeric Literal (4 places)\n[low] literal-instead-of-constant at src/KeelFactory.sol:287: Literal Instead of Constant (39 places)\n[low] non-reentrant-not-first at src/KeelRouter.sol:68: `nonReentrant` is Not the First Modifier (4 places)\n[low] require-revert-in-loop at src/KeelFactory.sol:288: Loop Contains `require`/`revert` (8 places)\n[low] state-change-without-event at src/KeelFactory.sol:260: State Change Without Event (4 places)\n[low] unchecked-return at src/KeelHook.sol:101: Unchecked Return","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"697ae96282503bb0c6c30d47cd652fc355b643c087058799dd55518179f8502e","verifiedTreeHash":"c72dd5ec3153942bba81c7d0db9040df551a7929","verifierVersion":"0.1.0+fdeb4d4a"}]}