{"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":"6a7439e9-e9aa-469b-bdb5-633bd6c7f423","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"15a1d7edde3933069527e9e53ec45c95983a9ea9458c4fad15dcbe3df61634a7","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"9e14d264922a15f5436d635cd9d902a3864950e8fc2485c2b18c8b63ba53ff93","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":null,"dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","state":"waiting"},{"acceptedSubmissionHash":"829ebcd3743bdf2c613b16cdc4535da81b5d1fa2bf9d5dd6e2bbef6311ef5403","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"473f9e5bce1cc03fd8cc15cc30e09e7c4fdd1b81eecfd9f94fc6a0af2e219c0a","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"072e83a060146fe00776d0b62d50b24096a4ce9f0b24f0a07f68e0b78d8794ae","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"6b8d2c2ebd3301cff2be8edb1e1cc6ba65c858c02fe7247be81722b969842265","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"6b8d2c2ebd3301cff2be8edb1e1cc6ba65c858c02fe7247be81722b969842265","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":null,"dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"failed"},{"acceptedSubmissionHash":"f521b1416987bd14698ac5948d7bbd5e57d1f847edddc76d11967a580028ccd1","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Prepare and execute a TEST-ONLY Sepolia release of the existing UNLOCK (symbol UNLK) project. Deploy onchain is ON, chainId=11155111, launch kind univ4_hook, ETH pairing, poolBps=8700. No Ethereum mainnet transactions and no website in this task. This is a separate launch from the existing GitHub source, not job.continue or a new implementation from scratch.\n\nSource: https://github.com/identity-md-launches/launch-1309-unlk\nBase commit: eb37eb6d1985670dfd2dc691c34a0c879c885b5a\nImport this repository through IMD's supported repository-import flow and bind repoUrl/baseCommit in the actual request. Preserve UNLOCK/UNLK, one billion tokens with 18 decimals, final 10% swarm / 87% pool / 3% payer allocation, individual quotas, milestone rules, unrestricted native claims, ordinary transfers and permanent $5M unlock. No upgrades, pausing, admin rule changes, counter resets or trader exemptions.\n\nThe requester now authorizes a Sepolia-only experimental deployment and test transactions with test assets. The earlier code-only scope is superseded for this new launch. Keep unresolved economic/security findings disclosed; a successful test deployment is not production approval. Do not weaken controls or fabricate compatibility merely to deploy. Respect platform release checks and report a specific rejection if they prevent this test release.\n\nResolve Sepolia configuration using current official IMD network/policy data and authoritative Uniswap/Chainlink deployment records. Verify actual code and interfaces; do not reuse mainnet addresses blindly or ask the requester for IMD's private key. Reproduce the build/dependency checks and tests. Run a recorded-block fork rehearsal with local impersonation of the authorized factory deployer; a fork test requires no real deployer key.\n\nPrepare all companion contracts and their one-time bindings: UnlkSwapRouter, MerkleClaimEvidence and UnlkOracle, predicted token/distributor/hook addresses, hook salt/permissions, manifest arguments and initialization order. Confirm how IMD's authorized release service deploys these companions and performs configureHook before the factory launch. Flat hook arguments alone do not prove companion orchestration support. If the service cannot perform the required sequence, report the exact missing operation and deliver a reproducible Sepolia deployment package; do not substitute an unattached hook, a plain pool or another launch type.\n\nVerify one reward leaf per recipient per round against the actual launch allocation tree, and verify payer equals the final allocation recipient. Native claim must remain independent of credit synchronization. Correct obsolete vault/constructor instructions in deployment documentation. Retain fixed quota and oracle rules; do not lower the $5M target or introduce test-only admin bypasses into the deployed contracts. Unreachable tiers in public testing must be reported; exercise them in local/fork scenarios with clearly labeled test data.\n\nAfter authorized Sepolia deployment, verify the actual PoolKey contains the enforcing hook. Run and report test buys, within-quota sells, rejected excess sells, eligible native claim plus credit synchronization, transfer-only holder behavior, fee collection and principal protection. Claims must obey native release times; never invent eligible claimants or successful tests. Deliver actual Sepolia contract addresses, explorer links, transaction hashes, PoolKey/PoolId, deployed source commit, ABIs, source-verification status and commands/examples to repeat tests. If deployment fails or remains blocked, state that clearly and deliver the exact error and completed artifacts. No claim that UNLK is live on Ethereum mainnet.\n\nWhen the hook acts: At pool initialization, authenticate the launch factory and bind the official pool once. Before every swap, authenticate the supported router and actual trader. After every swap, atomically record actual acquisitions/sales and enforce the wallet's remaining selling quota, reverting excess sales. After verified permanent $5M full unlock, bypass quotas while retaining authentication and standard fees. On liquidity operations, enforce the existing principal-protection rules. The hook charges no additional fee; preserve the standard 1.25% pool fee and its official distribution. No admin may change these rules.","parentJobId":null,"planHash":"96b9a28b9e26bf559a82f46790eedb492a23fd49c5a6b998cf2f19c0e5eb3da0","previousHash":"82a61ef6a114449f3c7cd8b884d63a8f756f2c572c7c546a995cb24de5815054","projectId":"6a7439e9-e9aa-469b-bdb5-633bd6c7f423","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1386-prepare-execute-test-only-sepolia"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51347","feedbackHash":"cdf2bbeae39ffc4002c41dff312869dac091756d46fe0c4914f11737151ae43c","nodeKey":"audit_economics","submissionHash":"15a1d7edde3933069527e9e53ec45c95983a9ea9458c4fad15dcbe3df61634a7","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50981","feedbackHash":"20a873acdfcff9ddc1d582780842cf8ecc630b34ec4560e4a45aea82cc429566","nodeKey":"write_foundry_tests","submissionHash":"f521b1416987bd14698ac5948d7bbd5e57d1f847edddc76d11967a580028ccd1","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"0cbd0e53c07c358e574cf6d35d944c91e86e5993aba7725f996720a3efa25860","state":"blocked","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"8b419c4abe12f826fd06ed78f95d49e0d7b83997c8dfdb4148f8bb641c111441","device":"9812c5faf226f7c7","findings":[],"hash":"072e83a060146fe00776d0b62d50b24096a4ce9f0b24f0a07f68e0b78d8794ae","nodeId":"001b95a3-79d4-4c5d-9027-6788db34c38b","outcome":"completed","summary":"The work is complete and verified. Here is the standing recap.\n\n**Outcome.** The Sepolia test release of UNLOCK (UNLK) is prepared, rehearsed end to end on a Sepolia fork at a recorded block, and documented. No public Sepolia transaction was sent, because this job holds no funded key and IMD's release service launches only after acceptance. UNLK is not live on Sepolia or Ethereum mainnet, and the docs say so plainly.\n\n**Source import and baseline.** The tree is the GitHub repository `identity-md-launches/launch-1309-unlk` at commit `eb37eb6d…`, imported as ordinary files with its vendored libraries. The baseline reproduced cleanly before any change (build, 118 tests, format, 407 dependency hashes).\n\n**Blocking finding and fix.** IMD's worker manifest for `univ4_hook` deploys exactly the token and the hook, mines the hook salt itself, and has no companion slot or post-deploy call. The imported hook verified `UnlkSwapRouter`, `UnlkOracle` and `MerkleClaimEvidence` inside `beforeInitialize`, so the service could never have launched it, and the admission floor's initialization test would have refused it on an empty chain. I moved that verification to a permissionless post-launch `activate(launchNumber)` that is proven by the factory's own `LaunchRegistry` record, kept the factory-only initialization and seed rules, and made swaps, credits and milestones fail closed until activation. The owner binds the three companions to the hook's real address afterwards, with reciprocity checked on both sides. No control was removed, no admin rule path was added, and the $5M target, quotas and oracle rules are unchanged.\n\n**Sepolia references verified on chain.** PoolManager `0xE03A1074c86CFeDd5C142C4F04F1a1536e203543` from IMD's chain table, and Chainlink ETH/USD `0x694AA1769357215DE4FAC081bf1f309aDC325306` with description, decimals and freshness checked. IMD publishes no Sepolia LaunchFactory, so the fork rehearsal deploys the Sourcify-verified factory sources as a labelled stand-in and impersonates its deployer.\n\n**Fork rehearsal, block 11897677.** The log is in `docs/validation/sepolia-fork-rehearsal.txt`. It covers the launch, the PoolKey carrying the hook with fee 12500 and spacing 60, two buys, a within-quota sale, a rejected excess sale, a rejected foreign router, a native claim refused before and paid at release time, attest plus credit sync, the payer's one-time 3% credit, a transfer-only holder who cannot sell, 80/20 fee collection with principal untouched, and a refused liquidity removal. Tiers above 25% are reported as unreachable in public testing and are exercised only in labelled local fixtures.\n\n**Checks.** `forge build` clean, `forge test` 121 passed with an empty environment, `forge fmt --check` clean, high lint clean, artifacts within size limits, 12 ABIs exported, and the hook, token and companions carry no DELEGATECALL, CALLCODE or SELFDESTRUCT.\n\n**Still open, disclosed.** ORC-01 economic manipulation risk, confirmation of one leaf per account per round in the real contributor tree, the owner's three post-launch binding transactions, and an independent review. The readiness list and operational responsibilities are in `docs/SEPOLIA_RELEASE.md` and `docs/DEPLOYMENT.md`.","treeHash":"359065cb591064c2c7c0d62a67a87d204b2221c4","usage":{"cachedInputTokens":3444694,"inputTokens":642,"model":"claude-fable-5-1","outputTokens":90738,"runtime":"claude","turns":47,"wallClockMs":1580854}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"af9a875696459139","findings":[{"citation":"resolved","description":"The irreversible tier-6 unlock (`tier = i + 1` ... `if (tier == 6) fullyUnlocked = true;`, UnlkHook.sol:436-444) is granted when the oracle's 30-minute TWAP FDV reads >= $5,000,000 at every call for 60 minutes with gaps <= 5 minutes. The only depth condition is the fixed `MIN_QUOTE_DEPTH = 10 ether` in UnlkOracle.sol:21, which does not scale with the tier, and FDV is `SUPPLY * price` with 870M of that supply sitting in the pool itself as token-only liquidity. With the Sepolia release parameters (20 ETH opening market cap, tick 177240, ETH/USD 2556.10) the pool's ETH principal at an FDV ratio r is only 17.4 ETH * (sqrt(r) - 1), so about 155 ETH of buying lifts the spot (and, once held, the TWAP) to the $5M threshold while the pool holds roughly 3% of that figure in quote. Nothing requires the price to be sustained by anyone but the buyer: no other trade is needed, the buyer runs the <=5-minute `updateMilestone()` cadence itself, counter-sellers can only sell 25% of their own E (the quota suppresses the arbitrage that would otherwise make holding expensive), and the unlocking call can be the attacker's own `UnlkSwapRouter.swapBatch` whose first leg (a dust buy) flips `fullyUnlocked` inside `afterSwap` and whose second leg sells the entire position in the same transaction, before any other holder can react. The attacker's ETH cost is only the buy-leg LP fee (the sell-leg fee is paid in UNLK that stays in the pool): 2.33 ETH (~$5,900) in the reproduction. Afterwards `p == 10000` and `remainingQuota == type(uint256).max` for every wallet forever, including the swarm's 100M and the 3% payer allocation, while the pool is back near the opening price with 2.3 ETH in it. This is the author's disclosed ORC-01; this review quantifies it against the actual Sepolia configuration and confirms it is reachable by an unprivileged actor at fee cost, so it is a defect in the committed rule set, not an abstract risk. A fix must not lower the $5M target; options that keep it: make the quote-depth floor scale with the tier being certified (e.g. a minimum ETH principal per tier derived from the FDV, so a $5M certification needs more than a few percent of $5M in the pool), lengthen the confirmation for the terminal tier, and/or defer `fullyUnlocked` by a delay so the unlocking swap cannot also be the exit.","line":436,"path":"src/UnlkHook.sol","reproduction":"State: Sepolia release configuration (native ETH pair, fee 12500, spacing 60, opening tick 177240 from the 20 ETH market cap, token-only seed [-887220,177240] of 870M UNLK, UnlkOracle(maxFeedAge 5400, minLiquidity 1e18), Chainlink answer 255609945656), hook activated, p = 2500, no other trader. Calls (all by one EOA with 200 ETH): (1) router.swap{value:170 ether}(zeroForOne=true, amountSpecified=-170e18, limit MIN_SQRT_PRICE+1, maxInput 170e18, minOutput 1) -> receives 787,966,638 UNLK, pool now holds 170 ETH; (2) wait 30 minutes, hook.updateMilestone(): oracle.fdvUsd() = $5,775,604 while the pool's quote principal is $434,536; qualifyingSince[0..5] set; (3) 11 x (wait 5 minutes, hook.updateMilestone()); (4) wait 5 minutes, router.swapBatch{value:0.001 ether}([buy 0.001 ETH, sell 787,966,638 UNLK]) -> leg 1's afterSwap sets tier 6 / fullyUnlocked = true, leg 2 sells the whole position with no quota check; withdrawNative(). Expected: a $5M certification should not be obtainable by a single holder whose pool backing is ~3% of that figure, and the unlock should not be exitable in the unlocking transaction. Actual: hook.fullyUnlocked() == true permanently, hook.p() == 10000, remainingQuota(anyWallet) == uint256.max; attacker balance 197.67 ETH (net cost 2.325 ETH ~= $5,943); pool left with 2.33 ETH. Run: forge test --match-path test/scratch/SingleActorPermanentUnlock.t.sol -vv (fails on the current code at the final assertFalse).","severity":"high","snippet":"            } else if (block.timestamp - qualifyingSince[i] >= CONFIRMATION) {","title":"Permanent $5M full unlock is purchasable by one actor for ~2.3 ETH: tier-independent 10 ETH depth floor, self-funded 90-minute hold, quota-capped counter-sellers and an atomic unlock-and-exit batch"},{"citation":"resolved","description":"`_updateMilestone` treats every `oracle.fdvUsd()` revert identically: it deletes all six `qualifyingSince` timers and zeroes `lastConfirmation`. `fdvUsd` reverts whenever the latest observation is not `liquid`, and `liquid` requires `depth >= MIN_QUOTE_DEPTH` (UnlkOracle.sol:135-136) where depth is the ETH principal inside the seed range at the current price. On the Sepolia curve the pool holds only 17.4*(sqrt(r)-1) ETH at FDV ratio r, so the tier-1 ($100K) confirmation is only possible near 11-13 ETH of depth, right above the floor. Because `afterSwap` runs `_updateMilestone` after every trade, a holder who sells within its own 25% quota and happens (or chooses) to take the depth from 11.0 ETH to 9.26 ETH at minute 59 of the hour resets the whole confirmation in its own transaction and, through `badSeconds`, keeps `fdvUsd` reverting until a fresh 30-minute window is fully liquid again. The seller loses nothing but the 1.25% fee and can repeat each time the depth recovers. Honest tiers therefore depend on no quota-holder selling near the floor for 90 minutes, which an unincentivized community cannot guarantee, while the finding-1 manipulator (who controls all the depth) is unaffected. Consider not discarding progress on a transient depth dip (e.g. pause the timer instead of deleting it) or measuring depth as a time-weighted quantity like the tick.","line":423,"path":"src/UnlkHook.sol","reproduction":"State: Sepolia configuration as in finding 1, activated hook. Calls: honest buys 5 ETH, griefer buys 6 ETH (pool 11.0 ETH, TWAP FDV $135,015 >= $100K); wait 30 min, updateMilestone() -> qualifyingSince[0] != 0; 11 x (5 min, updateMilestone()); at minute 59 griefer sells remainingQuota(griefer) = 25% of its own purchase (within quota, succeeds) -> pool 9.264 ETH. Expected: a quota-compliant trade should not erase 59 minutes of confirmation by every other participant. Actual: inside the griefer's afterSwap, qualifyingSince[0] == 0 and lastConfirmation == 0; oracle.fdvUsd() reverts InvalidOracle; after the griefer buys 2 ETH back one minute later, updateMilestone() still returns false and fdvUsd() still reverts 5 minutes later; the TWAP is valid again only 30 minutes after the recovery and tier stays 0. Verified by test/scratch/ConfirmationResetByThinPoolSell.t.sol (passes, demonstrating the reset).","severity":"low","snippet":"        } catch {\n            delete qualifyingSince;\n            lastConfirmation = 0;\n            return false;","title":"Any within-quota sale that dips pool quote depth under the fixed 10 ETH floor wipes the community's in-progress tier confirmation and poisons the TWAP for 30 minutes"},{"citation":"resolved","description":"The task requires verifying that the payer equals the final allocation recipient. The hook cannot: `payer` is the manifest's `$owner` (\"the owner the request named, else the payer\"), while the 3% remainder goes to the deployer's `LaunchParams.remainderTo` (\"the requester's wallet\"), and neither `activate` nor `syncPayer` checks anything about who received the tokens (not even `token.balanceOf(payer) >= PAYER_ALLOCATION` or the factory's own `positionOf(launchNumber).requester`). The two values are set by different parties (manifest step vs. release service policy) and the repository can only document that they must be equal (docs/DEPLOYMENT.md, IMD_INTEGRATION.md item 4). If they differ, `syncPayer()` (callable by anyone) grants E = 30M to a wallet with zero UNLK, i.e. 7.5M UNLK of immediate selling rights at p = 2500 that can be exercised on tokens bought or received by transfer, while the wallet that actually holds the 30M has E = 0 and is transfer-only until full unlock. Mitigation that preserves the rule: in `activate`, cross-check `payer` against the factory's recorded requester (`PoolFees.positionOf(launchNumber).requester`) and/or require `token.balanceOf(payer) >= PAYER_ALLOCATION` at activation, so a mismatched launch fails closed instead of mis-crediting.","line":369,"path":"src/UnlkHook.sol","reproduction":"State: manifest `$owner` = X (hook.payer() == X), deployer `remainderTo` = Y != X (factory sends 30M+dust to Y). Calls: anyone -> hook.activate(launchNumber) succeeds (no payer check); anyone -> hook.syncPayer() returns 30,000,000e18. Expected: credit goes to the wallet that received the 3% allocation (Y), or activation refuses the mismatch. Actual: eligible[X] == 30,000,000e18 with token.balanceOf(X) == 0 and remainingQuota(X) == 7,500,000e18; eligible[Y] == 0 although Y holds the 30M, so Y cannot sell and X can sell 7.5M of any UNLK it acquires by transfer or purchase on top of its own quota. Reproducible locally by passing payer != remainderTo to the fixture's launch (test/fixtures/SystemFixture.sol _launchParams sets both to `payer`).","severity":"low","snippet":"            amount = token.PAYER_ALLOCATION();\n            _credit(payer, amount, 1);","title":"syncPayer credits the fixed 30M allocation to the constructor word `$owner` with no on-chain tie to the wallet the factory actually paid (`remainderTo`); a mismatch mints 7.5M of selling rights to a w"},{"citation":"resolved","description":"The task asks to verify one reward leaf per recipient per round against the actual launch allocation tree. The adapter documents this as an unenforceable assumption (MerkleClaimEvidence.sol:22-24) and the repository carries only a one-leaf TEST tree; the real tree is produced by IMD's launch step and was not available to this review, so the property is unverified for the Sepolia launch. `MerkleDistributor.claimed[round][account]` is a boolean that does not record the amount, and `attest` accepts any `(account, amount)` leaf that verifies against the round root once that boolean is true. With two leaves (A, x) and (A, y > x) in one round, A natively claims x (the distributor then refuses the second claim), and anyone attests (A, y): `claimed[A] += y`, `syncRewards(A)` credits E by y, an over-credit of y - x in selling rights. The over-credit is bounded by the round's funding and only exists if IMD's tree builder emits duplicate accounts, which OpenZeppelin's StandardMerkleTree does not prevent. The launch step must confirm per-account uniqueness of the tree whose root goes into launch(); if it cannot, attest could instead bind the attested amount to the distributor's observable transfer (e.g. require amount <= roundOf(round).claimed delta) or a per-account aggregate leaf.","line":76,"path":"src/MerkleClaimEvidence.sol","reproduction":"State: round 0 root over leaves {(A, 1,000,000e18), (A, 2,000,000e18), (B, ...)}, funded 100M, unlocked. Calls: distributor.claim(0, A, 1,000,000e18, proofX) -> A receives 1M, claimed[0][A] = true; distributor.claim(0, A, 2,000,000e18, proofY) -> reverts AlreadyClaimed; rewards.attest(0, A, 2,000,000e18, proofY) -> succeeds (flag true, leaf verifies); hook.syncRewards(A). Expected: E[A] == 1,000,000e18 (tokens actually delivered). Actual: E[A] == 2,000,000e18; remainingQuota(A) is 500,000e18 instead of 250,000e18.","severity":"low","snippet":"        if (!merkleDistributor.claimed(round, account)) revert NotClaimed();\n        bytes32 root = merkleDistributor.roundOf(round).root;\n        bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(account, amount))));\n        if (!MerkleProof.verifyCalldata(proof, root, leaf)) revert InvalidProof();","title":"attest() proves a claim from the distributor's boolean flag plus any leaf for that account, so a round whose tree holds two leaves for one account lets the larger unclaimed leaf be attested after the "},{"citation":"resolved","description":"Trading can only open through `activate`, which requires `launchFactory.registry()`, `registry.artifactsOf(launchNumber)` with roles exactly bytes32(\"hook\"), bytes32(\"token\"), bytes32(\"distributor\"), and a distributor answering `token()`, `opener()`, `roundCount()` and `roundOf(0).funded == 100M`. These are the Sourcify-verified *mainnet* factory's shapes (test/imd/). docs/SEPOLIA_RELEASE.md records that IMD publishes no Sepolia LaunchFactory and that the service fills `$factory`; the fork rehearsal deployed the mainnet sources as a stand-in, so nothing has exercised `activate` against the contract that will actually be `$factory`. If that contract lacks `registry()`, records different roles, uses a different distributor shape, or records the launch under a number other than the one the owner expects, `activate` reverts `InvalidEvidence` on every call, `beforeSwap` reverts `InvalidPool` forever, and the 870M UNLK seeded by the factory (plus the owner's one-shot companion bindings) cannot be recovered or repointed: there is no alternative activation path and no new hook can be attached to the pool. This is the fail-closed branch the author chose, but the package should make it checkable before the service spends the launch: resolve the Sepolia `$factory` address, `eth_call` its `registry()`/`artifactsOf`/`distributorCreationCodeHash()` and compare them with test/imd/, or run the rehearsal against a fork that uses the real `$factory` instead of a stand-in. Report this as a specific precondition of the test release rather than assuming compatibility.","line":330,"path":"src/UnlkHook.sol","reproduction":"State: a Sepolia `$factory` whose registry stores the hook artifact under a different role string (e.g. \"hooks\") or whose `registry()` selector does not exist. Calls: service launch() succeeds (beforeInitialize/beforeAddLiquidity read no companion); owner binds router and oracle; anyone calls hook.activate(launchNumber). Expected: activation succeeds or the mismatch is detected before the launch transaction is sent. Actual: activate reverts (InvalidEvidence, or the registry call itself reverts) on every attempt; hook.active() stays false; every router.swap reverts InvalidPool; 870M UNLK remain in a pool nobody can trade and the owner's bindings cannot be redone. Local illustration: test/Launch.t.sol testActivationRequiresTheFactoryRecordOfThisHook shows activate(LAUNCH_NUMBER + 1) reverting InvalidEvidence when the record does not name the hook.","severity":"low","snippet":"        ILaunchRegistryView registry = ILaunchRegistryView(ILaunchFactoryView(launchFactory).registry());","title":"activate() hard-depends on the unpublished Sepolia LaunchFactory exposing the mainnet registry/distributor interface; any deviation leaves the pool closed forever with 87% of the supply locked, and th"},{"citation":"resolved","description":"`beforeSwap` reverts whenever slot0 reports a protocol fee, and the oracle marks observations illiquid in the same case. This preserves the promised 1.25% total fee, but the consequence is total and irreversible: after `setProtocolFee(key, >0)` by Uniswap's controller, no swap (including post-unlock sales) can ever execute on the only pool the quota governs, and the factory's 870M principal stays locked. The trigger is an external privileged action (no unprivileged amplifier), so this is a trust assumption on Uniswap governance, not an exploit; it is already disclosed in docs/SECURITY_REVIEW.md as GOV-01 and tested in test/Adversarial.t.sol. Listed so the entry-point coverage names it and so the requester weighs \"fee promise\" against \"pool survives a protocol fee\" before the Sepolia release.","line":204,"path":"src/UnlkHook.sol","reproduction":"State: activated pool. Calls: PoolManager owner setProtocolFeeController(c); c calls manager.setProtocolFee(key, 500). Then any router.swap. Expected per the chosen design: revert. Actual: revert InvalidPool for every swap thereafter with no recovery path (test/Adversarial.t.sol testProtocolFeeChangeCannotSilentlyAlterTradingEconomics).","severity":"info","snippet":"        if (protocolFee != 0 || lpFee != FEE) revert InvalidPool();","title":"A non-zero v4 protocol fee set by the PoolManager's fee controller permanently halts every official-pool swap (disclosed GOV-01); recorded here with its concrete trigger for the judge"}],"hash":"15a1d7edde3933069527e9e53ec45c95983a9ea9458c4fad15dcbe3df61634a7","nodeId":"cf97bbbf-01ee-4ff1-895f-eed7f83f47eb","outcome":"completed","summary":"The review is complete. `.imd-findings.json` is in the repository root with 6 findings and a 32-row coverage record covering all 28 listed entry points plus 4 invariants. No tracked file was changed; the only additions are the findings file and two scratch tests under `test/scratch/`, which git ignores.\n\n**Headline finding (high, with a Foundry proof).** The permanent $5M full unlock is purchasable by a single unprivileged actor for roughly the cost of one LP fee leg. Using the actual Sepolia release parameters, I reproduced it end to end in `test/scratch/SingleActorPermanentUnlock.t.sol`, which fails on the current code at its final assertion:\n\n| Quantity | Value |\n| --- | --- |\n| ETH the actor buys with | 170 |\n| TWAP FDV certified | $5.78M |\n| ETH principal backing it | $435K |\n| Time held | 90 minutes |\n| Actor's net cost | 2.33 ETH, about $5.9K |\n\nThe causes are the tier-independent 10 ETH depth floor, the self-funded confirmation cadence, quota-capped counter-sellers, and the fact that the unlocking swap can be the first leg of the actor's own `swapBatch` while the second leg dumps the whole position atomically. This is the author's disclosed ORC-01; my report quantifies it as a reachable defect in the committed rule set rather than an abstract risk, and lists fixes that keep the $5M target.\n\n**Other findings.**\n- Low: a within-quota sale that dips pool ETH below the 10 ETH floor erases everyone's in-progress tier confirmation and poisons the TWAP for 30 minutes. Verified in a second scratch test, pool going from 11.0 to 9.26 ETH.\n- Low: `syncPayer` credits 30M eligibility to the manifest `$owner` with no on-chain tie to the factory's `remainderTo`. A mismatch grants 7.5M of selling rights to a wallet holding nothing.\n- Low: `attest` trusts one leaf per account per round. The real IMD tree was not available, so this property is unverified for the launch, and a duplicate leaf over-credits eligibility.\n- Low: `activate` depends on the unpublished Sepolia LaunchFactory exposing the mainnet registry and distributor shape. A deviation closes the pool forever with 87% of supply locked, and the package has no pre-launch check against the real `$factory`.\n- Info: a non-zero v4 protocol fee halts every swap permanently, already disclosed as GOV-01.\n\n**Coverage.** Router settlement, hook authentication, quota accounting, principal protection, oracle math, claim evidence and token behavior all traced and marked `holds`. The full existing suite of 121 tests passes. Nothing was left `unreached`.\n\n**Not verifiable here.** The actual contributor Merkle tree, the Sepolia `$factory` address and whether `$owner` equals the service's `remainderTo` are launch-step inputs outside this repository, so those three items stay as stated preconditions for the test release.","treeHash":null,"usage":{"cachedInputTokens":4622936,"inputTokens":642,"model":"claude-fable-5-1","outputTokens":110195,"runtime":"claude","turns":61,"wallClockMs":1699879}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"56b13350bba65fd7","findings":[],"hash":"3053d055ea3bb7c67ef49e49fcfc3d8763146404e0a8bad163f8e41565c86004","nodeId":"31d31b2f-6014-46ab-8a28-dd9c0db265c6","outcome":"failed","summary":"the launch check failed, so this was not submitted:\nlaunch.json: constructor argument \"<UnlkSwapRouter deployed address>\" is neither an address nor an integer, and a launch manifest can express nothing else","treeHash":null,"usage":{"cachedInputTokens":2887516,"inputTokens":88,"model":"claude-fable-5-1","outputTokens":66511,"runtime":"claude","turns":66,"wallClockMs":1045995}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"077d2937780a81bc","findings":[{"citation":"resolved","description":"Asymmetry between the three one-time bindings. UnlkOracle.configureHook (src/UnlkOracle.sol:84) checks address(launched.poolManager()) == address(poolManager) and UnlkHook.activate (src/UnlkHook.sol:351) re-checks oracle.poolManager(); the router side checks only hook_.router() == address(this) and activate checks only IUnlkCompanion(router).hook() == address(this) (src/UnlkHook.sol:353). UnlkSwapRouter.poolManager is immutable and is the manager the router calls unlock()/swap() on. If the router was constructed against a different PoolManager than the hook's, every reciprocity check passes, activate succeeds and opens the hook, and then every router.swap reverts (the pool does not exist on the router's manager). Because router.hook is one-shot, UnlkHook.router is immutable and the hook has no admin, nothing can be rebound: 87% of supply stays seeded in a pool nobody can trade. The mirror check that the oracle already performs is missing on the router pair.","line":66,"path":"src/UnlkSwapRouter.sol","reproduction":"State: fixture chain with PoolManager M1 (the launch manager) and a second PoolManager M2. 1) router = new UnlkSwapRouter(M2, owner); 2) build the hook creation code with (M1, token, factory, owner, router, rewards, oracle, 60), mine the salt, factory.launch(...) succeeds (hook.bound() == true, 870M UNLK seeded); 3) owner: router.configureHook(hook) -> passes (hook.router() == router); oracle.configureHook(hook) -> passes; 4) anyone: hook.activate(1309) -> passes, hook.active() == true. Expected: step 3 or 4 reverts InvalidConfig like the oracle pair does. Actual: activation succeeds; alice's router.swap(buy 1 ETH) reverts (M2 has no such pool), and router.configureHook(hook) now reverts Unauthorized, so the state is final. Reproduced in test/scratch/ActivateGap.t.sol::testRouterOnOtherManagerActivatesButCannotTrade (passes on current code = demonstrates the gap). Fix: in UnlkSwapRouter.configureHook add `|| address(hook_.poolManager()) != address(poolManager)` to the InvalidConfig condition, and/or in UnlkHook.activate check `IUnlkCompanion(router).poolManager() == poolManager` (add poolManager() to IUnlkCompanion).","severity":"low","snippet":"        if (address(hook_) == address(0) || hook_.router() != address(this)) revert InvalidConfig();","title":"Router binding and activate never verify the router's PoolManager; a router on another manager activates and bricks trading permanently"},{"citation":"resolved","description":"beforeInitialize accepts any paired currency next to the token and records it as quoteToken; it deliberately consults no companion. UnlkOracle.quoteToken is immutable (0x0 for this native-ETH launch) and activate (src/UnlkHook.sol:352) reverts InvalidConfig when oracle.quoteToken() != quoteToken. So if the manifest/launch pairs UNLK with anything but the currency the pre-deployed oracle was built for (e.g. pool.pairedCurrency = Sepolia WETH 0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14 while the oracle has quoteToken = 0x0), LaunchFactory.launch succeeds atomically: 10% funds the distributor, 87% is seeded into the hooked pool, 3% goes to remainderTo, the registry records the launch. Afterwards activate reverts forever; the hook, oracle (configureHook one-shot) and router bindings cannot be redone and the hook has no admin. Result: a recorded launch whose official pool can never trade and whose 870M UNLK principal is locked for good. The asymmetry is between the launch-time check set (fee, spacing, token, hook) and the activation-time check set (quote token): the one check that can brick is deferred past the only atomic revert point. The oracle exists before the launch (the hook names it as a constructor word), so the check can run at launch time whenever the oracle has code, while still passing the admission floor that rehearses initialization on an empty chain.","line":170,"path":"src/UnlkHook.sol","reproduction":"Fixture: oracle = new UnlkOracle(manager, feed, address(0), owner, 1 hours, 1e18) (native-ETH quote), hook creation code names this oracle, pool pairedCurrency = fixture WETH. 1) factory.launch(params) -> succeeds; hook.bound() == true; token.balanceOf(factory) == 0 (870M seeded); payer holds >= 30M. 2) owner: router.configureHook(hook), oracle.configureHook(hook) -> both succeed. 3) hook.activate(1309) -> reverts InvalidConfig; hook.active() stays false; no further call can change this. Expected: the launch transaction itself reverts (so nothing is seeded) when the pool's quote is not the oracle's quote. Reproduced in test/scratch/ActivateGap.t.sol::testWrongPairedCurrencyLaunchesButNeverActivates. Fix (keeps the admission floor passing, where the oracle address has no code): in beforeInitialize, after computing quoteToken, add `if (address(oracle).code.length != 0 && oracle.quoteToken() != quoteToken) revert InvalidPool();` (and optionally the same code-guarded reciprocity for router/oracle poolManager), so a mis-paired launch reverts atomically inside launch() instead of after it.","severity":"low","snippet":"        quoteToken = c0 == address(token) ? c1 : c0;","title":"A launch whose paired currency differs from the oracle's immutable quoteToken succeeds on chain but can never activate (no recovery)"},{"citation":"resolved","description":"Trust-gap note (access x asymmetry), already listed as an operational check in docs/IMD_INTEGRATION.md and docs/SEPOLIA_RELEASE.md, recorded here so the judge sees it was checked and remains unenforced in code. UnlkHook.payer is the manifest word $owner (the owner the request named, else the payer). LaunchFactory sends the 3% remainder to params.remainderTo, which the deployer fills from policy. Nothing in beforeInitialize, activate or syncPayer verifies the two are the same wallet. If a request names an owner different from the wallet that receives the remainder, the owner gets 30M eligibility (7.5M sellable at p=2500) while holding no UNLK, and the actual 3% holder has E=0 and cannot sell in the official pool at all until full unlock. No unprivileged amplifier exists (the mismatch is a request/policy input), so this is not a defect of the code's access model; it is the one allocation rule the hook takes on trust.","line":369,"path":"src/UnlkHook.sol","reproduction":"Input: manifest hook.constructorArgs[3] = \"$owner\" resolved to wallet A; deployer policy remainderTo = wallet B != A. After launch and activate: syncPayer() -> eligible[A] = 30_000_000e18, token.balanceOf(A) == 0; eligible[B] = 0 while token.balanceOf(B) >= 30_000_000e18; B's router sell of 1 UNLK reverts QuotaExceeded. Expected per the brief: payer == final allocation recipient. Mitigation options: keep the documented operational check (docs already require $owner == economics.remainderTo and the fork rehearsal asserts the owner's balance >= 30M), or add a cheap sanity guard in activate: `if (token.balanceOf(payer) < token.PAYER_ALLOCATION()) revert InvalidConfig();` (holds at activation time on the prescribed sequence, since the owner receives the remainder in the launch transaction and activate is the next step).","severity":"info","snippet":"            amount = token.PAYER_ALLOCATION();","title":"syncPayer credits the fixed 3% quota to the constructor's $owner without any on-chain tie to the factory's remainderTo"}],"hash":"473f9e5bce1cc03fd8cc15cc30e09e7c4fdd1b81eecfd9f94fc6a0af2e219c0a","nodeId":"3a24f0b3-47fa-4db9-9d4f-c3b2fa9e5846","outcome":"completed","summary":"The review is complete. Findings and the coverage record are in `.imd-findings.json` at the repository root. Every snippet matches its cited line, and the only working-tree changes are that file and the ignored `test/scratch/` directory.\n\n**Scope covered.** I audited the permission model of all seven source contracts against the Access Control, Trust Gap and Asymmetry guides, mapped every role and one-shot binding, diffed the paired surfaces (before/after callbacks, the three `configureHook`-style bindings, `swap` vs `swapBatch`, `syncPayer` vs `syncRewards`), and traced the launch-then-activate lifecycle through IMD's verified factory sources. The build and all 121 existing tests pass. The coverage record answers all 28 listed entry points plus one invariant row, none marked unreached.\n\n**Findings (3, none above low).** The access model itself is tight: every callback is PoolManager-only, the factory is the only initializer and liquidity sender, the router is the only swap sender with the trader read from its own context, and no post-launch setter exists beyond three once-only bindings. The gaps are in the reciprocity checks that guard the one irreversible moment:\n\n- **Low, `src/UnlkSwapRouter.sol:66`.** The router binding and `activate` never verify the router's PoolManager, while the oracle pair does. A router built on another manager passes activation, every swap then reverts, and nothing can be rebound. Confirmed by a scratch test.\n- **Low, `src/UnlkHook.sol:170`.** `beforeInitialize` accepts any paired currency, but `activate` requires it to equal the oracle's immutable quote. A mis-paired launch succeeds atomically, seeds 87% of supply, and can never activate. Confirmed by a scratch test. The suggested fix runs the oracle check at launch only when the oracle address has code, so the admission floor still passes.\n- **Info, `src/UnlkHook.sol:369`.** `syncPayer` credits the manifest's `$owner` with no on-chain tie to the factory's `remainderTo`. Already disclosed in the docs as an operational check; recorded so the judge sees it was examined.\n\n**Not reported.** The protocol-fee halt (GOV-01), the parallel one-hour tier confirmation, and the setup authority's ability to leave the launch unactivated are documented design choices and trust assumptions with no unprivileged amplifier, so they are not findings. I did not reach economic calibration of the TWAP manipulation cost (ORC-01), which belongs to another specialist. No finding reached critical or high, so no proof files are attached.","treeHash":null,"usage":{"cachedInputTokens":1784163,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":44646,"runtime":"claude","turns":40,"wallClockMs":690602}},{"artifacts":[],"attempt":2,"bundleHash":null,"device":"f768e94767a9dde3","findings":[],"hash":"5f61b94445cfec7fc9f69df3c643502b4635858a777d7e02ca1ae48936607395","nodeId":"31d31b2f-6014-46ab-8a28-dd9c0db265c6","outcome":"failed","summary":"needs input: The Sepolia addresses of the three companion contracts that UnlkHook's constructor takes as literal words: UnlkSwapRouter (argument 5, router_), MerkleClaimEvidence (argument 6, rewards_) and UnlkOracle (argument 7, oracle_). The univ4_hook manifest can express a constructor word only as an address, an integer or one of $poolManager, $token, $owner, $factory; it has no $contract: reference and no companion slot, and the manifest check refuses stand-ins (zero, 0x...dEaD, low or protocol addresses) and prose. The previous attempt was rejected for writing a prose placeholder. Nothing is deployed on public Sepolia: the owner wallet 0x48E4E74fE1d0c14404cEB57B9fec5b21329C6ec9 has nonce 1 (one deposit transaction) and the repository's docs/SEPOLIA_RELEASE.md records that no public transaction was sent. This job holds no funded Sepolia key, so it cannot deploy them. Build and tests reproduce (forge build clean, 121 tests pass); the rest of launch.json is fixed: kind univ4_hook, hook UnlkHook w","treeHash":null,"usage":{"cachedInputTokens":1069152,"inputTokens":322,"model":"claude-fable-5-1","outputTokens":15633,"runtime":"claude","turns":25,"wallClockMs":271625}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"ca080fd306399669","findings":[{"citation":"resolved","description":"Seam: boundary x invariant (Numerical Gap guide). The milestone rule promises p = 3500 once the 30-minute TWAP FDV stays at or above $100,000 for one hour of confirmations (README 'Wallet rights and milestones', UnlkHook.tierForFdv line 414). The oracle only reports a usable TWAP while the factory's seed position holds at least MIN_QUOTE_DEPTH = 10 ether of ETH principal (UnlkOracle.checkpoint lines 126-136: depth = getAmount0Delta(clamped, rangeUpper, liquidity, false); liquid requires depth >= MIN_QUOTE_DEPTH, and twapTick reverts InvalidOracle on any illiquid second in the window, line 176). The amount of ETH inside a token-only seed is a pure function of the price ratio to the opening price: with the hook launch policy's 20 ETH opening market cap (script/SepoliaForkRehearsal.s.sol derives tick 177240, seed [-887220, 177240], L = 123308773220874512933136) the pool holds 870M tokens worth 17.4 ETH at opening, and ETH-in-range = 17.4 ETH * (sqrt(FDV/20 ETH) - 1). depth >= 10 ETH therefore needs FDV >= 49.65 ETH (computed by bisection on SqrtPriceMath.getAmount0Delta: 49653856248187629517 wei). The $100,000 threshold expressed in ETH is 100000/ethUsd, so tier 1 can confirm at its nominal threshold only while ethUsd <= $2014. At the Sepolia ETH/USD answer the release doc records (255609945656 = $2556.10), ETH depth at exactly $100k FDV is 6.913 ETH; the tier becomes confirmable only from FDV ~ $126,900. Concrete numbers: ethUsd $2016 -> 9.986 ETH; $2556 -> 6.913 ETH; $4000 -> 2.020 ETH (tier 2 at $250k is likewise blocked once ethUsd > $5035). Effect: the committed rule '$100,000 => 3500' is not what the deployed contracts enforce; the real first threshold floats with the ETH price (max($100k, 49.65 ETH * ethUsd)) and the immutable oracle cannot be corrected after launch. Because MIN_QUOTE_DEPTH is a requester-facing oracle rule and the 20 ETH cap is launch policy, the fix is a scope decision: either (a) size the floor to the opening cap so that depth(FDV = $100k) >= floor for the ETH price range the launch expects (e.g. a floor in the 2-5 ETH range, or a floor that scales with the tier as SECURITY_REVIEW.md already lists), or (b) raise the opening market cap, or (c) document that the first reachable tier is max($100k, 49.65 ETH) and amend the README table. The proof test seeds the pool exactly as the rehearsal does, sets the feed to the recorded answer, buys 7.4 ETH (FDV $103,265, depth 7.3075 ETH), and runs 95 minutes of five-minute confirmations: tier stays 0 and p stays 2500; it passes when the floor is reconciled with the tier (e.g. MIN_QUOTE_DEPTH = 5 ether was verified to make it pass).","line":21,"path":"src/UnlkOracle.sol","reproduction":"State: native-ETH launch as docs/SEPOLIA_RELEASE.md and script/SepoliaForkRehearsal.s.sol prepare it: currency0 = ETH, currency1 = UNLK, opening sqrtPrice = getSqrtPriceAtTick(177240) (1e27 UNLK per 20 ETH), factory seed [-887220, 177240] with liquidity floor(870e24 * 2^96 / (sqrt(177240) - sqrt(-887220))) = 123308773220874512933136, oracle constructed with minLiquidity = 1e18, Chainlink ETH/USD answer 255609945656 (8 decimals). After activate(). Input: one buyer calls UnlkSwapRouter.swap{value: 7.4 ether}(SwapRequest(zeroForOne=true, amountSpecified=-7.4e18, sqrtPriceLimitX96=MIN_SQRT_PRICE+1, maxInput=7.4e18, minOutput=1, deadline=now+1h)). Spot FDV computed the way fdvUsd does it is 103265102461531000000000 (= $103,265 >= $100,000, tierForFdv = 1). ETH principal inside the seed range is 7307499999999999999 wei (7.4 ETH minus the 1.25% LP fee), below MIN_QUOTE_DEPTH = 10e18. Then, every 5 minutes for 95 minutes: feed refreshed, UnlkHook.updateMilestone(). Expected per the documented rules: after the 30-minute window plus 60 minutes of confirmations, hook.tier() == 1 and hook.p() == 3500. Actual: UnlkOracle.checkpoint stores liquid = false for every observation (depth < 10 ether), UnlkOracle.fdvUsd() reverts InvalidOracle (twapTick: endBad != startBad / !last.liquid), _updateMilestone takes the catch branch every call, hook.tier() == 0, hook.p() == 2500. The same holds for any FDV below 49653856248187629517 wei of ETH (~$126.9k at $2556).","severity":"medium","snippet":"    uint256 public constant MIN_QUOTE_DEPTH = 10 ether;","title":"10 ETH quote-depth floor makes the $100,000 milestone unreachable at its threshold under the policy's 20 ETH opening market cap (effective tier-1 threshold is ~49.65 ETH of FDV, ~$127k at the recorded"}],"hash":"829ebcd3743bdf2c613b16cdc4535da81b5d1fa2bf9d5dd6e2bbef6311ef5403","nodeId":"8ebf6a37-530d-4817-b20a-b1877edf4ff2","outcome":"completed","summary":"No tracked file changed. The review is complete and `.imd-findings.json` holds the result.\n\n**Outcome.** The math in the hook, oracle and router holds up under the Math Precision, Boundary and Numerical Gap checklists, with one substantiated defect at the seam between the oracle's depth floor and the milestone thresholds.\n\n**Finding 1 (medium, with Foundry proof).** The oracle's 10 ETH quote-depth floor makes the $100,000 tier unreachable at its own threshold. With the policy's 20 ETH opening market cap, the ETH held inside the token-only seed is a pure function of the price ratio to the opening price, and it only reaches 10 ETH once FDV is about 49.65 ETH. At the Sepolia ETH/USD answer the release doc records ($2556), the pool holds 6.9 ETH when FDV is exactly $100k, so every observation is marked illiquid, `fdvUsd` reverts, and the tier stays at 0 until FDV is about $127k. The nominal rule only works while ETH is below about $2014. The proof seeds the pool exactly as the rehearsal script does, buys 7.4 ETH (FDV $103k, depth 7.31 ETH), runs 95 minutes of five-minute confirmations, and asserts tier 1. It fails on the current code and was verified to pass once the floor is lowered. The fix is a scope decision for the requester, since both inputs are committed rules: resize the floor to the opening cap, raise the cap, or amend the documented table.\n\n**What held.** Quota rounding matches between `remainingQuota` and `afterSwap`, sale and buy accounting sides are correct for ETH as currency0, the TWAP floors in both currency orders, the FDV scale chain and Chainlink normalization are correct, router balance equalities are exact for mixed native and ERC-20 batches, and the try/catch in milestone updates cannot be gas-griefed into a confirmation reset.\n\n**Coverage.** All 28 listed entry points have a row, plus four invariant rows. Two rows (`updateMilestone`, `checkpoint`) reference the finding; the rest hold. Static-analysis leads in my area (divide-before-multiply, strict equalities, timestamps) were traced and are intentional or harmless.\n\n**Left in place.** Only the ignored findings file and `test/scratch/` with the proof test and the generator script.","treeHash":null,"usage":{"cachedInputTokens":2736960,"inputTokens":514,"model":"claude-fable-5-1","outputTokens":58224,"runtime":"claude","turns":35,"wallClockMs":906772}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"f2101c240e0b52a9","findings":[{"citation":"resolved","description":"The hook's `payer` is the manifest word `$owner`; the wallet that actually receives the 3% remainder is `LaunchParams.remainderTo`, a launch-policy input the factory pays inside `launch()` (zero means the chain treasury, as for a launch nobody paid for). Nothing on chain ties the two together: `activate` proves the token, hook and distributor from the registry but never checks that `payer` holds `PAYER_ALLOCATION`, and `syncPayer` credits `eligible[payer] += 30_000_000e18` unconditionally. If the service's policy row names a different `remainderTo` than the request's `$owner` (the docs list this as an unverified operational check), the real 3% holder has E = 0 and cannot sell through the official pool at all, while `$owner` carries 7.5M UNLK of selling quota backed by no acquisition: it can buy up to 7.5M UNLK and sell 100% of it before any milestone, which the 25% rule forbids for every other wallet. The task brief asks that payer equals the final allocation recipient be verified; the deliverable only documents it.","line":369,"path":"src/UnlkHook.sol","reproduction":"Local fixture (test/fixtures/SystemFixture.sol, verified LaunchFactory sources): build `_launchParams(...)` exactly as the fixture does but set `params.remainderTo = address(0)` (unpaid launch, remainder to `LaunchFees.treasury()`), call `factory.launch(params)`, then the owner's bindings and `hook.activate(1309)`. State after launch: `token.balanceOf(payer) == 0`, `token.balanceOf(treasury) >= 30_000_000e18`. Call `hook.syncPayer()`: returns 30_000_000e18, `hook.eligible(payer) == 30_000_000e18`, `hook.remainingQuota(payer) == 7_500_000e18`, `hook.eligible(treasury) == 0`. Expected: the 3% credit follows the wallet that received the 3% (or activation refuses a launch whose `$owner` did not receive it); actual: an unbacked 7.5M quota on `$owner` and a permanently unsellable 3% on the real recipient. Reproduced in test/scratch/Explore.t.sol `ExplorePayerMismatch` (passes on the current tree, demonstrating the state). Minimal fix preserving the design: in `activate`, require `token.balanceOf(payer) >= token.PAYER_ALLOCATION()` (the remainder is paid in the same transaction as the registry record, and activation is the next step), so a mismatched owner/remainder configuration leaves the launch closed instead of mis-crediting.","severity":"low","snippet":"            amount = token.PAYER_ALLOCATION();\n            _credit(payer, amount, 1);","title":"syncPayer credits the $owner word with the 3% allocation without checking that wallet received the factory's remainder"},{"citation":"resolved","description":"The rehearsal (and its recorded log docs/validation/sepolia-fork-rehearsal.txt line 25, and docs/SEPOLIA_RELEASE.md line 150) states that after the one-hour warp the real feed is stale and that this is why `updateMilestone()` returns false. At the recorded block the feed's `updatedAt` is 1791792324 and `block.timestamp` is 1791793500, so the answer is 1176 s old; after the 3600 s warp it is 4776 s old, below the oracle's `maxFeedAge` of 5400 s. The feed is fresh. `fdvUsd()` reverts with `InvalidOracle` from `twapTick` because the last observation is not liquid: the rehearsal's buys put 1.5 ETH into the seed range, below the 10 ETH `MIN_QUOTE_DEPTH` floor. Consequently `UnlkOracle._ethUsd` (decimals, round, timestamp and ceiling checks against the real Sepolia proxy) was never executed in the rehearsal, although the release document presents the fail-closed behaviour as feed-related evidence. The same fork also shows tier 1 is exercisable with labelled test data (a 12 ETH buy and 31 minutes give FDV about 144,587 USD, above the 100k tier), so the 'tiers unreachable on the fork without replacing the real Chainlink answer' statement in docs/SEPOLIA_RELEASE.md is not accurate for the lower tiers.","line":248,"path":"script/SepoliaForkRehearsal.s.sol","reproduction":"Fork Sepolia at block 11897677 (`vm.createSelectFork(<sepolia rpc>, 11897677)`), run the rehearsal's deployment, binding and activation steps, buy 1 ETH through `UnlkSwapRouter`, then `vm.warp(block.timestamp + 1 hours)`, `oracle.checkpoint()`, and call `oracle.fdvUsd()`. Observed: `block.timestamp - updatedAt == 4776` with `oracle.maxFeedAge() == 5400`; `fdvUsd()` reverts with selector 0x9589a27d (`InvalidOracle`); `oracle.observations(n-1).liquid == false`. Expected per the script comment and the release log: failure caused by feed staleness. Second run on the same fork: buy 12 ETH, warp 31 minutes, `checkpoint()`, `fdvUsd()` returns about 144587e18 and `_ethUsd` executes against the real proxy. Reproduced in test/scratch/ForkFeed.t.sol (`testFeedFreshnessAfterRehearsalWarp`, `testFdvAgainstRealFeedWhenLiquid`). Fix: correct the comment, the log line and the release document; add to the rehearsal a labelled 12 ETH buy plus a 31 minute warp that asserts `fdvUsd()` succeeds (exercising the real feed) and, if desired, drives tier 1 through twelve 5 minute confirmations.","severity":"low","snippet":"        // Time passed on the fork, so the real feed is stale: milestone reads fail closed, trading continues.\n        require(!s.hook.updateMilestone() && s.hook.p() == 2500 && !s.hook.fullyUnlocked(), \"no tier\");\n        console2.log(\"feed stale after warp: milestones fail closed, p stays 2500, swaps unaffected\");","title":"Fork rehearsal misattributes the post-warp milestone failure to a stale Chainlink feed; the real Chainlink read path was never exercised"},{"citation":"resolved","description":"docs/DEPLOYMENT.md and docs/SEPOLIA_RELEASE.md describe the current flow (companions deployed with constructor arguments only, bound by the owner after the launch, `activate` proves the distributor from the registry). docs/IMD_INTEGRATION.md's compatibility matrix (line 29: 'Hook verifies at bind that the distributor holds exactly 10% and the factory holds 90%'; line 33: 'Router and oracle accept the predicted hook; the hook verifies ... in `beforeInitialize`; a mismatch reverts the whole launch'; line 38: distributor creation-code hash 'Open until confirmed per chain') and docs/SECURITY_REVIEW.md line 25 (INT-02: 'companions take the predicted hook and the hook verifies them at bind so a bad setup reverts the launch atomically') describe the design that this release removed. The code has no such checks: `beforeInitialize` reads no companion, `UnlkOracle.configureHook` and `UnlkSwapRouter.configureHook` call `hook.token()` / `hook.router()` on the supplied address and therefore cannot accept a predicted address with no code, and `activate` checks `roundOf(0).funded`, not balances. The task asked for obsolete deployment instructions to be corrected; these two documents still carry them.","line":33,"path":"docs/IMD_INTEGRATION.md","reproduction":"Follow docs/IMD_INTEGRATION.md line 33 or docs/SECURITY_REVIEW.md line 25 as written: before `launch()`, with `predictedHook = factory.predictHook(initCode, salt)` (no code yet), call `oracle.configureHook(predictedHook)` as the owner. Actual: the call reverts (the `launched.token()` staticcall to a codeless address returns no data, and decoding reverts), and `router.configureHook(IUnlkHookView(predictedHook))` reverts the same way; neither companion can be bound before the launch, which is the current intended order (docs/DEPLOYMENT.md steps 5 and 6). Expected: the documents describe the implemented post-launch binding and the `activate` registry proof. Fix: rewrite the three matrix rows and the INT-02 row to the current design (or mark them superseded and point to the 'Revision 2026-10-12' section and SEPOLIA_RELEASE.md).","severity":"low","snippet":"| Companion setup before the launch tx | Hook exists only inside `launch()` | Router and oracle accept the **predicted** hook; the hook verifies `router.hook()`, `oracle.hook()`, `rewards.token()` reciprocally in `beforeInitialize`; a mismatch reverts the whole launch, deploying nothing | Reconciled, tested (`InitializationGuardTest`) |","title":"Integration and security-review documents still describe the superseded pre-launch companion binding and bind-time allocation checks"}],"hash":"9e14d264922a15f5436d635cd9d902a3864950e8fc2485c2b18c8b63ba53ff93","nodeId":"c561bb2e-aa64-42af-8223-46a44f7ed53d","outcome":"completed","summary":"Review complete. The findings file is written and validated, and the tree is otherwise untouched (only the gitignored `test/scratch/` and `.imd-findings.json` were created).\n\n**Outcome.** The control-flow area holds up: I traced every hook callback, the router's settlement path, the oracle's accumulator, the claim adapter and the activation sequence, and found no exploitable execution-flow defect in the Solidity. Three low-severity findings are reported, each with a concrete reproduction.\n\n**Findings written to `.imd-findings.json`:**\n- **Payer credit is not tied to the remainder recipient.** The hook credits the `$owner` word with 30M eligible on `syncPayer` without checking that wallet received the factory's 3% remainder, which is a separate policy input. With `remainderTo` pointing elsewhere, the owner gets 7.5M of unbacked sale quota and the real recipient cannot sell. Reproduced locally in a scratch test.\n- **Rehearsal misattributes the milestone failure.** On the Sepolia fork at the recorded block, the feed is 4776 s old after the warp against a 5400 s limit, so it is fresh. The read fails because the pool is below the 10 ETH depth floor, which means the real Chainlink path was never exercised. A 12 ETH buy plus 31 minutes on the same fork makes `fdvUsd` succeed at about $144.6k, so tier 1 is exercisable with labelled data.\n- **Stale design text** in the integration matrix and the security review still describe pre-launch companion binding and bind-time balance checks that the code no longer has.\n\n**Verification performed.** Build and all 121 tests pass offline. Published ABIs match the compiled ones and the handoff inventory verifies. The admission-floor protected test passes 4/4 against the built hook with the Sepolia PoolManager address, ETH pairing, fee 12500 and spacing 60 (the floor file needed local shims for two library symbols absent from the vendored libs). Exploratory tests confirmed fail-closed behaviour for a sell into an ETH-empty pool, correct partial fill and refund on a pool-draining buy, and consistent accounting when a withdrawal re-enters a swap.\n\n**Coverage.** All 28 listed entry points have rows: 26 hold, `syncPayer` and `activate` reference finding 1. One row is marked unreached: the economic cost model for the disclosed ORC-01 manipulation risk and the one-leaf-per-account property of IMD's tree, both outside the control-flow area.","treeHash":null,"usage":{"cachedInputTokens":2718902,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":74555,"runtime":"claude","turns":54,"wallClockMs":1198348}},{"artifacts":[],"attempt":1,"bundleHash":"e0d32ec1d047bd25a7186263a4fcc24454864775c517548729a4f807b310c670","device":"5739ce0d803a43cd","findings":[{"description":"The protected hook suite (.imd/reads/protected/univ4_hook/Hook.protected.t.sol) imports `ModifyLiquidityParams, SwapParams` from `v4-core/src/types/PoolOperation.sol` and calls `fail(\"...\")`. The vendored v4-core (v4.0.0, commit e50237c, as test/imd/README.md notes) keeps both structs inside IPoolManager and has no PoolOperation.sol; the vendored forge-std StdAssertions exposes only `fail()` (lib/forge-std/src/StdAssertions.sol:46). The suite's relative imports (`../../../src/HookFlags.sol`, `../../mocks/MockERC20.sol`) place it inside this tree, so if the verifier compiles it with this tree's remappings the floor cannot build and the release is refused before any assertion runs. If the verifier compiles the floor in its own toolchain against the attested creation code only, there is no impact; this cannot be determined from the repository. With the two imports shimmed (structs taken from IPoolManager, `fail()` without a message) in test/scratch, all 11 floor tests pass on the real UnlkHook and UnlkToken creation code with probe addresses for $poolManager/$token/$factory, ETH pairing, fee 12500 and spacing 60. The fix is in lib/ (vendor a v4-core that ships types/PoolOperation.sol and a forge-std that still has fail(string), or whichever versions the verifier pins), which this task may not touch.","line":1,"path":"lib/v4-core/src/types","reproduction":"mkdir -p test/protected/univ4_hook && cp .imd/reads/protected/univ4_hook/*.sol test/protected/univ4_hook/ && forge build. Expected: compiles. Actual: ParserError 6275 `Source \"lib/v4-core/src/types/PoolOperation.sol\" not found`, then after shimming that import, Error 6160 `Wrong argument count for function call: 1 arguments given but expected 0` at the `fail(\"the hook refused PoolManager.initialize ...\")` call (Hook.protected.t.sol:244).","severity":"medium","title":"Admission-floor sources do not compile against the vendored libraries (v4-core v4.0.0 has no types/PoolOperation.sol; forge-std has no fail(string))"},{"description":"`payer` is the hook's `$owner` constructor word; the 30,000,000 UNLK remainder is paid by LaunchFactory to the manifest's `remainderTo`. Nothing on chain ties the two: `activate` proves the token, hook and distributor from the registry but the registry does not record remainderTo, and `syncPayer` credits `token.PAYER_ALLOCATION()` of E to `payer` without checking that `payer` holds it. If the manifest step writes different wallets in the two places (both are filled by hand per docs/SEPOLIA_RELEASE.md), the real recipient holds 30,000,000 UNLK with E = 0 and cannot sell, while the word wallet has E = 30,000,000 and can sell 25% of anything transferred to it, which is exactly the transfer-only bypass the quota is meant to prevent. A cheap on-chain guard is `require(token.balanceOf(payer) >= token.PAYER_ALLOCATION())` at activation (the factory pays before it records), or recording remainderTo in the hook's own event at bind time and comparing in syncPayer.","line":365,"path":"src/UnlkHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {SystemFixture} from \"../fixtures/SystemFixture.sol\";\nimport {UnlkHook} from \"../../src/UnlkHook.sol\";\nimport {UnlkToken} from \"../../src/UnlkToken.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\n\n/// @notice Relies on the repository's test fixtures because the IMD LaunchFactory sources live in test/imd.\n/// Place at test/scratch/PayerMismatch.t.sol. Fails on the current code at the last assertion.\ncontract PayerMismatchTest is SystemFixture {\n    address internal phantom;\n\n    function setUp() public override {\n        _deploy(false);\n        phantom = makeAddr(\"payer word that received nothing\");\n        UnlkHook.Config memory config = UnlkHook.Config(\n            manager,\n            UnlkToken(factory.predictToken(LAUNCH_NUMBER, type(UnlkToken).creationCode)),\n            address(factory),\n            phantom,\n            address(router),\n            rewards,\n            oracle,\n            SPACING\n        );\n        hookInitCode = deployer.initCode(config);\n        _mineHookSalt();\n        key.hooks = IHooks(predictedHook);\n        // remainderTo stays `payer`: the factory pays the 3% to `payer`, the hook credits `phantom`.\n        _launchTokenOnly(OPENING_TICK);\n    }\n\n    function testPayerCreditGoesToTheWordNotTheRecipient() public {\n        assertEq(hook.payer(), phantom);\n        assertGe(token.balanceOf(payer), 30_000_000 ether, \"the factory paid the remainder to payer\");\n        assertEq(token.balanceOf(phantom), 0, \"the hook's payer word received nothing\");\n        hook.syncPayer();\n        assertEq(hook.eligible(phantom), 30_000_000 ether);\n        assertEq(hook.eligible(payer), 30_000_000 ether, \"payer credit must follow the 3% recipient\");\n    }\n}","reproduction":"Build the hook with Config.payer = X and launch with LaunchParams.remainderTo = Y != X (test/scratch/PayerMismatch.t.sol). After activate: token.balanceOf(Y) >= 30_000_000e18, token.balanceOf(X) == 0, hook.syncPayer() returns 30_000_000e18 and hook.eligible(X) == 30_000_000e18, hook.eligible(Y) == 0. Expected: activation refuses or the credit follows Y.","severity":"low","title":"Hook payer word and the factory's remainderTo are never cross-checked: the 3% credit can go to a wallet that received no tokens"},{"description":"`pendingRewardCredit` reverts with InvalidEvidence when `rewards.claimed(recipient) > 100_000_000 ether`, and `syncRewards` calls it unconditionally. `MerkleClaimEvidence.claimed` is cumulative across rounds, and the verified distributor lets the chain treasury open further rounds funded with any UNLK it holds (test/imd/MerkleDistributor.sol openRound). Once one account's attested total crosses the ceiling, every later `syncRewards(account)` reverts forever, so credits from that point are lost for that account although the claims were real; earlier credit stays. The sanity bound is meant to reject absurd adapters, but it is applied to a legitimate monotone counter. Bounding the per-call delta instead of the cumulative total, or capping to the distributor's `roundOf(round).funded` sum, keeps the check without the dead end.","line":384,"path":"src/UnlkHook.sol","reproduction":"After round 0, fund the distributor with 100,000,000 UNLK from any holder and have the LaunchFees treasury open round 1 with a single leaf (A, 100_000_000e18); A claims it and attests it. Expected: syncRewards(A) credits the new claim. Actual: pendingRewardCredit(A) reverts InvalidEvidence (claimed = 101,000,000e18 > ceiling) and syncRewards(A) reverts on every later call.","severity":"low","title":"pendingRewardCredit's fixed 100,000,000 UNLK ceiling permanently disables reward synchronization for an account whose cumulative attested claims exceed it"},{"description":"Documented in the contract's own comment and not enforceable on chain: the distributor records only `claimed[round][account] == true`, so if IMD's tree builder emitted two leaves for the same account in one round, the smaller could be claimed natively and the larger attested, crediting E above what was paid (bounded by the launch's allocation). The actual launch tree is built by the launch step and is not available in this repository; test/MerkleRounds.t.sol proves the rules on a four-leaf tree of the distributor's shape with one leaf per recipient. The launch step must confirm one leaf per account per round in the tree whose root goes into launch().","line":22,"path":"src/MerkleClaimEvidence.sol","reproduction":"Open a round whose root commits leaves (A, 1e18) and (A, 5e18). Claim (A, 1e18) natively; attest(round, A, 5e18, proof5). Expected: credit equals the 1e18 paid. Actual: claimed(A) == 5e18 and syncRewards(A) credits 5e18.","severity":"info","title":"Attestation assumes one leaf per account per round; a tree with two leaves for one account over-credits by the larger unclaimed leaf"}],"hash":"f521b1416987bd14698ac5948d7bbd5e57d1f847edddc76d11967a580028ccd1","nodeId":"b2fe5f43-1bc3-47fb-bc9b-4b439369345e","outcome":"completed","summary":"The findings file is in place at the repository root (the harness excludes it from git deliberately, same as `test/scratch/`). Everything is verified, so here is the recap.\n\n## Outcome\n\nThe submitted tree builds, passes `forge fmt --check`, and passes `forge test` with 179 tests and 1 skipped (the fork variant, which needs a URL). I also ran the new Sepolia fork suite against live Sepolia at the latest block, and all 14 scenarios passed on the real Uniswap v4 PoolManager and Chainlink feed, with the live pool returning exactly the recorded sale proceeds. The recorded block itself needs an archive node, which the public endpoint does not offer. No public transaction was sent and UNLK is not live anywhere.\n\n## What I added under `test/`\n\n- **`SepoliaRelease.t.sol`**: the release shape offline (ETH as currency0, tick 177240 from the 20 ETH policy cap, token-only seed, 87% pool share, owner as payer). It reproduces the recorded fork rehearsal's exact numbers and runs every release scenario: PoolKey carries the enforcing hook, rejected excess sale with the exact wrapped `QuotaExceeded`, transfer-only holder, stranger router, native claim at release time with separate credit sync, 80/20 fee split, principal protection, unreachable tiers reported then exercised on labelled data. `SepoliaForkReleaseTest` reruns it on a fork when `UNLK_SEPOLIA_FORK_URL` is set.\n- **`FailurePaths.t.sol`**: constructor guards for every contract, each `beforeInitialize` term, bound-key checks on all callbacks, inactive hook refuses swaps, seed range recorded once, removal and activation guards, oracle binding guards, router field boundaries, batch limits, callback and `receive` guards.\n- **`MerkleRounds.t.sol`**: a four-leaf tree in the distributor's leaf shape proving one claim and one attestation per recipient per round, non-redirectable proofs, treasury-only later rounds, and swept rounds.\n- **`NativeInvariants.t.sol`**: a ten-action handler over the ETH-holding router with ETH and UNLK conservation, router custody equals credits, ledgers equal ghosts, quotas, fixed principal and bindings, plus a fully-unlocked variant.\n- **`README.md`** describing the suites and the fork command. Two one-line edits to existing test files: `virtual` on a fixture function and formatting of a file that already failed the formatter.\n\n## Findings reported in `.imd-findings.json`\n\n- **Medium**: the admission-floor hook suite imports `v4-core/src/types/PoolOperation.sol` and calls `fail(string)`, neither of which exists in the vendored libraries. With those two shims applied in scratch, all 11 floor tests pass on the real creation code. The fix is in `lib/`, outside my paths.\n- **Low**: the hook's payer word and the factory's `remainderTo` are never cross-checked, so a manifest mismatch credits the 3% to a wallet holding nothing. A scratch proof fails on the current code.\n- **Low**: the fixed 100,000,000 UNLK ceiling in reward credit permanently disables synchronization for an account that crosses it through treasury rounds.\n- **Info**: the documented one-leaf-per-account assumption, which the launch step must confirm on the real tree.","treeHash":"cac9bcb5dee5c8ef70376437c36cff2ed7aa2fea","usage":{"cachedInputTokens":6313122,"inputTokens":930,"model":"claude-fable-5-1","outputTokens":111386,"runtime":"claude","turns":72,"wallClockMs":1560671}}],"verification":[{"checks":[{"durationMs":9295,"exitCode":0,"name":"build","output":"Compiling 113 files with Solc 0.8.26\nSolc 0.8.26 finished in 9.19s\nCompiler run successful!\n","passed":true},{"durationMs":1771,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/ReleaseConfig.t.sol:ReleaseConfigTest\n[PASS] testReadOnlyReleaseChecksOnLocalFixture() (gas: 1373116)\n[PASS] testReadOnlyReleaseChecksRejectWrongChainOrHash() (gas: 1299155)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 19.73ms (255.77µs CPU time)\n\nRan 8 tests for test/Launch.t.sol:LaunchTest\n[PASS] testFactoryHoldsPrincipalAndCannotRemoveIt() (gas: 321578)\n[PASS] testLaunchAllocationAndLockedPrincipal() (gas: 256081)\n[PASS] testLaunchIsOnceAndDeployerOnly() (gas: 1798844)\n[PASS] testOneTimeSetup() (gas: 120638)\n[PASS] testOnlyCanonicalManagerCanCallHook() (gas: 66673)\n[PASS] testPayerCreditExactDeduplicated() (gas: 143245)\n[PASS] testUnauthorizedFactoryAndWrongPoolRejected() (gas: 80542)\n[PASS] testUnconfiguredComponentsFailClosed() (gas: 39605)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 21.87ms (1.51ms CPU time)\n\nRan 7 tests for test/NativeLaunch.t.sol:NativeLaunchTest\n[PASS] testDeepNativePoolWarmsUpAndCanUnlock() (gas: 3316156)\n[PASS] testNativeBatchSettlement() (gas: 856062)\n[PASS] testNativeBuySellAndFeeDistribution() (gas: 1101025)\n[PASS] testNativeExactOutputRefundToAuthenticatedCaller() (gas: 444378)\n[PASS] testNativeValueMismatchAndUnsolicitedSendRejected() (gas: 94900)\n[PASS] testOneSidedOpeningLocks87PercentWithoutEthFunding() (gas: 71438)\n[PASS] testThinLaunchCannotUnlockFromInitialSpotOrTinyBuy() (gas: 4804292)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 22.06ms (6.45ms CPU time)\n\nRan 14 tests for test/Oracle.t.sol:Token0OracleTest\n[PASS] testBadChainlinkRoundsAndAnswersFailClosed() (gas: 2335687)\n[PASS] testConfirmationGapRestartsRatherThanCountingUnobservedTime() (gas: 2824604)\n[PASS] testEveryTierAndPermanentUnlock() (gas: 16095653)\n[PASS] testFallingPricesNeverLowerReachedTier() (gas: 4986596)\n[PASS] testFlashLoanCannotCrossMilestoneInItsTransaction() (gas: 1550470)\n[PASS] testInvalidDataResetsUnfinishedConfirmation() (gas: 3121617)\n[PASS] testNegativeTickAveragesRoundConservativelyForBothOrders() (gas: 766917)\n[PASS] testOneBlockSpikeCannotConfirm() (gas: 747404)\n[PASS] testOracleCannotBeReboundOrConfiguredByAnyone() (gas: 98810)\n[PASS] testSameTimestampManipulationHasNoTimeWeight() (gas: 769614)\n[PASS] testStaleCheckpointAndFeedPreventNewTierButNotTrades() (gas: 1289748)\n[PASS] testSustainedManipulationRequiresWindowAndConfirmation() (gas: 3111809)\n[PASS] testThresholdMappingBoundaries() (gas: 104606)\n[PASS] testWarmupAndExactWindow() (gas: 386642)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 40.64ms (23.06ms CPU time)\n\nRan 14 tests for test/Oracle.t.sol:OracleTest\n[PASS] testBadChainlinkRoundsAndAnswersFailClosed() (gas: 2340801)\n[PASS] testConfirmationGapRestartsRatherThanCountingUnobservedTime() (gas: 2830848)\n[PASS] testEveryTierAndPermanentUnlock() (gas: 16129713)\n[PASS] testFallingPricesNeverLowerReachedTier() (gas: 4997438)\n[PASS] testFlashLoanCannotCrossMilestoneInItsTransaction() (gas: 1567754)\n[PASS] testInvalidDataResetsUnfinishedConfirmation() (gas: 3128695)\n[PASS] testNegativeTickAveragesRoundConservativelyForBothOrders() (gas: 768869)\n[PASS] testOneBlockSpikeCannotConfirm() (gas: 749285)\n[PASS] testOracleCannotBeReboundOrConfiguredByAnyone() (gas: 98354)\n[PASS] testSameTimestampManipulationHasNoTimeWeight() (gas: 771517)\n[PASS] testStaleCheckpointAndFeedPreventNewTierButNotTrades() (gas: 1293260)\n[PASS] testSustainedManipulationRequiresWindowAndConfirmation() (gas: 3118509)\n[PASS] testThresholdMappingBoundaries() (gas: 104606)\n[PASS] testWarmupAndExactWindow() (gas: 387476)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 40.62ms (27.43ms CPU time)\n\nRan 12 tests for test/CreditsAndFees.t.sol:CreditsAndFeesTest\n[PASS] testAttestationRequiresActualClaimAndExactLeaf() (gas: 451572)\n[PASS] testClaimTransferAndPayerTransferDoNotDuplicateCredit() (gas: 589610)\n[PASS] testFeeCollectionDoesNotReducePrincipal() (gas: 688055)\n[PASS] testNativeClaimNeverDependsOnAdapterOrFeed() (gas: 866498)\n[PASS] testNativeClaimThenAttestThenSyncThenSale() (gas: 1332504)\n[PASS] testNegativeLiquidityForbiddenEvenForFactory() (gas: 82460)\n[PASS] testPoolFee125BpsNoHookFeeAndDistribution80_20() (gas: 861566)\n[PASS] testProofReleaseAndDedupRemainNativeRules() (gas: 587366)\n[PASS] testRequesterShareIsTransferableOnlyByRequesterAndOwedOnRejection() (gas: 1307010)\n[PASS] testSaleFeesCountGrossInputAndNeverCreditFeeRecipients() (gas: 1209780)\n[PASS] testUnauthorizedLiquidityAndDonationNoCredit() (gas: 241891)\n[PASS] testUnclaimedRewardsNotEligible() (gas: 53804)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 40.71ms (6.73ms CPU time)\n\nRan 2 tests for test/LocalScript.t.sol:LocalScriptTest\n[PASS] testLocalScriptRefusesPublicChainId() (gas: 14568602)\n[PASS] testLocalSimulationFunctionDoesNotDependOnCaller() (gas: 82665316)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 44.94ms (44.38ms CPU time)\n\nRan 8 tests for test/Adversarial.t.sol:AdversarialTest\n[PASS] test100PercentAloneIsNotFullUnlock() (gas: 7254507)\n[PASS] testArbitraryHookDataAndMultihopPayloadCannotSelectVictim() (gas: 87489)\n[PASS] testCallbacksAllRequireCanonicalManager() (gas: 211647)\n[PASS] testForeignPoolKeyRejectedEvenWithManagerPrank() (gas: 49877)\n[PASS] testNoMintPauseBlacklistUpgradeOrCounterResetEntrypoint() (gas: 121542)\n[PASS] testProtocolFeeChangeCannotSilentlyAlterTradingEconomics() (gas: 385971)\n[PASS] testQuotaMultiplicationDoesNotOverflowAtMaxEligible() (gas: 242266)\n[PASS] testTokenMintsWholeSupplyToDeployerOnly() (gas: 41399)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 73.57ms (7.20ms CPU time)\n\nRan 9 tests for test/Launch.t.sol:InitializationGuardTest\n[PASS] testActivationRefusesCompanionsThatDoNotPointBack() (gas: 7605260)\n[PASS] testActivationRequiresTheFactoryRecordOfThisHook() (gas: 7467895)\n[PASS] testForeignCallerCannotConfigureUnsetRouter() (gas: 32808)\n[PASS] testLaunchNeedsNoCompanionCodeAndStaysClosedWithoutActivation() (gas: 23160969)\n[PASS] testLaunchRejectsSeedThatSpendsPairedCurrency() (gas: 6578827)\n[PASS] testLaunchRejectsWrongFeeOrTickSpacing() (gas: 11141638)\n[PASS] testOnlyFactoryCanInitializeUnboundOfficialPool() (gas: 45076976)\n[PASS] testRewardAdapterBindsOnlyToTheActivatedHookThatNamesIt() (gas: 7980603)\n[PASS] testUnboundCreditsCannotBeSynchronized() (gas: 45115588)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 73.68ms (81.15ms CPU time)\n\nRan 11 tests for test/NativeLaunch.t.sol:NativeWithdrawalTest\n[PASS] testDeepNativePoolWarmsUpAndCanUnlock() (gas: 3316157)\n[PASS] testNativeBatchSettlement() (gas: 856062)\n[PASS] testNativeBuySellAndFeeDistribution() (gas: 1101036)\n[PASS] testNativeExactOutputRefundToAuthenticatedCaller() (gas: 444467)\n[PASS] testNativeSettlementResetsPriorSyncCurrency() (gas: 600534)\n[PASS] testNativeValueMismatchAndUnsolicitedSendRejected() (gas: 94878)\n[PASS] testNativeWithdrawalUsesCEIAndCannotDoubleWithdraw() (gas: 994890)\n[PASS] testOneSidedOpeningLocks87PercentWithoutEthFunding() (gas: 71416)\n[PASS] testOtherWalletCannotWithdrawNativeRefund() (gas: 456671)\n[PASS] testRejectingNativeWalletCanTradeAndRetryWithdrawal() (gas: 1070342)\n[PASS] testThinLaunchCannotUnlockFromInitialSpotOrTinyBuy() (gas: 4804292)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 74.28ms (11.11ms CPU time)\n\nRan 16 tests for test/Trading.t.sol:Token0TradingTest\n[PASS] testAnotherWalletCannotConsumeVictimsAllowanceOrQuota() (gas: 483575)\n[PASS] testBatchBuysThenSellsAndRollbackExcess() (gas: 1650464)\n[PASS] testDirectManagerAndForeignRouterSpoofingRejected() (gas: 643484)\n[PASS] testDirectSmartWalletOwnsRightsNotItsOwner() (gas: 703685)\n[PASS] testDonatedRouterBalancesCannotCreateCreditOrBeSwept() (gas: 490414)\n[PASS] testExactInputBuyCreditsOnlyDeliveredTokens() (gas: 454939)\n[PASS] testExactOutputBuyAndRefund() (gas: 396086)\n[PASS] testExactOutputSaleAccountsActualInput() (gas: 720758)\n[PASS] testExcessSaleRollsBackPoolBalancesOracleAndNonce() (gas: 809743)\n[PASS] testFuzzQuotaBoundary(uint96) (runs: 256, μ: 1019408, ~: 1020059)\nLogs:\n  Bound result 1999999001776796633\n\n[PASS] testPartialExactOutputRejectedAtomically() (gas: 443226)\n[PASS] testPartialInputFillCreditsOnlyExecutedBuy() (gas: 392227)\n[PASS] testReplayOldCallbackHasNoLiveContext() (gas: 429159)\n[PASS] testSaleBoundaryExecutedInputIncludesPoolFee() (gas: 1048997)\n[PASS] testSlippageDeadlineAndNativeValueRejected() (gas: 521863)\n[PASS] testTransfersDoNotMoveRightsOrAllowChainCredit() (gas: 824122)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 74.68ms (62.11ms CPU time)\n\nRan 16 tests for test/Trading.t.sol:TradingTest\n[PASS] testAnotherWalletCannotConsumeVictimsAllowanceOrQuota() (gas: 497902)\n[PASS] testBatchBuysThenSellsAndRollbackExcess() (gas: 1665805)\n[PASS] testDirectManagerAndForeignRouterSpoofingRejected() (gas: 642657)\n[PASS] testDirectSmartWalletOwnsRightsNotItsOwner() (gas: 717573)\n[PASS] testDonatedRouterBalancesCannotCreateCreditOrBeSwept() (gas: 504530)\n[PASS] testExactInputBuyCreditsOnlyDeliveredTokens() (gas: 469055)\n[PASS] testExactOutputBuyAndRefund() (gas: 411390)\n[PASS] testExactOutputSaleAccountsActualInput() (gas: 736403)\n[PASS] testExcessSaleRollsBackPoolBalancesOracleAndNonce() (gas: 824180)\n[PASS] testFuzzQuotaBoundary(uint96) (runs: 256, μ: 1034636, ~: 1035114)\nLogs:\n  Bound result 1999999000000000350\n\n[PASS] testPartialExactOutputRejectedAtomically() (gas: 460474)\n[PASS] testPartialInputFillCreditsOnlyExecutedBuy() (gas: 406140)\n[PASS] testReplayOldCallbackHasNoLiveContext() (gas: 442856)\n[PASS] testSaleBoundaryExecutedInputIncludesPoolFee() (gas: 1063752)\n[PASS] testSlippageDeadlineAndNativeValueRejected() (gas: 539131)\n[PASS] testTransfersDoNotMoveRightsOrAllowChainCredit() (gas: 838559)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 74.75ms (63.71ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:FullyUnlockedInvariantTest\n[PASS]\nFullyUnlockedInvariantTest invariants:\n[PASS] invariantEligibleEqualsOnlyExecutedBuysAndSynchronizedProofs\n[PASS] invariantNoActorExceedsItsQuotaBeforeUnlock\n[PASS] invariantPrincipalLiquidityNeverDecreases\n[PASS] invariantSupplyConservedAndRouterHasNoCustody\n[PASS] invariantUnlockRemainsPermanentDespiteInvalidFeed\n FullyUnlockedInvariantTest invariants (runs: 128, calls: 5120, reverts: 0)\n\n╭---------------+----------+-------+---------+----------╮\n| Contract      | Selector | Calls | Reverts | Discards |\n+=======================================================+\n| ActionHandler | advance  | 822   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | buy      | 877   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | claim    | 849   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | sell     | 884   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | sync     | 839   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | transfer | 849   | 0       | 0        |\n╰---------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 499999000000012808\n  Bound result 8474\n  Bound result 13018350644972794775617\n  Bound result 2896\n  Bound result 499999000000008193\n  Bound result 12\n  Bound result 157\n  Bound result 5\n  Bound result 499999002161749450\n  Bound result 499999000000007116\n  Bound result 87\n  Bound result 125\n  Bound result 128\n  Bound result 220\n  Bound result 2121\n  Bound result 2091\n  Bound result 3509\n  Bound result 4096859416665284\n  Bound result 158516411833631751\n  Bound result 31536001\n  Bound result 2\n  Bound result 217\n  Bound result 2088\n  Bound result 54\n  Bound result 12\n  Bound result 153976213517145844\n  Bound result 360757436137989314\n  Bound result 14\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.57s (1.54s CPU time)\n\nRan 1 test for test/Invariants.t.sol:SystemInvariantTest\n[PASS]\nSystemInvariantTest invariants:\n[PASS] invariantEligibleEqualsOnlyExecutedBuysAndSynchronizedProofs\n[PASS] invariantNoActorExceedsItsQuotaBeforeUnlock\n[PASS] invariantPrincipalLiquidityNeverDecreases\n[PASS] invariantSupplyConservedAndRouterHasNoCustody\n SystemInvariantTest invariants (runs: 128, calls: 5120, reverts: 0)\n\n╭---------------+----------+-------+---------+----------╮\n| Contract      | Selector | Calls | Reverts | Discards |\n+=======================================================+\n| ActionHandler | advance  | 822   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | buy      | 877   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | claim    | 849   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | sell     | 884   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | sync     | 839   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | transfer | 849   | 0       | 0        |\n╰---------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 499999000000010440\n  Bound result 7789\n  Bound result 13018350644972794775617\n  Bound result 0\n  Bound result 499999000000000450\n  Bound result 12\n  Bound result 223\n  Bound result 158\n  Bound result 499999000000000348\n  Bound result 499999000000005742\n  Bound result 87\n  Bound result 38\n  Bound result 42\n  Bound result 220\n  Bound result 973\n  Bound result 8525\n  Bound result 2765\n  Bound result 4096859416665284\n  Bound result 302089960269922142\n  Bound result 11903\n  Bound result 2\n  Bound result 199\n  Bound result 4228666474\n  Bound result 54\n  Bound result 12\n  Bound result 499999000000011098\n  Bound result 106391653717924078\n  Bound result 14\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.67s (1.64s CPU time)\n\nRan 14 test suites in 1.67s (3.84s CPU time): 121 tests passed, 0 failed, 0 skipped (121 total tests)\n","passed":true},{"durationMs":85,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"HookDeployer.deploy(bytes32,(address,address,address,address,address,address,address,int24))\",\"MerkleClaimEvidence.attest(uint256,address,uint256,bytes32[])\",\"MerkleClaimEvidence.configure(address)\",\"UnlkHook.activate(uint64)\",\"UnlkHook.afterAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"UnlkHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"UnlkHook.afterRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"UnlkHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"UnlkHook.beforeAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"UnlkHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"UnlkHook.beforeRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"UnlkHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"UnlkHook.syncPayer()\",\"UnlkHook.syncRewards(address)\",\"UnlkHook.updateMilestone()\",\"UnlkOracle.bind((address,address,uint24,int24,address))\",\"UnlkOracle.checkpoint()\",\"UnlkOracle.configureHook(address)\",\"UnlkOracle.setLiquidityRange(int24,int24)\",\"UnlkSwapRouter.configureHook(address)\",\"UnlkSwapRouter.receive()\",\"UnlkSwapRouter.swap((bool,int256,uint160,uint256,uint256,uint256))\",\"UnlkSwapRouter.swapBatch((bool,int256,uint160,uint256,uint256,uint256)[])\",\"UnlkSwapRouter.unlockCallback(bytes)\",\"UnlkSwapRouter.withdrawNative()\",\"UnlkToken.approve(address,uint256)\",\"UnlkToken.transfer(address,uint256)\",\"UnlkToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"LICENSE\":25,\"README.md\":101,\"dependencies.files.json\":409,\"dependencies.formatted.json\":1082,\"dependencies.lock.json\":29,\"docs/ARCHITECTURE.md\":66,\"docs/DEPENDENCY_VERIFICATION.md\":23,\"docs/DEPLOYMENT.md\":108,\"docs/GITHUB_HANDOFF.md\":22,\"docs/IMD_INTEGRATION.md\":76,\"docs/SECURITY_REVIEW.md\":73,\"docs/SEPOLIA_READINESS.md\":38,\"docs/SEPOLIA_RELEASE.md\":227,\"docs/VALIDATION.md\":73,\"docs/abi/HookDeployer.json\":233,\"docs/abi/IChainlinkEthUsd.json\":48,\"docs/abi/IClaimEvidence.json\":47,\"docs/abi/IHooks.json\":789,\"docs/abi/IMerkleDistributor.json\":125,\"docs/abi/IPoolManager.json\":1286,\"docs/abi/IRouteContext.json\":20,\"docs/abi/MerkleClaimEvidence.json\":243,\"docs/abi/UnlkHook.json\":1636,\"docs/abi/UnlkOracle.json\":500,\"docs/abi/UnlkSwapRouter.json\":398,\"docs/abi/UnlkToken.json\":367,\"docs/validation/build.txt\":1,\"docs/validation/contract-sizes.json\":26,\"docs/validation/dependencies.txt\":1,\"docs/validation/lint-high-medium.txt\":358,\"docs/validation/lint-high.txt\":0,\"docs/validation/sepolia-fork-rehearsal.txt\":30,\"docs/validation/tests.txt\":15,\"foundry.toml\":31,\"handoff/inventory-digest.txt\":1,\"handoff/inventory.sha256\":479,\"remappings.txt\":4,\"script/CheckReleaseConfig.s.sol\":63,\"script/LocalDemo.s.sol\":41,\"script/SepoliaForkRehearsal.s.sol\":394,\"scripts/check.sh\":11,\"scripts/check_dependencies.py\":135,\"scripts/export_abis.py\":26,\"scripts/mine-hook.sh\":9,\"scripts/source_inventory.py\":24,\"scripts/verify_artifacts.py\":19,\"src/HookDeployer.sol\":51,\"src/HookFlags.sol\":45,\"src/MerkleClaimEvidence.sol\":85,\"src/UnlkHook.sol\":457,\"src/UnlkOracle.sol\":225,\"src/UnlkSwapRouter.sol\":239,\"src/UnlkToken.sol\":22,\"src/interfaces/IIntegration.sol\":81,\"src/libraries/OperationHash.sol\":14,\"test/Adversarial.t.sol\":117,\"test/CreditsAndFees.t.sol\":220,\"test/Invariants.t.sol\":215,\"test/Launch.t.sol\":312,\"test/LocalScript.t.sol\":28,\"test/NativeLaunch.t.sol\":234,\"test/Oracle.t.sol\":331,\"test/ReleaseConfig.t.sol\":24,\"test/Trading.t.sol\":277,\"test/fixtures/LocalFixtures.sol\":59,\"test/fixtures/SystemFixture.sol\":365,\"test/imd/HookFlags.sol\":45,\"test/imd/LaunchFactory.sol\":374,\"test/imd/LaunchFees.sol\":73,\"test/imd/LaunchRegistry.sol\":109,\"test/imd/MerkleDistributor.sol\":181,\"test/imd/PoolFees.sol\":219,\"test/imd/README.md\":19,\"test/mocks/MockERC20.sol\":12},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":3290,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/UnlkSwapRouter.sol:113: Reentrancy in UnlkSwapRouter._swap(UnlkSwapRouter.SwapRequest,uint256) (src/UnlkSwapRouter.sol#113-176):\n[high/medium] reentrancy-balance at src/UnlkSwapRouter.sol:113: Reentrancy in UnlkSwapRouter._swap(UnlkSwapRouter.SwapRequest,uint256) (src/UnlkSwapRouter.sol#113-176):\n[medium/medium] divide-before-multiply at src/UnlkOracle.sol:157: UnlkOracle.twapTick() (src/UnlkOracle.sol#157-182) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/UnlkOracle.sol:121: UnlkOracle.checkpoint() (src/UnlkOracle.sol#121-150) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/UnlkOracle.sol:121: UnlkOracle.checkpoint() (src/UnlkOracle.sol#121-150) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/UnlkOracle.sol:157: UnlkOracle.twapTick() (src/UnlkOracle.sol#157-182) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/UnlkHook.sol:211: Reentrancy in UnlkHook.afterSwap(address,PoolKey,IPoolManager.SwapParams,BalanceDelta,bytes) (src/UnlkHook.sol#211-242):\n[medium/medium] uninitialized-local at src/UnlkHook.sol:332: UnlkHook.activate(uint64).recordedHook (src/UnlkHook.sol#332) is a local variable never initialized\n[medium/medium] uninitialized-local at src/UnlkHook.sol:334: UnlkHook.activate(uint64).distributor (src/UnlkHook.sol#334) is a local variable never initialized\n[medium/medium] uninitialized-local at src/UnlkHook.sol:333: UnlkHook.activate(uint64).recordedToken (src/UnlkHook.sol#333) is a local variable never initialized\n[medium/medium] uninitialized-local at src/UnlkSwapRouter.sol:124: UnlkSwapRouter._swap(UnlkSwapRouter.SwapRequest,uint256).state (src/UnlkSwapRouter.sol#124) is a local variable never initialized\n[medium/medium] unused-return at src/UnlkHook.sol:188: UnlkHook.beforeSwap(address,PoolKey,IPoolManager.SwapParams,bytes) (src/UnlkHook.sol#188-209) ignores return value by (None,None,protocolFee,lpFee) = poolManager.getSlot0(poolId) (src/UnlkHook.sol#203)\n[low/medium] events-maths at src/UnlkOracle.sol:106: UnlkOracle.setLiquidityRange(int24,int24) (src/UnlkOracle.sol#106-113) should emit an event for: \n[low/medium] events-maths at src/UnlkHook.sol:244: UnlkHook.beforeAddLiquidity(address,PoolKey,IPoolManager.ModifyLiquidityParams,bytes) (src/UnlkHook.sol#244-261) should emit an event for: \n[low/medium] missing-zero-check at src/UnlkOracle.sol:59: UnlkOracle.constructor(IPoolManager,IChainlinkEthUsd,address,address,uint256,uint128).quoteToken_ (src/UnlkOracle.sol#59) lacks a zero-check on :\n[low/medium] calls-loop at src/UnlkSwapRouter.sol:113: UnlkSwapRouter._swap(UnlkSwapRouter.SwapRequest,uint256) (src/UnlkSwapRouter.sol#113-176) has external calls inside a loop: state.outputToken.balanceOf(msg.sender) != outputBefore + output (src/UnlkSwapRouter.sol#167)\n[low/medium] calls-loop at src/UnlkSwapRouter.sol:113: UnlkSwapRouter._swap(UnlkSwapRouter.SwapRequest,uint256) (src/UnlkSwapRouter.sol#113-176) has external calls inside a loop: outputBefore = state.outputToken.balanceOf(msg.sender) (src/UnlkSwapRouter.sol#165)\n[low/medium] calls-loop at src/UnlkSwapRouter.sol:113: UnlkSwapRouter._swap(UnlkSwapRouter.SwapRequest,uint256) (src/UnlkSwapRouter.sol#113-176) has external calls inside a loop: state.baseInput = state.inputToken.balanceOf(address(this)) (src/UnlkSwapRouter.sol#130-132)\n[low/medium] calls-loop at src/UnlkSwapRouter.sol:113: UnlkSwapRouter._swap(UnlkSwapRouter.SwapRequest,uint256) (src/UnlkSwapRouter.sol#113-176) has external calls inside a loop: key = hook.poolKey() (src/UnlkSwapRouter.sol#123)\n[low/medium] calls-loop at src/UnlkSwapRouter.sol:113: UnlkSwapRouter._swap(UnlkSwapRouter.SwapRequest,uint256) (src/UnlkSwapRouter.sol#113-176) has external calls inside a loop: state.inputToken.balanceOf(address(this)) != state.baseInput + request.maxInput (src/UnlkSwapRouter.sol#139)\n[low/medium] calls-loop at src/UnlkSwapRouter.sol:113: UnlkSwapRouter._swap(UnlkSwapRouter.SwapRequest,uint256) (src/UnlkSwapRouter.sol#113-176) has external calls inside a loop: address(hook) == address(0) || ! hook.bound() || block.timestamp > request.deadline || request.amountSpecified == 0 || request.amountSpecified == type()(int256).min || request.maxInput == 0 || request.maxInput > uint256(uint128(type()(int128).max)) || request.minOutput == 0 (src/UnlkSwapRouter.sol#118-121)\n[low/medium] calls-loop at src/UnlkSwapRouter.sol:182: UnlkSwapRouter._balance(IERC20) (src/UnlkSwapRouter.sol#182-184) has external calls inside a loop: currency.balanceOf(address(this)) (src/UnlkSwapRouter.sol#183)\n[low/medium] calls-loop at src/UnlkSwapRouter.sol:113: UnlkSwapRouter._swap(UnlkSwapRouter.SwapRequest,uint256) (src/UnlkSwapRouter.sol#113-176) has external calls inside a loop: state.baseOutput = state.outputToken.balanceOf(address(this)) (src/UnlkSwapRouter.sol#133-134)\n[low/medium] calls-loop at src/UnlkSwapRouter.sol:113: UnlkSwapRouter._swap(UnlkSwapRouter.SwapRequest,uint256) (src/UnlkSwapRouter.sol#113-176) has external calls inside a loop: result = poolManager.unlock(abi.encode(key,params,operationNonce,request.maxInput)) (src/UnlkSwapRouter.sol#149)\n[low/medium] reentrancy-benign at src/UnlkSwapRouter.sol:113: Reentrancy in UnlkSwapRouter._swap(UnlkSwapRouter.SwapRequest,uint256) (src/UnlkSwapRouter.sol#113-176):\n[low/medium] reentrancy-benign at src/UnlkHook.sol:402: Reentrancy in UnlkHook.updateMilestone() (src/UnlkHook.sol#402-406):\n[low/medium] reentrancy-benign at src/UnlkHook.sol:211: Reentrancy in UnlkHook.afterSwap(address,PoolKey,IPoolManager.SwapParams,BalanceDelta,bytes) (src/UnlkHook.sol#211-242):\n[low/medium] reentrancy-events at src/UnlkHook.sol:211: Reentrancy in UnlkHook.afterSwap(address,PoolKey,IPoolManager.SwapParams,BalanceDelta,bytes) (src/UnlkHook.sol#211-242):\n[low/medium] reentrancy-events at src/UnlkHook.sol:402: Reentrancy in UnlkHook.updateMilestone() (src/UnlkHook.sol#402-406):\n[low/medium] reentrancy-events at src/UnlkHook.sol:328: Reentrancy in UnlkHook.activate(uint64) (src/UnlkHook.sol#328-361):\n[low/medium] timestamp at src/UnlkOracle.sol:185: UnlkOracle.fdvUsd() (src/UnlkOracle.sol#185-199) uses timestamp for comparisons\n[low/medium] timestamp at src/UnlkOracle.sol:121: UnlkOracle.checkpoint() (src/UnlkOracle.sol#121-150) uses timestamp for comparisons\n[low/medium] timestamp at src/UnlkOracle.sol:201: UnlkOracle._ethUsd() (src/UnlkOracle.sol#201-214) uses timestamp for comparisons\n[low/medium] timestamp at src/UnlkHook.sol:418: UnlkHook._updateMilestone() (src/UnlkHook.sol#418-447) uses timestamp for comparisons\n[low/medium] timestamp at src/UnlkSwapRouter.sol:113: UnlkSwapRouter._swap(UnlkSwapRouter.SwapRequest,uint256) (src/UnlkSwapRouter.sol#113-176) uses timestamp for comparisons\n[low/medium] timestamp at src/UnlkOracle.sol:157: UnlkOracle.twapTick() (src/UnlkOracle.sol#157-182) uses timestamp for comparisons","passed":true},{"durationMs":645,"exitCode":0,"name":"aderyn","output":"[high] eth-send-unchecked-address at src/UnlkSwapRouter.sol:194: ETH transferred without address checks\n[high] reentrancy-state-change at src/MerkleClaimEvidence.sol:53: Reentrancy: State change after external call (13 places)\n[high] unprotected-initializer at src/HookDeployer.sol:11: Unprotected initializer (2 places)\n[low] costly-loop at src/UnlkHook.sol:431: Costly operations inside loop (2 places)\n[low] internal-function-used-once at src/HookFlags.sol:35: Internal Function Used Only Once\n[low] large-numeric-literal at src/UnlkHook.sol:232: Large Numeric Literal (16 places)\n[low] literal-instead-of-constant at src/UnlkHook.sol:196: Literal Instead of Constant (12 places)\n[low] local-variable-shadowing at src/UnlkHook.sol:85: Local Variable Shadows State Variable (7 places)\n[low] missing-inheritance at src/UnlkOracle.sol:15: Missing Inheritance\n[low] require-revert-in-loop at src/UnlkSwapRouter.sol:104: Loop Contains `require`/`revert`\n[low] state-change-without-event at src/UnlkHook.sol:188: State Change Without Event (5 places)\n[low] unchecked-return at src/UnlkHook.sol:240: Unchecked Return\n[low] unused-public-function at src/HookDeployer.sol:31: Public Function Not Used Internally","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"072e83a060146fe00776d0b62d50b24096a4ce9f0b24f0a07f68e0b78d8794ae","verifiedTreeHash":"359065cb591064c2c7c0d62a67a87d204b2221c4","verifierVersion":"0.1.0+fb8278c5"},{"checks":[{"durationMs":14027,"exitCode":0,"name":"build","output":"Compiling 117 files with Solc 0.8.26\nSolc 0.8.26 finished in 13.92s\nCompiler run successful!\n","passed":true},{"durationMs":2779,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/SepoliaRelease.t.sol:SepoliaForkReleaseTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 774.49µs (0.00ns CPU time)\n\nRan 2 tests for test/ReleaseConfig.t.sol:ReleaseConfigTest\n[PASS] testReadOnlyReleaseChecksOnLocalFixture() (gas: 1373116)\n[PASS] testReadOnlyReleaseChecksRejectWrongChainOrHash() (gas: 1299155)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 22.63ms (303.75µs CPU time)\n\nRan 2 tests for test/LocalScript.t.sol:LocalScriptTest\n[PASS] testLocalScriptRefusesPublicChainId() (gas: 14568602)\n[PASS] testLocalSimulationFunctionDoesNotDependOnCaller() (gas: 82665316)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 28.72ms (28.25ms CPU time)\n\nRan 8 tests for test/Launch.t.sol:LaunchTest\n[PASS] testFactoryHoldsPrincipalAndCannotRemoveIt() (gas: 321578)\n[PASS] testLaunchAllocationAndLockedPrincipal() (gas: 256081)\n[PASS] testLaunchIsOnceAndDeployerOnly() (gas: 1798844)\n[PASS] testOneTimeSetup() (gas: 120638)\n[PASS] testOnlyCanonicalManagerCanCallHook() (gas: 66673)\n[PASS] testPayerCreditExactDeduplicated() (gas: 143245)\n[PASS] testUnauthorizedFactoryAndWrongPoolRejected() (gas: 80542)\n[PASS] testUnconfiguredComponentsFailClosed() (gas: 39605)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 35.59ms (1.45ms CPU time)\n\nRan 12 tests for test/CreditsAndFees.t.sol:CreditsAndFeesTest\n[PASS] testAttestationRequiresActualClaimAndExactLeaf() (gas: 451572)\n[PASS] testClaimTransferAndPayerTransferDoNotDuplicateCredit() (gas: 589610)\n[PASS] testFeeCollectionDoesNotReducePrincipal() (gas: 688055)\n[PASS] testNativeClaimNeverDependsOnAdapterOrFeed() (gas: 866498)\n[PASS] testNativeClaimThenAttestThenSyncThenSale() (gas: 1332504)\n[PASS] testNegativeLiquidityForbiddenEvenForFactory() (gas: 82460)\n[PASS] testPoolFee125BpsNoHookFeeAndDistribution80_20() (gas: 861566)\n[PASS] testProofReleaseAndDedupRemainNativeRules() (gas: 587366)\n[PASS] testRequesterShareIsTransferableOnlyByRequesterAndOwedOnRejection() (gas: 1307010)\n[PASS] testSaleFeesCountGrossInputAndNeverCreditFeeRecipients() (gas: 1209780)\n[PASS] testUnauthorizedLiquidityAndDonationNoCredit() (gas: 241891)\n[PASS] testUnclaimedRewardsNotEligible() (gas: 53804)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 34.87ms (7.01ms CPU time)\n\nRan 7 tests for test/MerkleRounds.t.sol:MerkleRoundsTest\n[PASS] testClaimantsCanSellOnlyTheirQuotaAndNeverOthersCredit() (gas: 2288977)\n[PASS] testEachRecipientClaimsOnceAndIsAttestedOncePerRound() (gas: 2454682)\n[PASS] testFuzzOnlyTheExactLeafAmountAttests(uint256) (runs: 256, μ: 203392, ~: 203381)\n[PASS] testLaterRoundIsTheTreasurysAndIsAttestedSeparately() (gas: 1186605)\n[PASS] testProofOfOneLeafCreditsNobodyElse() (gas: 784631)\n[PASS] testRoundZeroCommitsTheLaunchTree() (gas: 66448)\n[PASS] testSweptRoundCannotBeClaimedOrAttestedAfterwards() (gas: 675286)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 36.93ms (11.21ms CPU time)\n\nRan 11 tests for test/NativeLaunch.t.sol:NativeWithdrawalTest\n[PASS] testDeepNativePoolWarmsUpAndCanUnlock() (gas: 3316157)\n[PASS] testNativeBatchSettlement() (gas: 856062)\n[PASS] testNativeBuySellAndFeeDistribution() (gas: 1101036)\n[PASS] testNativeExactOutputRefundToAuthenticatedCaller() (gas: 444467)\n[PASS] testNativeSettlementResetsPriorSyncCurrency() (gas: 600534)\n[PASS] testNativeValueMismatchAndUnsolicitedSendRejected() (gas: 94878)\n[PASS] testNativeWithdrawalUsesCEIAndCannotDoubleWithdraw() (gas: 994890)\n[PASS] testOneSidedOpeningLocks87PercentWithoutEthFunding() (gas: 71416)\n[PASS] testOtherWalletCannotWithdrawNativeRefund() (gas: 456671)\n[PASS] testRejectingNativeWalletCanTradeAndRetryWithdrawal() (gas: 1070342)\n[PASS] testThinLaunchCannotUnlockFromInitialSpotOrTinyBuy() (gas: 4804292)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 36.98ms (8.60ms CPU time)\n\nRan 8 tests for test/Adversarial.t.sol:AdversarialTest\n[PASS] test100PercentAloneIsNotFullUnlock() (gas: 7254507)\n[PASS] testArbitraryHookDataAndMultihopPayloadCannotSelectVictim() (gas: 87489)\n[PASS] testCallbacksAllRequireCanonicalManager() (gas: 211647)\n[PASS] testForeignPoolKeyRejectedEvenWithManagerPrank() (gas: 49877)\n[PASS] testNoMintPauseBlacklistUpgradeOrCounterResetEntrypoint() (gas: 121542)\n[PASS] testProtocolFeeChangeCannotSilentlyAlterTradingEconomics() (gas: 385971)\n[PASS] testQuotaMultiplicationDoesNotOverflowAtMaxEligible() (gas: 242266)\n[PASS] testTokenMintsWholeSupplyToDeployerOnly() (gas: 41399)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 40.19ms (9.96ms CPU time)\n\nRan 14 tests for test/Oracle.t.sol:OracleTest\n[PASS] testBadChainlinkRoundsAndAnswersFailClosed() (gas: 2340801)\n[PASS] testConfirmationGapRestartsRatherThanCountingUnobservedTime() (gas: 2830848)\n[PASS] testEveryTierAndPermanentUnlock() (gas: 16129713)\n[PASS] testFallingPricesNeverLowerReachedTier() (gas: 4997438)\n[PASS] testFlashLoanCannotCrossMilestoneInItsTransaction() (gas: 1567754)\n[PASS] testInvalidDataResetsUnfinishedConfirmation() (gas: 3128695)\n[PASS] testNegativeTickAveragesRoundConservativelyForBothOrders() (gas: 768869)\n[PASS] testOneBlockSpikeCannotConfirm() (gas: 749285)\n[PASS] testOracleCannotBeReboundOrConfiguredByAnyone() (gas: 98354)\n[PASS] testSameTimestampManipulationHasNoTimeWeight() (gas: 771517)\n[PASS] testStaleCheckpointAndFeedPreventNewTierButNotTrades() (gas: 1293260)\n[PASS] testSustainedManipulationRequiresWindowAndConfirmation() (gas: 3118509)\n[PASS] testThresholdMappingBoundaries() (gas: 104606)\n[PASS] testWarmupAndExactWindow() (gas: 387476)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 49.89ms (22.15ms CPU time)\n\nRan 13 tests for test/SepoliaRelease.t.sol:SepoliaShapedLaunchTest\n[PASS] testAllocationAndSeedMatchTheRecordedRehearsal() (gas: 156185)\n[PASS] testExcessSaleRevertsWithQuotaExceededAndLeavesNoTrace() (gas: 1377885)\n[PASS] testFeeCollectionSplits80_20InEthAndLeavesPrincipal() (gas: 1103609)\n[PASS] testFuzzWithinQuotaSellsAndExcessRejected(uint96,uint16) (runs: 256, μ: 1299723, ~: 1330105)\nLogs:\n  Bound result 3999999000000013018\n  Bound result 2495\n\n[PASS] testNativeClaimObeysReleaseTimeAndCreditSyncIsSeparate() (gas: 2070627)\n[PASS] testOwnerBindingsAreOneShotAndActivationIsOnce() (gas: 145644)\n[PASS] testPoolKeyContainsTheEnforcingHookAtPolicyTerms() (gas: 157483)\n[PASS] testPrincipalRemovalIsRefusedHoweverItIsRelayed() (gas: 242765)\n[PASS] testRecordedRehearsalTradesReproduce() (gas: 1461962)\n[PASS] testReleaseConfigCheckPassesOnThisShape() (gas: 1370932)\n[PASS] testStrangerRouterIsRefusedAsSwapSender() (gas: 522356)\n[PASS] testTiersUnreachableFromTestTradesFailClosedAndTradingContinues() (gas: 5048216)\n[PASS] testTransferOnlyHolderCanTransferButNotSell() (gas: 858478)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 94.57ms (87.55ms CPU time)\n\nRan 7 tests for test/NativeLaunch.t.sol:NativeLaunchTest\n[PASS] testDeepNativePoolWarmsUpAndCanUnlock() (gas: 3316156)\n[PASS] testNativeBatchSettlement() (gas: 856062)\n[PASS] testNativeBuySellAndFeeDistribution() (gas: 1101025)\n[PASS] testNativeExactOutputRefundToAuthenticatedCaller() (gas: 444378)\n[PASS] testNativeValueMismatchAndUnsolicitedSendRejected() (gas: 94900)\n[PASS] testOneSidedOpeningLocks87PercentWithoutEthFunding() (gas: 71438)\n[PASS] testThinLaunchCannotUnlockFromInitialSpotOrTinyBuy() (gas: 4804292)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 101.93ms (6.28ms CPU time)\n\nRan 9 tests for test/Launch.t.sol:InitializationGuardTest\n[PASS] testActivationRefusesCompanionsThatDoNotPointBack() (gas: 7605260)\n[PASS] testActivationRequiresTheFactoryRecordOfThisHook() (gas: 7467895)\n[PASS] testForeignCallerCannotConfigureUnsetRouter() (gas: 32808)\n[PASS] testLaunchNeedsNoCompanionCodeAndStaysClosedWithoutActivation() (gas: 23160969)\n[PASS] testLaunchRejectsSeedThatSpendsPairedCurrency() (gas: 6578827)\n[PASS] testLaunchRejectsWrongFeeOrTickSpacing() (gas: 11141638)\n[PASS] testOnlyFactoryCanInitializeUnboundOfficialPool() (gas: 45076976)\n[PASS] testRewardAdapterBindsOnlyToTheActivatedHookThatNamesIt() (gas: 7980603)\n[PASS] testUnboundCreditsCannotBeSynchronized() (gas: 45115588)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 102.03ms (73.15ms CPU time)\n\nRan 16 tests for test/Trading.t.sol:TradingTest\n[PASS] testAnotherWalletCannotConsumeVictimsAllowanceOrQuota() (gas: 497902)\n[PASS] testBatchBuysThenSellsAndRollbackExcess() (gas: 1665805)\n[PASS] testDirectManagerAndForeignRouterSpoofingRejected() (gas: 642657)\n[PASS] testDirectSmartWalletOwnsRightsNotItsOwner() (gas: 717573)\n[PASS] testDonatedRouterBalancesCannotCreateCreditOrBeSwept() (gas: 504530)\n[PASS] testExactInputBuyCreditsOnlyDeliveredTokens() (gas: 469055)\n[PASS] testExactOutputBuyAndRefund() (gas: 411390)\n[PASS] testExactOutputSaleAccountsActualInput() (gas: 736403)\n[PASS] testExcessSaleRollsBackPoolBalancesOracleAndNonce() (gas: 824180)\n[PASS] testFuzzQuotaBoundary(uint96) (runs: 256, μ: 1034537, ~: 1035114)\nLogs:\n  Bound result 298374779107036372\n\n[PASS] testPartialExactOutputRejectedAtomically() (gas: 460474)\n[PASS] testPartialInputFillCreditsOnlyExecutedBuy() (gas: 406140)\n[PASS] testReplayOldCallbackHasNoLiveContext() (gas: 442856)\n[PASS] testSaleBoundaryExecutedInputIncludesPoolFee() (gas: 1063752)\n[PASS] testSlippageDeadlineAndNativeValueRejected() (gas: 539131)\n[PASS] testTransfersDoNotMoveRightsOrAllowChainCredit() (gas: 838559)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 100.83ms (72.48ms CPU time)\n\nRan 7 tests for test/FailurePaths.t.sol:RouterGuardTest\n[PASS] testBatchBoundaries() (gas: 7491376)\n[PASS] testCallbackAndReceiveRefuseEveryoneButTheManagerInContext() (gas: 178846)\n[PASS] testConfigureHookRejectsZeroAndAHookNamingAnotherRouter() (gas: 102784)\n[PASS] testExactInputLargerThanPrefundIsSlippage() (gas: 410750)\n[PASS] testFuzzDeadlineInThePastAlwaysRejected(uint32) (runs: 256, μ: 49889, ~: 49890)\n[PASS] testInsufficientAllowanceOrBalanceFailsBeforeAnySwap() (gas: 262494)\n[PASS] testRequestFieldBoundaries() (gas: 583766)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 147.42ms (40.94ms CPU time)\n\nRan 5 tests for test/FailurePaths.t.sol:CallbackGuardTest\n[PASS] testActivationNeedsASeedAndTheRegistryRecord() (gas: 280529)\n[PASS] testAfterInitializeAndEveryCallbackRequireTheBoundKey() (gas: 476017)\n[PASS] testBeforeInitializeRejectsEveryWrongTermThenBindsOnce() (gas: 444376)\n[PASS] testBoundButInactiveHookRefusesSwapsAndRecordsOnlyTheFirstSeedRange() (gas: 689483)\n[PASS] testOracleBindingGuards() (gas: 220142351)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 147.53ms (91.14ms CPU time)\n\nRan 8 tests for test/FailurePaths.t.sol:ConstructorGuardTest\n[PASS] testEvidenceConstructorRejectsZeroAuthority() (gas: 3908)\n[PASS] testHookConstructorAcceptsTickSpacingBoundaries() (gas: 364613138)\n[PASS] testHookConstructorRejectsEveryMissingWord() (gas: 668872)\n[PASS] testHookConstructorRejectsTickSpacingOutOfRangeAndPayerEqualToFactory() (gas: 389530)\n[PASS] testHookPermissionsMatchTheFlagsConstant() (gas: 59305336)\n[PASS] testHookRefusesAnAddressWithoutTheDeclaredFlags() (gas: 20781)\n[PASS] testOracleConstructorRejectsBadBounds() (gas: 6288)\n[PASS] testRouterConstructorRejectsZeroWords() (gas: 3966)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 192.11ms (247.21ms CPU time)\n\nRan 14 tests for test/Oracle.t.sol:Token0OracleTest\n[PASS] testBadChainlinkRoundsAndAnswersFailClosed() (gas: 2335687)\n[PASS] testConfirmationGapRestartsRatherThanCountingUnobservedTime() (gas: 2824604)\n[PASS] testEveryTierAndPermanentUnlock() (gas: 16095653)\n[PASS] testFallingPricesNeverLowerReachedTier() (gas: 4986596)\n[PASS] testFlashLoanCannotCrossMilestoneInItsTransaction() (gas: 1550470)\n[PASS] testInvalidDataResetsUnfinishedConfirmation() (gas: 3121617)\n[PASS] testNegativeTickAveragesRoundConservativelyForBothOrders() (gas: 766917)\n[PASS] testOneBlockSpikeCannotConfirm() (gas: 747404)\n[PASS] testOracleCannotBeReboundOrConfiguredByAnyone() (gas: 98810)\n[PASS] testSameTimestampManipulationHasNoTimeWeight() (gas: 769614)\n[PASS] testStaleCheckpointAndFeedPreventNewTierButNotTrades() (gas: 1289748)\n[PASS] testSustainedManipulationRequiresWindowAndConfirmation() (gas: 3111809)\n[PASS] testThresholdMappingBoundaries() (gas: 104606)\n[PASS] testWarmupAndExactWindow() (gas: 386642)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 1.04s (21.42ms CPU time)\n\nRan 1 test for test/Invariants.t.sol:FullyUnlockedInvariantTest\n[PASS]\nFullyUnlockedInvariantTest invariants:\n[PASS] invariantEligibleEqualsOnlyExecutedBuysAndSynchronizedProofs\n[PASS] invariantNoActorExceedsItsQuotaBeforeUnlock\n[PASS] invariantPrincipalLiquidityNeverDecreases\n[PASS] invariantSupplyConservedAndRouterHasNoCustody\n[PASS] invariantUnlockRemainsPermanentDespiteInvalidFeed\n FullyUnlockedInvariantTest invariants (runs: 128, calls: 5120, reverts: 0)\n\n╭---------------+----------+-------+---------+----------╮\n| Contract      | Selector | Calls | Reverts | Discards |\n+=======================================================+\n| ActionHandler | advance  | 847   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | buy      | 830   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | claim    | 854   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | sell     | 840   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | sync     | 855   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | transfer | 894   | 0       | 0        |\n╰---------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 2558132088\n  Bound result 0\n  Bound result 215\n  Bound result 2578\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 499999000000009936\n  Bound result 0\n  Bound result 0\n  Bound result 503615006\n  Bound result 0\n  Bound result 329705013865776703\n  Bound result 23274153231321806275387440\n  Bound result 295\n  Bound result 198\n  Bound result 6\n  Bound result 8817\n  Bound result 284\n  Bound result 230\n  Bound result 2362519550015371085186335\n  Bound result 174\n  Bound result 250\n  Bound result 38\n  Bound result 499999000000006211\n  Bound result 2110234840\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.70s (1.67s CPU time)\n\nRan 1 test for test/Invariants.t.sol:SystemInvariantTest\n[PASS]\nSystemInvariantTest invariants:\n[PASS] invariantEligibleEqualsOnlyExecutedBuysAndSynchronizedProofs\n[PASS] invariantNoActorExceedsItsQuotaBeforeUnlock\n[PASS] invariantPrincipalLiquidityNeverDecreases\n[PASS] invariantSupplyConservedAndRouterHasNoCustody\n SystemInvariantTest invariants (runs: 128, calls: 5120, reverts: 0)\n\n╭---------------+----------+-------+---------+----------╮\n| Contract      | Selector | Calls | Reverts | Discards |\n+=======================================================+\n| ActionHandler | advance  | 847   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | buy      | 830   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | claim    | 854   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | sell     | 840   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | sync     | 855   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| ActionHandler | transfer | 894   | 0       | 0        |\n╰---------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 2276628879\n  Bound result 0\n  Bound result 215\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 499999002038677586\n  Bound result 0\n  Bound result 0\n  Bound result 8051\n  Bound result 0\n  Bound result 329705013865776703\n  Bound result 570645264464485674538048\n  Bound result 295\n  Bound result 217\n  Bound result 6\n  Bound result 9028\n  Bound result 2957\n  Bound result 63\n  Bound result 247\n  Bound result 1\n  Bound result 174\n  Bound result 26\n  Bound result 213\n  Bound result 499999000000005975\n  Bound result 793\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.77s (1.74s CPU time)\n\nRan 16 tests for test/Trading.t.sol:Token0TradingTest\n[PASS] testAnotherWalletCannotConsumeVictimsAllowanceOrQuota() (gas: 483575)\n[PASS] testBatchBuysThenSellsAndRollbackExcess() (gas: 1650464)\n[PASS] testDirectManagerAndForeignRouterSpoofingRejected() (gas: 643484)\n[PASS] testDirectSmartWalletOwnsRightsNotItsOwner() (gas: 703685)\n[PASS] testDonatedRouterBalancesCannotCreateCreditOrBeSwept() (gas: 490414)\n[PASS] testExactInputBuyCreditsOnlyDeliveredTokens() (gas: 454939)\n[PASS] testExactOutputBuyAndRefund() (gas: 396086)\n[PASS] testExactOutputSaleAccountsActualInput() (gas: 720758)\n[PASS] testExcessSaleRollsBackPoolBalancesOracleAndNonce() (gas: 809743)\n[PASS] testFuzzQuotaBoundary(uint96) (runs: 256, μ: 1019277, ~: 1020059)\nLogs:\n  Bound result 1999999000000014458\n\n[PASS] testPartialExactOutputRejectedAtomically() (gas: 443226)\n[PASS] testPartialInputFillCreditsOnlyExecutedBuy() (gas: 392227)\n[PASS] testReplayOldCallbackHasNoLiveContext() (gas: 429159)\n[PASS] testSaleBoundaryExecutedInputIncludesPoolFee() (gas: 1048997)\n[PASS] testSlippageDeadlineAndNativeValueRejected() (gas: 521863)\n[PASS] testTransfersDoNotMoveRightsOrAllowChainCredit() (gas: 824122)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 2.14s (70.72ms CPU time)\n\nRan 6 tests for test/NativeInvariants.t.sol:NativeFullyUnlockedInvariantTest\n[PASS] invariantEthConservedAndNobodyWithdrawsMoreThanCredited() (runs: 128, calls: 5120, reverts: 0)\n\n╭---------------------+-----------+-------+---------+----------╮\n| Contract            | Selector  | Calls | Reverts | Discards |\n+==============================================================+\n| NativeActionHandler | advance   | 527   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | attack    | 502   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | batch     | 515   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | buy       | 525   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | claim     | 506   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | claimFees | 498   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | sell      | 530   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | sync      | 484   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | transfer  | 544   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | withdraw  | 489   | 0       | 0        |\n╰---------------------+-----------+-------+---------+----------╯\n\nLogs:\n  Bound result 294\n  Bound result 999999000000002319\n  Bound result 220\n  Bound result 240\n  Bound result 242\n  Bound result 999999000000011942\n  Bound result 189\n  Bound result 1723280963667512760\n  Bound result 285\n  Bound result 6203\n  Bound result 999999014672504223\n  Bound result 13053764735409\n  Bound result 999999000000010295\n  Bound result 12322628130659497526426443\n  Bound result 20242729092148508983029361\n  Bound result 5310583147193831796701664\n  Bound result 722\n  Bound result 999999000000001875\n  Bound result 11783\n  Bound result 999999000000000457\n  Bound result 8443\n  Bound result 13814\n  Bound result 1999999000000007025\n\n[PASS] invariantPrincipalBindingsAndPoolTermsAreFixed() (runs: 128, calls: 5120, reverts: 0)\n\n╭---------------------+-----------+-------+---------+----------╮\n| Contract            | Selector  | Calls | Reverts | Discards |\n+==============================================================+\n| NativeActionHandler | advance   | 527   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | attack    | 502   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | batch     | 515   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | buy       | 525   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | claim     | 506   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | claimFees | 498   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | sell      | 530   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | sync      | 484   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | transfer  | 544   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | withdraw  | 489   | 0       | 0        |\n╰---------------------+-----------+-------+---------+----------╯\n\nLogs:\n  Bound result 294\n  Bound result 999999000000002319\n  Bound result 220\n  Bound result 240\n  Bound result 242\n  Bound result 999999000000011942\n  Bound result 189\n  Bound result 1723280963667512760\n  Bound result 285\n  Bound result 6203\n  Bound result 999999014672504223\n  Bound result 13053764735409\n  Bound result 999999000000010295\n  Bound result 12322628130659497526426443\n  Bound result 15417711704369723566813988\n  Bound result 8906858952312383601019427\n  Bound result 722\n  Bound result 999999000000001875\n  Bound result 11783\n  Bound result 999999000000000457\n  Bound result 8443\n  Bound result 13814\n  Bound result 1999999000000007025\n\n[PASS] invariantQuotasHoldBeforeUnlock() (runs: 128, calls: 5120, reverts: 0)\n\n╭---------------------+-----------+-------+---------+----------╮\n| Contract            | Selector  | Calls | Reverts | Discards |\n+==============================================================+\n| NativeActionHandler | advance   | 527   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | attack    | 502   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | batch     | 515   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | buy       | 525   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | claim     | 506   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | claimFees | 498   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | sell      | 530   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | sync      | 484   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | transfer  | 544   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | withdraw  | 489   | 0       | 0        |\n╰---------------------+-----------+-------+---------+----------╯\n\nLogs:\n  Bound result 294\n  Bound result 999999000000002319\n  Bound result 220\n  Bound result 240\n  Bound result 242\n  Bound result 999999000000011942\n  Bound result 189\n  Bound result 1723280963667512760\n  Bound result 285\n  Bound result 6203\n  Bound result 999999014672504223\n  Bound result 13053764735409\n  Bound result 999999000000010295\n  Bound result 12322628130659497526426443\n  Bound result 20242729092148508983029363\n  Bound result 5310583147193831796701936\n  Bound result 722\n  Bound result 999999000000001875\n  Bound result 11783\n  Bound result 999999000000000457\n  Bound result 8443\n  Bound result 13814\n  Bound result 1999999000000007025\n\n[PASS] invariantRouterHoldsExactlyWhatItOwesInEth() (runs: 128, calls: 5120, reverts: 0)\n\n╭---------------------+-----------+-------+---------+----------╮\n| Contract            | Selector  | Calls | Reverts | Discards |\n+==============================================================+\n| NativeActionHandler | advance   | 527   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | attack    | 502   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | batch     | 515   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | buy       | 525   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | claim     | 506   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | claimFees | 498   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | sell      | 530   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | sync      | 484   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | transfer  | 544   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | withdraw  | 489   | 0       | 0        |\n╰---------------------+-----------+-------+---------+----------╯\n\nLogs:\n  Bound result 294\n  Bound result 999999000000002319\n  Bound result 220\n  Bound result 240\n  Bound result 242\n  Bound result 999999000000011942\n  Bound result 189\n  Bound result 1723280963667512760\n  Bound result 285\n  Bound result 6203\n  Bound result 999999014672504223\n  Bound result 13053764735409\n  Bound result 999999000000010295\n  Bound result 12322628130659497526426443\n  Bound result 20242729092148508983029363\n  Bound result 5310583147193831796701936\n  Bound result 722\n  Bound result 999999000000001875\n  Bound result 11783\n  Bound result 999999000000000457\n  Bound result 8443\n  Bound result 13814\n  Bound result 1999999000000007025\n\n[PASS] invariantSupplyConservedAndLedgersMatchGhosts() (runs: 128, calls: 5120, reverts: 0)\n\n╭---------------------+-----------+-------+---------+----------╮\n| Contract            | Selector  | Calls | Reverts | Discards |\n+==============================================================+\n| NativeActionHandler | advance   | 527   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | attack    | 502   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | batch     | 515   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | buy       | 525   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | claim     | 506   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | claimFees | 498   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | sell      | 530   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | sync      | 484   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | transfer  | 544   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | withdraw  | 489   | 0       | 0        |\n╰---------------------+-----------+-------+---------+----------╯\n\nLogs:\n  Bound result 294\n  Bound result 999999000000002319\n  Bound result 220\n  Bound result 240\n  Bound result 242\n  Bound result 999999000000011942\n  Bound result 189\n  Bound result 1723280963667512760\n  Bound result 285\n  Bound result 6203\n  Bound result 999999014672504223\n  Bound result 13053764735409\n  Bound result 999999000000010295\n  Bound result 12322628130659497526426443\n  Bound result 20242729092148508983029361\n  Bound result 5310583147193831796701664\n  Bound result 722\n  Bound result 999999000000001875\n  Bound result 11783\n  Bound result 999999000000000457\n  Bound result 8443\n  Bound result 13814\n  Bound result 1999999000000007025\n\n[PASS] invariantUnlockIsPermanentAndAuthenticationRemains() (runs: 64, calls: 2560, reverts: 0)\n\n╭---------------------+-----------+-------+---------+----------╮\n| Contract            | Selector  | Calls | Reverts | Discards |\n+==============================================================+\n| NativeActionHandler | advance   | 277   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | attack    | 236   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | batch     | 270   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | buy       | 264   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | claim     | 251   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | claimFees | 240   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | sell      | 251   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | sync      | 231   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | transfer  | 289   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | withdraw  | 251   | 0       | 0        |\n╰---------------------+-----------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 1999999003033974658\n  Bound result 12202679041432882552206989\n  Bound result 6\n  Bound result 999999000000096944\n  Bound result 1610746720\n  Bound result 573529108446027158\n  Bound result 282\n  Bound result 2826\n  Bound result 999999000000004173\n  Bound result 1\n  Bound result 3\n  Bound result 381485965570166140994143\n  Bound result 1999999000000011337\n  Bound result 169\n  Bound result 97906068047898350026204\n  Bound result 1933419352402393426\n  Bound result 98\n  Bound result 1999999000000000047\n  Bound result 8366\n  Bound result 999999000000006783\n  Bound result 18785\n  Bound result 10\n  Bound result 174999999999999999999\n  Bound result 7\n  Bound result 1000000000002\n  Bound result 274492783718691601865682\n\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 2.14s (7.61s CPU time)\n\nRan 1 test for test/NativeInvariants.t.sol:NativeSystemInvariantTest\n[PASS]\nNativeSystemInvariantTest invariants:\n[PASS] invariantEthConservedAndNobodyWithdrawsMoreThanCredited\n[PASS] invariantPrincipalBindingsAndPoolTermsAreFixed\n[PASS] invariantQuotasHoldBeforeUnlock\n[PASS] invariantRouterHoldsExactlyWhatItOwesInEth\n[PASS] invariantSupplyConservedAndLedgersMatchGhosts\n NativeSystemInvariantTest invariants (runs: 96, calls: 4800, reverts: 0)\n\n╭---------------------+-----------+-------+---------+----------╮\n| Contract            | Selector  | Calls | Reverts | Discards |\n+==============================================================+\n| NativeActionHandler | advance   | 489   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | attack    | 475   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | batch     | 474   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | buy       | 503   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | claim     | 470   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | claimFees | 464   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | sell      | 502   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | sync      | 458   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | transfer  | 503   | 0       | 0        |\n|---------------------+-----------+-------+---------+----------|\n| NativeActionHandler | withdraw  | 462   | 0       | 0        |\n╰---------------------+-----------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 0\n  Bound result 1582801529563467714\n  Bound result 130\n  Bound result 1000000000003\n  Bound result 118684634164089694780\n  Bound result 1681581843742575306\n  Bound result 999999000000013553\n  Bound result 15310\n  Bound result 0\n  Bound result 12\n  Bound result 100\n  Bound result 144369018291373905383520\n  Bound result 999999000000005724\n  Bound result 11952\n  Bound result 999999001877468908\n  Bound result 12111\n  Bound result 532453217248518351\n  Bound result 23801\n  Bound result 1999999000000007201\n  Bound result 32663093691523025744766\n  Bound result 403172687016978891\n  Bound result 2928136487850301896056\n  Bound result 194\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.67s (2.63s CPU time)\n\nRan 22 test suites in 2.67s (12.63s CPU time): 168 tests passed, 0 failed, 1 skipped (169 total tests)\n","passed":true},{"durationMs":100,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"HookDeployer.deploy(bytes32,(address,address,address,address,address,address,address,int24))\",\"MerkleClaimEvidence.attest(uint256,address,uint256,bytes32[])\",\"MerkleClaimEvidence.configure(address)\",\"UnlkHook.activate(uint64)\",\"UnlkHook.afterAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"UnlkHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"UnlkHook.afterRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"UnlkHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"UnlkHook.beforeAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"UnlkHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"UnlkHook.beforeRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"UnlkHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"UnlkHook.syncPayer()\",\"UnlkHook.syncRewards(address)\",\"UnlkHook.updateMilestone()\",\"UnlkOracle.bind((address,address,uint24,int24,address))\",\"UnlkOracle.checkpoint()\",\"UnlkOracle.configureHook(address)\",\"UnlkOracle.setLiquidityRange(int24,int24)\",\"UnlkSwapRouter.configureHook(address)\",\"UnlkSwapRouter.receive()\",\"UnlkSwapRouter.swap((bool,int256,uint160,uint256,uint256,uint256))\",\"UnlkSwapRouter.swapBatch((bool,int256,uint160,uint256,uint256,uint256)[])\",\"UnlkSwapRouter.unlockCallback(bytes)\",\"UnlkSwapRouter.withdrawNative()\",\"UnlkToken.approve(address,uint256)\",\"UnlkToken.transfer(address,uint256)\",\"UnlkToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"LICENSE\":25,\"README.md\":101,\"dependencies.files.json\":409,\"dependencies.formatted.json\":1082,\"dependencies.lock.json\":29,\"docs/ARCHITECTURE.md\":66,\"docs/DEPENDENCY_VERIFICATION.md\":23,\"docs/DEPLOYMENT.md\":108,\"docs/GITHUB_HANDOFF.md\":22,\"docs/IMD_INTEGRATION.md\":76,\"docs/SECURITY_REVIEW.md\":73,\"docs/SEPOLIA_READINESS.md\":38,\"docs/SEPOLIA_RELEASE.md\":227,\"docs/VALIDATION.md\":73,\"docs/abi/HookDeployer.json\":233,\"docs/abi/IChainlinkEthUsd.json\":48,\"docs/abi/IClaimEvidence.json\":47,\"docs/abi/IHooks.json\":789,\"docs/abi/IMerkleDistributor.json\":125,\"docs/abi/IPoolManager.json\":1286,\"docs/abi/IRouteContext.json\":20,\"docs/abi/MerkleClaimEvidence.json\":243,\"docs/abi/UnlkHook.json\":1636,\"docs/abi/UnlkOracle.json\":500,\"docs/abi/UnlkSwapRouter.json\":398,\"docs/abi/UnlkToken.json\":367,\"docs/validation/build.txt\":1,\"docs/validation/contract-sizes.json\":26,\"docs/validation/dependencies.txt\":1,\"docs/validation/lint-high-medium.txt\":358,\"docs/validation/lint-high.txt\":0,\"docs/validation/sepolia-fork-rehearsal.txt\":30,\"docs/validation/tests.txt\":15,\"foundry.toml\":31,\"handoff/inventory-digest.txt\":1,\"handoff/inventory.sha256\":479,\"remappings.txt\":4,\"script/CheckReleaseConfig.s.sol\":63,\"script/LocalDemo.s.sol\":41,\"script/SepoliaForkRehearsal.s.sol\":394,\"scripts/check.sh\":11,\"scripts/check_dependencies.py\":135,\"scripts/export_abis.py\":26,\"scripts/mine-hook.sh\":9,\"scripts/source_inventory.py\":24,\"scripts/verify_artifacts.py\":19,\"src/HookDeployer.sol\":51,\"src/HookFlags.sol\":45,\"src/MerkleClaimEvidence.sol\":85,\"src/UnlkHook.sol\":457,\"src/UnlkOracle.sol\":225,\"src/UnlkSwapRouter.sol\":239,\"src/UnlkToken.sol\":22,\"src/interfaces/IIntegration.sol\":81,\"src/libraries/OperationHash.sol\":14,\"test/Adversarial.t.sol\":115,\"test/CreditsAndFees.t.sol\":220,\"test/FailurePaths.t.sol\":613,\"test/Invariants.t.sol\":215,\"test/Launch.t.sol\":312,\"test/LocalScript.t.sol\":28,\"test/MerkleRounds.t.sol\":239,\"test/NativeInvariants.t.sol\":447,\"test/NativeLaunch.t.sol\":234,\"test/Oracle.t.sol\":331,\"test/README.md\":51,\"test/ReleaseConfig.t.sol\":24,\"test/SepoliaRelease.t.sol\":489,\"test/Trading.t.sol\":277,\"test/fixtures/LocalFixtures.sol\":59,\"test/fixtures/SystemFixture.sol\":365,\"test/imd/HookFlags.sol\":45,\"test/imd/LaunchFactory.sol\":374,\"test/imd/LaunchFees.sol\":73,\"test/imd/LaunchRegistry.sol\":109,\"test/imd/MerkleDistributor.sol\":181,\"test/imd/PoolFees.sol\":219,\"test/imd/README.md\":19,\"test/mocks/MockERC20.sol\":12},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"f521b1416987bd14698ac5948d7bbd5e57d1f847edddc76d11967a580028ccd1","verifiedTreeHash":"cac9bcb5dee5c8ef70376437c36cff2ed7aa2fea","verifierVersion":"0.1.0+fb8278c5"}]}