{"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":null,"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":"working"},{"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":null,"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":"working"}],"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":"0000000000000000000000000000000000000000000000000000000000000000","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":"51450","feedbackHash":"2be759fef1761b8f19ea9591ad3fb867a4c5b679e43b41497558021adb104820","nodeKey":"audit_flow","submissionHash":"9e14d264922a15f5436d635cd9d902a3864950e8fc2485c2b18c8b63ba53ff93","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51023","feedbackHash":"968fa3b42e9a7ddb495bf28351dbf7b388f1b17972cb8ff5b8325ac586455d1f","nodeKey":"audit_math","submissionHash":"829ebcd3743bdf2c613b16cdc4535da81b5d1fa2bf9d5dd6e2bbef6311ef5403","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52082","feedbackHash":"dbef08e1ba8c434c924b661a3e5659ca79cc148cb74a464025662a86aeee9bb1","nodeKey":"audit_permissions","submissionHash":"473f9e5bce1cc03fd8cc15cc30e09e7c4fdd1b81eecfd9f94fc6a0af2e219c0a","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52679","feedbackHash":"344ecb9cbe2225bc24d065cadca8a542160e32417e854c135e7545bb573a1670","nodeKey":"build_contract_project","submissionHash":"072e83a060146fe00776d0b62d50b24096a4ce9f0b24f0a07f68e0b78d8794ae","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"d1f7cbede3d8dfab60cafb2859ac55e9c635e06440dde045fc274f7e809d80aa","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":"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}}],"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"}]}