{"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":"f16cb00c-f00d-4fb2-833f-de627e488f57","kind":"skill:build-contract-project","nodes":[{"acceptedSubmissionHash":"ae410e119ea5dc3e67847be61d257fc151a00a66b4daadb384464972fd46dc31","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"d59561da3b378461b7c749f6bd5084477d44993aa94972ddea90f542535751df","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"d59561da3b378461b7c749f6bd5084477d44993aa94972ddea90f542535751df","skillId":"build-contract-project","state":"accepted"}],"objective":"Keel v2 contracts, part 2a: the heart, pulse fees, launch permit and launch fee. Continue the existing Keel v2 project (part 1: KeelV2Factory, KeelV2Hook, KeelV2Token, FixedRateModule). Nothing is deployed yet, so change part 1 code directly where needed. Keep everything immutable: no proxies, no upgrade paths, no sweep or rescue functions, no admin power over funds already credited. Keep Solidity 0.8.26, Cancun, via IR, bytecode_hash none, vendored deps, offline tests.\n\n1. Router and heart wiring (factory). Add set-once owner functions setRouter(address) and setHeart(address), same pattern as setTakeoverContract (nonzero, has code, cannot be changed or cleared). Add views router() and heart().\n\n2. Launch permit and launch fee (factory). launch becomes payable and takes a permit: launch(name, symbol, metadata, quote, startTick, baseFeeBps, deadline, signature). Owner sets signer via setSigner(address) (replaceable, nonzero) and launchFee via setLaunchFee(uint256), default 0.0001 ether, max 0.01 ether. Public views: signer(), launchFee(), nonces(address creator). Require msg.value == launchFee, forward it to teamRecipient in the same call (revert if the transfer fails). Require block.timestamp <= deadline and a valid EIP-712 signature by signer over Launch(address creator, address quote, int24 startTick, uint256 deadline, uint256 nonce) with domain name \"Keel\", version \"2\", chainId, verifyingContract = factory; creator = msg.sender; per-creator nonce increments on use. Keep the existing per-quote tick bounds check. The off-chain signer picks the tick so every token starts at a $5,000 market cap.\n\n3. Buyer identity (hook). In afterSwap, a buy counts for the heart only when sender == factory.router() and hookData is exactly 32 bytes; buyer = abi.decode(hookData, (address)). Any other caller still trades normally, with no heart credit. hookData keeps being forwarded to the module unchanged.\n\n4. Pulse (hook). Each pool keeps a decaying buy counter: pulse (uint128, 1e18 = one buy) and pulseUpdatedAt. On every buy whose gross quote input is at least heart.minBuy(quote) (or every buy while heart is unset), set pulse = pulse * 2^(-elapsed / 1 hour) + 1e18. Add view pulseOf(bytes32 poolId) returning the decayed value now. Use a fixed-point exp2 that cannot overflow or revert for any elapsed time (cap elapsed at 64 half-lives, then zero).\n\n5. Heart call (hook). After a counted buy, call heart.onBuy(token, quote, buyer, grossQuoteIn, tokensOut) with a fixed gas stipend (200k) inside try/catch. A failing or out-of-gas heart must never revert or change the swap. Emit HeartCallFailed(poolId) on failure. tokensOut is the token amount the swap delivered.\n\n6. PulseRateModule (new IRulesModule). Same creator controls as FixedRateModule (lowerRate, scheduleRaise 24h +1% max, applyRaise, cancelRaise, creatorAction). feeBps(poolId) = 100 + (rate - 100) * min(pulse, HOT) / HOT, with HOT = 6e18 and rate = the creator's current rate (100..500). Result always in 100..500. View only: reads hook.pulseOf. The anti-snipe decay in the hook stays as it is.\n\n7. KeelHeart (new contract). One heartbeat for all v2 pools.\n- Constructor(hook, owner). Owner (two-step ownership) may only setMinBuy(quote, rawAmount) and add quotes to a list of at most 16. No other powers, no withdrawal.\n- onBuy only from hook. Ignore if grossQuoteIn < minBuy[quote] or minBuy[quote] == 0. If the current round is over (deadline != 0 and now >= deadline), end it first. If no round is running, start one: deadline = now + 300. Otherwise deadline = min(deadline + 30, now + 600). Record lastBuyer, lastToken, lastTokens = tokensOut, round number. Emit Beat(round, buyer, token, quote, grossQuoteIn, deadline).\n- fund(quote, amount): anyone adds to the current pot. ETH via msg.value (quote = address(0)), ERC20 via transferFrom with exact balance-change check. Only listed quotes. Emit Funded(round, quote, from, amount).\n- end(): anyone, once now >= deadline and the round has a buyer. Winner = lastBuyer. Eligible only if lastToken.balanceAt(winner, block.number - 1) >= lastTokens (KeelV2Token from part 1 already keeps per-block checkpoints; keep balanceAt reverting for the current block, so nothing done inside the end() block can count). If eligible: for each listed quote, prize[round][quote] = pot[quote] * 7000 / 10000 and pot keeps the rest for the next round. If not eligible: nothing is awarded, the whole pot carries over. Store the round result (winner, token, endBlock, eligible). Emit Flatline(round, winner, eligible). Next round starts on the next counted buy.\n- claim(round, to): only that round's winner, once, pays every quote's prize. Exact transfer checks, nonReentrant, solvency check: balance of each quote >= pot + unpaid prizes. Emit Claimed(round, winner, to).\n- Exact public views (the site reads these): state() returns (uint256 round, uint256 deadline, address lastBuyer, address lastToken); quoteList() returns (address[]); potOf(address quote); minBuy(address quote); prizeOf(uint256 round, address quote); result(uint256 round) returns (address winner, address token, uint256 endBlock, bool eligible, bool claimed).\n\n8. Tests. Extend the suite: permit and nonce replay, wrong fee, fee forwarding; router-only heart credit and spoofed hookData from a non-router caller; pulse decay math and caps; PulseRateModule bounds at pulse 0, HOT and above; heart timer start, +30 s, 600 s cap, end after deadline, sells never touching the timer, heart revert or gas burn never breaking a swap, winner holding check pass and fail (sold part before end, sold and bought back before end, received tokens by transfer, tokens moved in and out inside the end() block, end() called many blocks after the deadline), 70/30 split per quote, claim once, funding in ETH and ERC20, multiple rounds. Invariants: heart solvent, hook solvent, a swap result is identical with or without a heart.\n\n9. Gas. Measure with forge snapshot and document in the README: a plain swap, a router buy that counts (heart running, round start, round end inside onBuy), a non-router buy, and a sell, each with and without a heart set. Report the extra gas a counted buy costs over part 1. Keep heart.onBuy at most 120k gas on every path, assert it in a test, and keep the 200k stipend as the hard ceiling.\n\nDeliver: updated source, tests, README section for part 2a, REVIEW.md update. Do not deploy.","parentJobId":"f4c6c94f-30c2-45be-8a4d-313ff9cf3b3d","planHash":"e63f5868aa49b41cf3ac03107e287478003f11e261685c2cf4c7f4546a50e57b","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"f4c6c94f-30c2-45be-8a4d-313ff9cf3b3d","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2320-kkkkk"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"52270","feedbackHash":"0822eeb1b5bf61557d2e9dc713a5d327e3b2e75e82d1b16bbdbb7626507dbfd1","nodeKey":"build_contract_project","submissionHash":"ae410e119ea5dc3e67847be61d257fc151a00a66b4daadb384464972fd46dc31","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"5e1195b35a09fcb5decfa716bee95786b1370c627e6f16356bd590db24365920","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"3c7381c849cce4a69661630fa4180ce92206456c2f1a9a729681d32f71375336","device":"f9a12c57a094ca8d","findings":[],"hash":"ae410e119ea5dc3e67847be61d257fc151a00a66b4daadb384464972fd46dc31","nodeId":"b8c02323-d122-416b-8b95-d5d81d218e7a","outcome":"completed","summary":"Done. Keel v2 part 2a is implemented, tested and documented; nothing was deployed or committed.\n\n**What was built**\n\n- `KeelV2Factory`: set-once `setRouter`/`setHeart` with `router()`/`heart()` views, replaceable `setSigner`, `setLaunchFee` (default 0.0001 ether, max 0.01 ether), per-creator `nonces`, and a payable `launch(..., deadline, signature)` that checks an EIP-712 permit (domain `Keel`/`2`, low-s, nonce consumed on use) and forwards the exact fee to `teamRecipient` in the same call. Tick bounds still apply.\n- `KeelV2Hook`: buys from `factory.router()` with exactly 32 bytes of hookData name a buyer; everything else trades normally. Per-pool decaying pulse with `pulseOf`, a saturating no-revert `PulseMath.decay` (64 half-life cap), and `heart.onBuy` under a 200k stipend in try/catch with `HeartCallFailed` on failure. hookData is still forwarded to the module.\n- `PulseRateModule` plus a shared `RateModuleBase` that `FixedRateModule` now inherits unchanged in behaviour.\n- `KeelHeart`: 300 s rounds, +30 s extensions capped at 600 s, previous-block holding check via `balanceAt`, 70/30 split, winner-only single claim with exact transfer and solvency checks, ETH and ERC20 funding, two-step owner limited to `setMinBuy` and a 16-quote list. Pots are split lazily from funding anchors so `onBuy` stays O(1) whatever the quote count.\n\n**Verification**\n\n| Check | Result |\n|---|---|\n| `forge test` with the verifier's fuzz seed | 107 passed, 0 failed, 1 mainnet-only skip |\n| `forge fmt --check` | clean |\n| `forge build --sizes` | all contracts well under the limit |\n| `heart.onBuy` gas, asserted ≤ 120k on every path | worst measured path 119,766 (eligible end with a 400-checkpoint winner) |\n\nTests cover permit replay and binding, wrong fee and fee forwarding failure, router-only credit and spoofed hookData, pulse math and saturation, module bounds at pulse 0, HOT and above, timer start, extension and cap, sells not touching the timer, reverting and gas-burning hearts, every listed winner-holding scenario, 70/30 per quote, claim once, ETH and ERC20 funding, multiple rounds, and invariants for heart solvency, hook solvency and swap identity with or without a heart.\n\n**Worth knowing**\n\n- The README gas table comes from the committed `.gas-snapshot`. A counted buy costs about 85k more than a plain buy. Part 1's exact baseline was not rebuilt from the old commit, so the part 1 delta is stated as an estimate in the README.\n- The eligible-end path sits close to the 120k bound and grows by roughly 2.1k per doubling of the winner's checkpoint history. The 200k stipend remains the hard ceiling and a failing call never affects the swap. This is called out in REVIEW.md.\n- After launch the owner must call `setSigner`, `setRouter`, `setHeart`, `heart.addQuote` and `heart.setMinBuy`; the README lists each setter and where its value comes from. Slither and the mainnet fork run were not executed in this part.","treeHash":"10fa0c685590fdd107be0043cb6fdb329d87a08f","usage":{"cachedInputTokens":4737752,"inputTokens":898,"model":"claude-fable-5-1","outputTokens":117512,"runtime":"claude","turns":76,"wallClockMs":2078982}}],"verification":[{"checks":[{"durationMs":261062,"exitCode":0,"name":"build","output":"Compiling 97 files with Solc 0.8.26\nSolc 0.8.26 finished in 260.82s\nCompiler run successful!\n","passed":true},{"durationMs":1658,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/Math.t.sol:MathTest\n[PASS] testFuzz_bitScansAgainstDivisionReference(uint256) (runs: 256, μ: 14296, ~: 11370)\n[PASS] testFuzz_fullWidthMultiplicationAndDivision(uint256,uint256) (runs: 256, μ: 5355, ~: 5083)\n[PASS] test_allPowerOfTwoBoundaries() (gas: 697936)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 5.98ms (12.50ms CPU time)\n\nRan 4 tests for test/Gas.t.sol:GasWithoutHeartTest\n[PASS] test_gas_nonRouterBuyNamed() (gas: 345311)\n[PASS] test_gas_plainBuyNoHookData() (gas: 319609)\n[PASS] test_gas_routerBuyNamed() (gas: 345370)\n[PASS] test_gas_sell() (gas: 428684)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 64.30ms (2.12ms CPU time)\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP] test_mainnetRealUSDCAndETH() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 64.81ms (74.97µs CPU time)\n\nRan 6 tests for test/Heart.t.sol:HeartWinnerTest\n[PASS] test_endLongAfterDeadlineStillChecksPreviousBlock() (gas: 703049)\n[PASS] test_holderWins() (gas: 690752)\n[PASS] test_movesInsideEndBlockDoNotCount() (gas: 2194952)\n[PASS] test_receivedByTransferCounts() (gas: 1511596)\n[PASS] test_soldAndBoughtBackBeforeEndWins() (gas: 2947357)\n[PASS] test_soldPartBeforeEndLoses() (gas: 1033507)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 76.13ms (11.83ms CPU time)\n\nRan 13 tests for test/Launch.t.sol:LaunchTest\n[PASS] test_bothAddressOrderings() (gas: 35355033)\n[PASS] test_callbackAndUnknownPoolGuards() (gas: 1573131)\n[PASS] test_canDisableQuoteWhoseMetadataBreaks() (gas: 54600)\n[PASS] test_decayModuleAndTakeoverGuards() (gas: 351132)\n[PASS] test_launch6and8and18DecimalQuotes() (gas: 6977962)\n[PASS] test_launchETHWholeSupplyLockedAndCheckpointed() (gas: 1500987)\n[PASS] test_launchValidation() (gas: 699106)\n[PASS] test_outsideAddAndRemoveRevert() (gas: 1549663)\n[PASS] test_ownerGuardsAndTwoStep() (gas: 389904)\n[PASS] test_ownerPowersApplyOnlyToFuturePools() (gas: 2058744)\n[PASS] test_pauseOnlyNewLaunches() (gas: 3291570)\n[PASS] test_quoteClassificationAndBounds() (gas: 2136370)\n[PASS] test_realSwapsCheckpointPoolManagerAndTrader() (gas: 2263912)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 77.11ms (48.58ms CPU time)\n\nRan 9 tests for test/Rules.t.sol:RulesTest\n[PASS] test_allCreatorActionsRefreshActivity() (gas: 1889254)\n[PASS] test_creatorTransferAndPendingRaiseControl() (gas: 1846049)\n[PASS] test_lowerImmediateCancelsPending() (gas: 1592520)\n[PASS] test_moduleImmutablePerPool() (gas: 2977324)\n[PASS] test_pingAndModuleActionDelayTakeover() (gas: 1693072)\n[PASS] test_raiseDelayAndBoundaryAndCancellation() (gas: 1792351)\n[PASS] test_rateGuardsAndUnauthorized() (gas: 1836359)\n[PASS] test_rescheduleRestartsRaiseDelay() (gas: 1644046)\n[PASS] test_takeoverUnknownEarlyBoundaryAndSuccess() (gas: 2702567)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 80.98ms (11.86ms CPU time)\n\nRan 5 tests for test/Token.t.sol:TokenTest\n[PASS] testFuzz_balanceAtMatchesHistory(uint8,uint256) (runs: 256, μ: 2680090, ~: 1846904)\nLogs:\n  Bound result 22\n  Bound result 581036929466753912666728129\n  Bound result 389997752644641764522669255\n  Bound result 176861808946290410620050990\n  Bound result 302411606192306030098381991\n  Bound result 59588323286284405446464168\n  Bound result 57912609018482646341613890\n  Bound result 85244887368356215205874290\n  Bound result 327214361117973715283537825\n  Bound result 77200525250837471911465952\n  Bound result 326446509748246995900430277\n  Bound result 33634771019014586169717442\n  Bound result 23462813558461634846003494\n  Bound result 104308388482452798645682500\n  Bound result 315956410137389131949948423\n  Bound result 81283510174625847898879413\n  Bound result 281349977849230144488721492\n  Bound result 83458356249733226927255265\n  Bound result 154332734163605665110046020\n  Bound result 149077158901455973106093641\n  Bound result 360547104049000858764682662\n  Bound result 1159295036348201293488596\n  Bound result 94123969852919926468989304\n\n[PASS] test_allowanceAndTransferFailures() (gas: 272217)\n[PASS] test_checkpointsSameBlockPoolManagerAndSelfTransfer() (gas: 612594)\n[PASS] test_erc20AndExactBalances() (gas: 402310)\n[PASS] test_noOwnerMintOrReinitialize() (gas: 81339)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 93.29ms (94.50ms CPU time)\n\nRan 2 tests for test/Deploy.t.sol:DeployTest\n[PASS] test_dryRunRejectsWrongChainAndMissingOwner() (gas: 6873103)\n[PASS] test_dryRunWiringExplicitOwnerAndPermissions() (gas: 14452074)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 111.69ms (4.03ms CPU time)\n\nRan 3 tests for test/Pulse.t.sol:PulseRateModuleTest\n[PASS] testFuzz_alwaysWithinRateBounds(uint16,uint256) (runs: 256, μ: 1394311, ~: 1394360)\nLogs:\n  Bound result 282\n\n[PASS] test_boundsAtZeroHotAndAbove() (gas: 1545919)\n[PASS] test_liveFeeFollowsRealPulseAndCreatorControls() (gas: 4445698)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 115.64ms (60.10ms CPU time)\n\nRan 7 tests for test/Heart.t.sol:HeartPotTest\n[PASS] test_fundingRules() (gas: 774309)\n[PASS] test_ineligibleRoundCarriesWholePotAndCannotClaim() (gas: 1611483)\n[PASS] test_manyEmptyRoundsThenFunding() (gas: 3107243)\n[PASS] test_onBuyGasOnEveryPath() (gas: 124684507)\nLogs:\n  onBuy gas: 25433\n  onBuy gas: 25645\n  onBuy gas: 73885\n  onBuy gas: 39677\n  onBuy gas: 39773\n  onBuy gas: 96760\n  onBuy gas: 44197\n  onBuy gas: 119766\n\n[PASS] test_ownerPowersAndLimits() (gas: 1456251)\n[PASS] test_rejectedRecipientAndTaxedPayoutRollBack() (gas: 1677353)\n[PASS] test_splitClaimAndCarryAcrossRounds() (gas: 2288836)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 327.74ms (147.36ms CPU time)\n\nRan 6 tests for test/Gas.t.sol:GasWithHeartTest\n[PASS] test_gas_nonRouterBuyNamed() (gas: 354438)\n[PASS] test_gas_plainBuyNoHookData() (gas: 324064)\n[PASS] test_gas_routerBuyEndsRoundInsideOnBuy() (gas: 781900)\n[PASS] test_gas_routerBuyNamed() (gas: 404615)\n[PASS] test_gas_routerBuyRoundRunning() (gas: 662489)\n[PASS] test_gas_sell() (gas: 428615)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 343.68ms (3.77ms CPU time)\n\nRan 1 test for test/HeartInvariant.t.sol:SwapIdentityTest\n[PASS] testFuzz_swapResultIdenticalWithOrWithoutHeart(uint96[4],uint8) (runs: 256, μ: 4727285, ~: 4715832)\nLogs:\n  Bound result 2876876388304179\n  Bound result 99000000038539659\n  Bound result 5336793259480492\n  Bound result 99001463297877180\n  Bound result 2876876388304179\n  Bound result 99000000038539659\n  Bound result 5336793259480492\n  Bound result 99001463297877180\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 358.71ms (292.82ms CPU time)\n\nRan 6 tests for test/Pulse.t.sol:PulseTest\n[PASS] testFuzz_decayNeverRevertsAndIsMonotone(uint128,uint256,uint256) (runs: 256, μ: 16668, ~: 16335)\nLogs:\n  Bound result 10000000\n\n[PASS] testFuzz_wholeHalfLivesAreExactShifts(uint128,uint8) (runs: 256, μ: 12719, ~: 12586)\nLogs:\n  Bound result 3\n\n[PASS] test_decayExactHalfLivesAndCap() (gas: 90131)\n[PASS] test_pulseAccumulatesDecaysAndIgnoresSmallBuys() (gas: 2996060)\n[PASS] test_pulseCountsEveryBuyWithoutHeart() (gas: 2046340)\n[PASS] test_pulseSaturatesInsteadOfOverflowing() (gas: 2338517)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 441.77ms (431.53ms CPU time)\n\nRan 22 tests for test/Fees.t.sol:FeesTest\n[PASS] testFuzz_balanceInvariant(uint96[6],uint8,uint16) (runs: 256, μ: 5197258, ~: 5488771)\nLogs:\n  Bound result 0\n  Bound result 199\n  Bound result 9000011030422775\n  Bound result 9000000007946305\n  Bound result 9000000058805613\n  Bound result 7847147598864080\n  Bound result 7188778689242870\n  Bound result 9000067518861510\n\n[PASS] testFuzz_roundingConserves(uint128,uint16,uint16) (runs: 256, μ: 19900, ~: 19947)\nLogs:\n  Bound result 170\n  Bound result 170\n\n[PASS] test_buySellFeeSplitForAllDecimals() (gas: 9371822)\n[PASS] test_buyUsesSingleRateSnapshot() (gas: 2280344)\n[PASS] test_creatorAndTeamClaimsToContractsReentrancyAndFailure() (gas: 2474152)\n[PASS] test_creatorTransferPreservesOldCreditAndActivity() (gas: 2280647)\n[PASS] test_erc20ClaimsAndInvalidRecipients() (gas: 2309352)\n[PASS] test_exactOutputAndTinyBuyRevert() (gas: 1618122)\n[PASS] test_invalidModuleRateCannotCollectFees() (gas: 1678259)\n[PASS] test_lowerDuringScheduleDoesNotRestartClock() (gas: 1984145)\n[PASS] test_multiplePoolsSameQuoteAggregateClaims() (gas: 3784244)\n[PASS] test_negativeRebasePreventsPreferentialClaim() (gas: 2146790)\n[PASS] test_noCreatorOwnerOrTeamExemption() (gas: 2409782)\n[PASS] test_partialBuyRevertsWithoutAccruingFee() (gas: 1756909)\n[PASS] test_raiseChangesSwapRateAfterExplicitApplication() (gas: 1977923)\n[PASS] test_reentrancyCannotClaimAnotherLiveCredit() (gas: 2218156)\n[PASS] test_scheduleAtSeveralTimesAndExtraAllToTeam() (gas: 3022315)\n[PASS] test_scheduledRateRoundsUp() (gas: 1397293)\n[PASS] test_taxedOrFailedPayoutPreservesCredit() (gas: 2489056)\n[PASS] test_taxedReceiptRollsBackSwapAndFeeCredits() (gas: 1932266)\n[PASS] test_teamRecipientChangePreservesAccruedOwnership() (gas: 2296281)\n[PASS] test_unauthorizedClaimsCannotRedirectCredits() (gas: 1929171)\nSuite result: ok. 22 passed; 0 failed; 0 skipped; finished in 442.06ms (445.83ms CPU time)\n\nRan 8 tests for test/Permit.t.sol:PermitTest\n[PASS] test_domainSeparatorBindsChainAndContract() (gas: 133177)\n[PASS] test_feeForwardFailureRevertsLaunch() (gas: 2775917)\n[PASS] test_permitBindsCreatorQuoteTickDeadlineAndSigner() (gas: 2072492)\n[PASS] test_routerHeartSignerWiringGuards() (gas: 444289)\n[PASS] test_signerReplacementInvalidatesOldPermits() (gas: 1462352)\n[PASS] test_tickBoundsStillApplyWithValidPermit() (gas: 1426103)\n[PASS] test_validPermitConsumesNonceAndForwardsFee() (gas: 2684987)\n[PASS] test_wrongFeeAndFeeChanges() (gas: 2904031)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 469.21ms (8.68ms CPU time)\n\nRan 10 tests for test/Heart.t.sol:HeartTimerTest\n[PASS] test_belowMinBuyOrUnlistedThresholdIgnored() (gas: 4122381)\n[PASS] test_buyAfterDeadlineEndsInsideOnBuyAndStartsNext() (gas: 2351638)\n[PASS] test_endRequiresExpiredRound() (gas: 2515443)\n[PASS] test_extensionAndCap() (gas: 7616977)\n[PASS] test_gasBurningAndRevertingHeartsBoundedByStipend() (gas: 516669199)\n[PASS] test_nonRouterAndSpoofedHookDataNeverCount() (gas: 2965028)\n[PASS] test_onlyHookCallsOnBuy() (gas: 35415)\n[PASS] test_revertingHeartNeverBreaksSwap() (gas: 2144611)\n[PASS] test_routerBuyStartsRoundAndRecordsBuy() (gas: 1902840)\n[PASS] test_sellsNeverTouchTimer() (gas: 2599015)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 469.58ms (436.31ms CPU time)\n\nRan 1 test for test/Invariant.t.sol:FeeInvariantTest\n[PASS]\nFeeInvariantTest invariants:\n[PASS] invariant_feesFullyBackedAndExactlyAttributed\n[PASS] invariant_fixedSupplyAndLockedPosition\n FeeInvariantTest invariants (runs: 128, calls: 4096, reverts: 0)\n\n╭------------+-----------------+-------+---------+----------╮\n| Contract   | Selector        | Calls | Reverts | Discards |\n+===========================================================+\n| FeeHandler | advance         | 583   | 0       | 0        |\n|------------+-----------------+-------+---------+----------|\n| FeeHandler | buy             | 591   | 0       | 0        |\n|------------+-----------------+-------+---------+----------|\n| FeeHandler | claim           | 641   | 0       | 0        |\n|------------+-----------------+-------+---------+----------|\n| FeeHandler | lower           | 533   | 0       | 0        |\n|------------+-----------------+-------+---------+----------|\n| FeeHandler | sell            | 560   | 0       | 0        |\n|------------+-----------------+-------+---------+----------|\n| FeeHandler | transferCreator | 565   | 0       | 0        |\n|------------+-----------------+-------+---------+----------|\n| FeeHandler | transferTeam    | 623   | 0       | 0        |\n╰------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 298\n  Bound result 3818262770779881\n  Bound result 913672734944604\n  Bound result 57480\n  Bound result 19900000000007100\n  Bound result 214\n  Bound result 0\n  Bound result 150\n  Bound result 1000\n  Bound result 103\n  Bound result 100\n  Bound result 5233612229251126\n  Bound result 9999\n  Bound result 14195\n  Bound result 8864\n  Bound result 42249996230777636\n  Bound result 6771\n  Bound result 3738055059\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.32s (1.23s CPU time)\n\nRan 1 test for test/HeartInvariant.t.sol:HeartInvariantTest\n[PASS]\nHeartInvariantTest invariants:\n[PASS] invariant_heartSolventAndConserved\n[PASS] invariant_hookSolvent\n HeartInvariantTest invariants (runs: 128, calls: 4096, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| HeartHandler | advance  | 700   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| HeartHandler | buy      | 668   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| HeartHandler | claim    | 678   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| HeartHandler | end      | 694   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| HeartHandler | fund     | 665   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| HeartHandler | sell     | 691   | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 38401815\n  Bound result 1\n  Bound result 15808\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 38401815\n  Bound result 323\n  Bound result 2\n  Bound result 264337593543950335\n  Bound result 1\n  Bound result 34\n  Bound result 49900000000004676\n  Bound result 543950335\n  Bound result 2734298\n  Bound result 38336005805222595\n  Bound result 2\n  Bound result 1\n  Bound result 49900116418878236\n  Bound result 543950335\n  Bound result 1\n  Bound result 378\n  Bound result 1\n  Bound result 2\n  Bound result 198945745134659475036325462\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.40s (1.32s CPU time)\n\nRan 18 test suites in 1.40s (6.26s CPU time): 107 tests passed, 0 failed, 1 skipped (108 total tests)\n","passed":true},{"durationMs":136,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"FixedRateModule.applyRaise(bytes32)\",\"FixedRateModule.cancelRaise(bytes32)\",\"FixedRateModule.initialize(bytes32,uint16)\",\"FixedRateModule.lowerRate(bytes32,uint16)\",\"FixedRateModule.scheduleRaise(bytes32,uint16)\",\"KeelHeart.acceptOwnership()\",\"KeelHeart.addQuote(address)\",\"KeelHeart.claim(uint256,address)\",\"KeelHeart.end()\",\"KeelHeart.fund(address,uint256)\",\"KeelHeart.onBuy(address,address,address,uint256,uint256)\",\"KeelHeart.setMinBuy(address,uint256)\",\"KeelHeart.transferOwnership(address)\",\"KeelV2Factory.acceptOwnership()\",\"KeelV2Factory.configureQuote(address,bool,int24,int24,bool,bool)\",\"KeelV2Factory.initialize(address,address)\",\"KeelV2Factory.launch(string,string,string,address,int24,uint16,uint256,bytes)\",\"KeelV2Factory.registerModule(address)\",\"KeelV2Factory.setDecaySeconds(uint32)\",\"KeelV2Factory.setHeart(address)\",\"KeelV2Factory.setLaunchFee(uint256)\",\"KeelV2Factory.setLaunchesPaused(bool)\",\"KeelV2Factory.setRouter(address)\",\"KeelV2Factory.setSigner(address)\",\"KeelV2Factory.setTakeoverContract(address)\",\"KeelV2Factory.setTeamRecipient(address)\",\"KeelV2Factory.transferOwnership(address)\",\"KeelV2Factory.unlockCallback(bytes)\",\"KeelV2Hook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"KeelV2Hook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"KeelV2Hook.claimCreator(address,address)\",\"KeelV2Hook.claimTeam(address,address)\",\"KeelV2Hook.creatorAction(bytes32,address)\",\"KeelV2Hook.ping(address)\",\"KeelV2Hook.receive()\",\"KeelV2Hook.registerPool((address,address,uint24,int24,address),address,address,address,address,uint16,uint32)\",\"KeelV2Hook.setCreatorByTakeover(address,address)\",\"KeelV2Hook.transferCreator(address,address)\",\"KeelV2Token.approve(address,uint256)\",\"KeelV2Token.transfer(address,uint256)\",\"KeelV2Token.transferFrom(address,address,uint256)\",\"PulseRateModule.applyRaise(bytes32)\",\"PulseRateModule.cancelRaise(bytes32)\",\"PulseRateModule.initialize(bytes32,uint16)\",\"PulseRateModule.lowerRate(bytes32,uint16)\",\"PulseRateModule.scheduleRaise(bytes32,uint16)\"],\"files\":{\".gas-snapshot\":10,\".gitignore\":5,\"README.md\":263,\"REVIEW.md\":108,\"foundry.toml\":26,\"script/Deploy.s.sol\":70,\"src/FixedRateModule.sol\":13,\"src/KeelHeart.sol\":330,\"src/KeelV2Factory.sol\":400,\"src/KeelV2Hook.sol\":426,\"src/KeelV2Token.sol\":94,\"src/PulseRateModule.sol\":20,\"src/RateModuleBase.sol\":111,\"src/interfaces/IKeelHeart.sol\":10,\"src/interfaces/IRulesModule.sol\":25,\"src/libraries/Guard.sol\":14,\"src/libraries/PulseMath.sol\":56,\"src/libraries/QuoteTransfer.sol\":35,\"test/Deploy.t.sol\":55,\"test/Fees.t.sol\":445,\"test/Gas.t.sol\":78,\"test/Heart.t.sol\":534,\"test/HeartInvariant.t.sol\":198,\"test/Invariant.t.sol\":185,\"test/Launch.t.sol\":265,\"test/MainnetFork.t.sol\":34,\"test/Math.t.sol\":45,\"test/Permit.t.sol\":191,\"test/Pulse.t.sol\":195,\"test/Rules.t.sol\":198,\"test/Token.t.sol\":113,\"test/support/HeartTest.sol\":91,\"test/support/KeelTest.sol\":204,\"test/support/MockQuote.sol\":67,\"test/support/Router.sol\":101,\"test/support/TestModules.sol\":37},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":13478,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-eth at src/KeelHeart.sol:195: Reentrancy in KeelHeart.claim(uint256,address) (src/KeelHeart.sol#195-212):\n[high/high] uninitialized-state at src/KeelHeart.sol:158: KeelHeart._anchors (src/KeelHeart.sol#74) is never initialized. It is used in:\n[medium/high] incorrect-equality at src/RateModuleBase.sol:95: RateModuleBase._creator(bytes32) (src/RateModuleBase.sol#95-98) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/RateModuleBase.sol:90: RateModuleBase.currentRate(bytes32) (src/RateModuleBase.sol#90-93) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/KeelHeart.sol:133: KeelHeart.onBuy(address,address,address,uint256,uint256) (src/KeelHeart.sol#133-155) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/KeelV2Token.sol:86: KeelV2Token._checkpoint(address) (src/KeelV2Token.sol#86-93) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/RateModuleBase.sol:50: Reentrancy in RateModuleBase.lowerRate(bytes32,uint16) (src/RateModuleBase.sol#50-58):\n[medium/medium] reentrancy-no-eth at src/RateModuleBase.sol:60: Reentrancy in RateModuleBase.scheduleRaise(bytes32,uint16) (src/RateModuleBase.sol#60-68):\n[medium/medium] reentrancy-no-eth at src/RateModuleBase.sol:70: Reentrancy in RateModuleBase.cancelRaise(bytes32) (src/RateModuleBase.sol#70-75):\n[medium/medium] reentrancy-no-eth at src/KeelV2Factory.sol:337: Reentrancy in KeelV2Factory.unlockCallback(bytes) (src/KeelV2Factory.sol#337-367):\n[medium/medium] reentrancy-no-eth at src/RateModuleBase.sol:78: Reentrancy in RateModuleBase.applyRaise(bytes32) (src/RateModuleBase.sol#78-87):\n[medium/medium] uninitialized-local at src/KeelHeart.sol:269: KeelHeart._finish(KeelHeart.Live).ordinal (src/KeelHeart.sol#269) is a local variable never initialized\n[medium/medium] uninitialized-local at src/KeelHeart.sol:305: KeelHeart._anchorBefore(address,uint256).low (src/KeelHeart.sol#305) is a local variable never initialized\n[medium/medium] uninitialized-local at src/KeelV2Token.sol:62: KeelV2Token.balanceAt(address,uint256).low (src/KeelV2Token.sol#62) is a local variable never initialized\n[medium/medium] uninitialized-local at src/KeelV2Hook.sol:344: KeelV2Hook._beat(bytes32,KeelV2Hook.Pool,address,uint256,uint256,bytes).threshold (src/KeelV2Hook.sol#344) is a local variable never initialized\n[medium/medium] unused-return at src/KeelV2Factory.sol:271: KeelV2Factory.launch(string,string,string,address,int24,uint16,uint256,bytes) (src/KeelV2Factory.sol#271-330) ignores return value by poolManager.initialize(key,TickMath.getSqrtPriceAtTick(startTick)) (src/KeelV2Factory.sol#319)\n[medium/medium] unused-return at src/KeelV2Factory.sol:271: KeelV2Factory.launch(string,string,string,address,int24,uint16,uint256,bytes) (src/KeelV2Factory.sol#271-330) ignores return value by poolManager.unlock(abi.encode(token)) (src/KeelV2Factory.sol#321)\n[medium/medium] unused-return at src/KeelV2Factory.sol:337: KeelV2Factory.unlockCallback(bytes) (src/KeelV2Factory.sol#337-367) ignores return value by (delta,None) = poolManager.modifyLiquidity(info.key,ModifyLiquidityParams(info.tickLower,info.tickUpper,int256(uint256(info.liquidity)),bytes32(0)),) (src/KeelV2Factory.sol#343-349)\n[medium/medium] unused-return at src/KeelV2Factory.sol:271: KeelV2Factory.launch(string,string,string,address,int24,uint16,uint256,bytes) (src/KeelV2Factory.sol#271-330) ignores return value by poolManager.initialize(key,TickMath.getSqrtPriceAtTick(- startTick)) (src/KeelV2Factory.sol#319)\n[medium/medium] unused-return at src/KeelV2Factory.sol:337: KeelV2Factory.unlockCallback(bytes) (src/KeelV2Factory.sol#337-367) ignores return value by poolManager.settle() (src/KeelV2Factory.sol#361)\n[low/medium] reentrancy-benign at src/KeelV2Factory.sol:271: Reentrancy in KeelV2Factory.launch(string,string,string,address,int24,uint16,uint256,bytes) (src/KeelV2Factory.sol#271-330):\n[low/medium] reentrancy-benign at src/KeelV2Factory.sol:271: Reentrancy in KeelV2Factory.launch(string,string,string,address,int24,uint16,uint256,bytes) (src/KeelV2Factory.sol#271-330):\n[low/medium] reentrancy-benign at src/KeelHeart.sol:158: Reentrancy in KeelHeart.fund(address,uint256) (src/KeelHeart.sol#158-183):\n[low/medium] reentrancy-events at src/RateModuleBase.sol:50: Reentrancy in RateModuleBase.lowerRate(bytes32,uint16) (src/RateModuleBase.sol#50-58):\n[low/medium] reentrancy-events at src/KeelV2Factory.sol:337: Reentrancy in KeelV2Factory.unlockCallback(bytes) (src/KeelV2Factory.sol#337-367):\n[low/medium] reentrancy-events at src/RateModuleBase.sol:78: Reentrancy in RateModuleBase.applyRaise(bytes32) (src/RateModuleBase.sol#78-87):\n[low/medium] reentrancy-events at src/RateModuleBase.sol:70: Reentrancy in RateModuleBase.cancelRaise(bytes32) (src/RateModuleBase.sol#70-75):\n[low/medium] reentrancy-events at src/RateModuleBase.sol:60: Reentrancy in RateModuleBase.scheduleRaise(bytes32,uint16) (src/RateModuleBase.sol#60-68):\n[low/medium] timestamp at src/KeelHeart.sol:299: KeelHeart._anchorBefore(address,uint256) (src/KeelHeart.sol#299-315) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelV2Factory.sol:271: KeelV2Factory.launch(string,string,string,address,int24,uint16,uint256,bytes) (src/KeelV2Factory.sol#271-330) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelHeart.sol:133: KeelHeart.onBuy(address,address,address,uint256,uint256) (src/KeelHeart.sol#133-155) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelHeart.sol:186: KeelHeart.end() (src/KeelHeart.sol#186-192) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelHeart.sol:263: KeelHeart._finish(KeelHeart.Live) (src/KeelHeart.sol#263-278) uses timestamp for comparisons\n[low/medium] timestamp at src/RateModuleBase.sol:90: RateModuleBase.currentRate(bytes32) (src/RateModuleBase.sol#90-93) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelV2Hook.sol:237: KeelV2Hook.splitFee(uint256,uint16,uint16) (src/KeelV2Hook.sol#237-246) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelHeart.sol:281: KeelHeart._potAt(address,uint256) (src/KeelHeart.sol#281-290) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelV2Hook.sol:186: KeelV2Hook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/KeelV2Hook.sol#186-205) uses timestamp for comparisons\n[low/medium] timestamp at src/RateModuleBase.sol:95: RateModuleBase._creator(bytes32) (src/RateModuleBase.sol#95-98) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelV2Hook.sol:334: KeelV2Hook._beat(bytes32,KeelV2Hook.Pool,address,uint256,uint256,bytes) (src/KeelV2Hook.sol#334-366) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelV2Hook.sol:368: KeelV2Hook._accrue(bytes32,KeelV2Hook.Pool,uint256,uint256) (src/KeelV2Hook.sol#368-382) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelV2Hook.sol:286: KeelV2Hook.setCreatorByTakeover(address,address) (src/KeelV2Hook.sol#286-292) uses timestamp for comparisons\n[low/medium] timestamp at src/RateModuleBase.sol:78: RateModuleBase.applyRaise(bytes32) (src/RateModuleBase.sol#78-87) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelV2Hook.sol:384: KeelV2Hook._pay(address,address,uint256) (src/KeelV2Hook.sol#384-399) uses timestamp for comparisons\n[low/medium] timestamp at src/KeelV2Hook.sol:314: KeelV2Hook._rates(bytes32,KeelV2Hook.Pool,bool,bytes) (src/KeelV2Hook.sol#314-331) uses timestamp for comparisons\n[low/medium] timestamp at src/RateModuleBase.sol:42: RateModuleBase.initialize(bytes32,uint16) (src/RateModuleBase.sol#42-48) uses timestamp for comparisons","passed":true},{"durationMs":1046,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/KeelV2Factory.sol:142: Reentrancy: State change after external call (6 places)\n[low] centralization-risk at src/KeelHeart.sol:105: Centralization Risk (14 places)\n[low] costly-loop at src/KeelHeart.sol:204: Costly operations inside loop\n[low] ecrecover at src/KeelV2Factory.sol:379: `ecrecover` Signature Malleability\n[low] internal-function-used-once at src/libraries/PulseMath.sol:37: Internal Function Used Only Once\n[low] large-numeric-literal at src/KeelHeart.sol:36: Large Numeric Literal (5 places)\n[low] literal-instead-of-constant at src/KeelV2Factory.sol:186: Literal Instead of Constant (32 places)\n[low] missing-inheritance at src/KeelV2Token.sol:5: Missing Inheritance\n[low] non-reentrant-not-first at src/KeelV2Hook.sol:189: `nonReentrant` is Not the First Modifier (2 places)\n[low] require-revert-in-loop at src/KeelHeart.sol:204: Loop Contains `require`/`revert`\n[low] state-variable-could-be-immutable at src/KeelV2Token.sol:11: State Variable Could Be Immutable (2 places)\n[low] unchecked-return at src/KeelV2Factory.sol:319: Unchecked Return (5 places)\n[low] uninitialized-local-variable at src/KeelHeart.sol:204: Uninitialized Local Variable\n[low] unsafe-erc20-operation at src/KeelV2Factory.sol:360: Unsafe ERC20 Operation\n[low] unused-public-function at src/KeelV2Hook.sol:257: Public Function Not Used Internally","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"ae410e119ea5dc3e67847be61d257fc151a00a66b4daadb384464972fd46dc31","verifiedTreeHash":"10fa0c685590fdd107be0043cb6fdb329d87a08f","verifierVersion":"0.1.0+560ab6a2"}]}