{"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":"46c695fa-7061-4e9f-a06a-659e1f72ee3b","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"35fd767b2491636250cb259532b3d742e582cba287018022bfa8aa9e02d07cd3","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":"00db2803f151e21bdb991e529e06be7aee1aa3300d205ea262fab4f38df9febe","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":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","state":"waiting"},{"acceptedSubmissionHash":"5e6977f2282623305514c42024afdd01d8d259d1f010b8fd0534c83ab3ded1aa","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":"8609882aa695710ae78859e1093672abf99ec38079d769e0abf220c6eda0203c","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":"88eb13467209315434218c2a8999c40107c0aecfc8c5031752f54a61631e9dba","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"587567a5ef71546b5973ceaf6ef1dbbd590f49503b5ac9a6973ed86b89584857","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"587567a5ef71546b5973ceaf6ef1dbbd590f49503b5ac9a6973ed86b89584857","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":"078d226ac5710d4505f91045096c3ac822b237535415aeedfd5b46403a63e06b","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":"pray: a token where selling is a prayer to the swarm. Buy freely; to sell cheaply you file a plea and a panel of IMD agents judges it through the IMD oracle. Approved sinners pay 1%, everyone else pays 50%, and the sinners' tax buys back and burns PRAY. Launch kind univ4_hook on Ethereum mainnet, paired with FWA (0xa0df17b5ac76ababa36e1450e2cbcd18a620c845): the launch's standard token and pool with our hook, plus our plea registry. Deploy in the launch, then host a site on the live addresses. No owner, admin, upgrade or pause in our contracts.\n\nToken name: pray. Symbol: PRAY.\n\nWhen the hook acts: on every swap in this pool. beforeSwap reads hookData and checks the blessing for sells; the fee is taken in the swap (buys and sells); filePlea is a separate call on our registry.\n\nFEES (hook, on top of the launch's pool fee; taken from the FWA side and kept in FWA, do not sell for ETH)\n- Buys: 1%. First 30 minutes after launch the buy fee decays linearly from 50% to 1%.\n- Sells with a valid blessing: 1%. Sells without one: 50%.\n- Every hook fee: 90% to the burn vault, 10% to 0xd01122bBfFd00fc96252c8b29867a5359a3bca13.\n- Burn vault: buyback() is callable by anyone, at most once per 60 s, up to 50,000 FWA per call; it swaps vault FWA for PRAY through the pool and burns it. Caller gets 0.5% of the FWA spent.\n\nPLEAS (the registry asks the IMD oracle on chain through the IMD Intake; no HTTP API is needed by the contracts)\n- IMD oracle on Ethereum: Intake 0x1397434cd35e8a9c8ac312a61d3a285eb31dea56, IMD 0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7, action id bytes32(\"oracle.request@oracle-1\"), price 0.5 IMD, attestation signer 0x5598aa9146215bc13eb26f2c692ad1461fd32982, EIP-712 domain {name \"IdentityMD Oracle\", version \"2\", chainId 1, verifyingContract = our registry}, OracleAttestation(bytes32 requestId,uint256 chainId,bytes32 questionHash,uint8 answerType,bytes answer,uint256 figure,uint64 fromBlock,uint64 toBlock,bytes32 blockHash,bytes32 panelJobId,uint16 panelSize,uint16 quorum,uint16 agreed,uint64 issuedAt,uint64 expiresAt). Docs: https://imdnotdead.fun/docs/ (\"Pay on chain\", \"Ask the oracle\").\n- filePlea(amount, text): the pleader approves 0.5 IMD to the registry first. The registry pulls 0.5 IMD, builds the oracle body on chain from a fixed template (v 1, chainId 1, window 1 hour, answerType bool, evidence panel, panelSize 7, quorum 5, validForSeconds 900, consumer = the registry; the question names the plea id, seller, PRAY amount and the plea text, JSON-escaped, text max 600 bytes; definitions pin the rubric: approve only if the plea is at least 20 words, specific and sincere, not abusive, not spam, not copied from an earlier plea), calls Intake.request with callback (registry, onOracleResult), and stores intake requestId -> plea. Max amount: 10% of the seller's PRAY balance. One open plea per wallet; one blessing per wallet per 24 h. Emits PleaFiled with the text.\n- onOracleResult(requestId, attestation, signature): only from the Intake, only for a pending plea; verify the EIP-712 signature against the signer above in the registry's domain, answerType bool, not expired; if the answer is true, the plea's seller is blessed for its amount for 15 minutes. Keep it under 200k gas (store, act later). A public submit() takes the same arguments for a missed callback, with the same checks.\n- Blessing: a sell through our pool names the seller in hookData via our router (else tx.origin); the hook asks the registry, and up to the blessed amount sells at 1% within the 15 minutes, then the blessing is spent.\n- The swarm sleeps: if no oracle answer reaches the registry for 24 h, every sell pays 1% until the next answer, so holders are never stuck if the oracle goes quiet.\n\nSITE\nBuy/sell PRAY vs FWA through our router; the plea form (approve 0.5 IMD, file the plea on chain, watch the panel vote live via https://api.imdnotdead.fun/oracle/requests, then sell with the blessing); a public feed of every plea with its text, verdict and the panel's votes; the sinners' tax collected and PRAY burned; swarm awake/asleep countdown; buyback button. Dark, candle-lit, minimal.\n\nTESTS\nFee per path (buy, blessed sell, unblessed sell, sleeping swarm) and the launch decay; 90/10 split exact; callbacks not from the Intake, unknown request ids, wrong signer or domain, expired, false answers, reused, wrong-seller or over-amount blessings all fail; plea text is escaped into valid JSON; one blessing per 24 h; sleep after 24 h without an oracle answer and wake on the next; buyback limits and burns; no function moves FWA or PRAY anywhere else. Mainnet fork: buy, blessed sell and unblessed sell through the real PoolManager with the hook, and verify a real attestation from the IMD oracle signer. Independent review.","parentJobId":null,"planHash":"50857cc99fa98bb2e82aa59ec7e057d0d3f1d7a663fb5d0fd65037305bd8f1e9","previousHash":"3738f5e1a7e414b9a40988eeac9776781269470c3ae401eaa7df625d1b25e9b5","projectId":"46c695fa-7061-4e9f-a06a-659e1f72ee3b","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1024-pray-symbol-pray"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51234","feedbackHash":"d57d386d925420fa326bb7c88d99574c7298825bde283ef013d389040ebce9a3","nodeKey":"audit_economics","submissionHash":"35fd767b2491636250cb259532b3d742e582cba287018022bfa8aa9e02d07cd3","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50986","feedbackHash":"92ab47e44449b1ac08bb470aa0179b271b8b243f8ab7f78afeaed3bf026d1dfe","nodeKey":"audit_flow","submissionHash":"00db2803f151e21bdb991e529e06be7aee1aa3300d205ea262fab4f38df9febe","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51082","feedbackHash":"74d93314bc046bf4538938998dc852dcb47aa1df6da599c3e292060ff9c0ecd0","nodeKey":"audit_math","submissionHash":"5e6977f2282623305514c42024afdd01d8d259d1f010b8fd0534c83ab3ded1aa","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51140","feedbackHash":"63a63ac9d40df43f4c50d8ea14291535f56ce1b4696a26a527212fba73a17bff","nodeKey":"audit_permissions","submissionHash":"8609882aa695710ae78859e1093672abf99ec38079d769e0abf220c6eda0203c","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51422","feedbackHash":"e2f2d670416a1b48c2eada86280ec431bbfbbab1785097c89992dcdc65f86710","nodeKey":"write_foundry_tests","submissionHash":"078d226ac5710d4505f91045096c3ac822b237535415aeedfd5b46403a63e06b","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"4a9ed879db3afec7768abda267bcd4252a8174f366018c9dbaf26c1321401ba2","state":"blocked","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"e8f1969b1b8ff9d4","findings":[],"hash":"004da0efb22b61e0efe1e4dd2ce6194952885710978bea65e2ac84885ea70109","nodeId":"d13b60bb-37a1-4198-862a-6ade70f3621b","outcome":"failed","summary":"needs input: The required pool.initialPrice (decimal sqrtPriceX96) is not specified. deployment-parameters.json leaves it to launch policy, but no policy price is supplied. The hook accepts any valid initial price; the 1:1 price in tests is only a fixture. The manifest schema permits no price placeholder, and choosing a launch valuation would require guessing. — What decimal sqrtPriceX96 should pool.initialPrice use for the Ethereum PRAY/FWA launch?","treeHash":null,"usage":{"cachedInputTokens":479104,"inputTokens":58468,"model":"gpt-6-astra","outputTokens":2143,"runtime":"codex","turns":3,"wallClockMs":106978}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"ef5038c1bdac3372","findings":[{"citation":"resolved","description":"For every swap whose `sender` is not the PRAY router, the hook attributes the sell to `tx.origin` and then reads `registry.available(tx.origin)`, charges 1% when that wallet is blessed, and `afterSwap` calls `registry.consume(tx.origin, prayDelta)`. Nothing ties the PRAY being sold to the blessed wallet: the PRAY leaves whichever account the swapping contract settles from. So any contract that a blessed wallet merely transacts with during its 15-minute window (a 'claim' button, a mint, an airdrop checker, any dapp) can sell ITS OWN PRAY through `PoolManager.unlock` with empty hookData, pay the blessed 1% instead of the 50% sinner tax, and consume the victim's blessing in the process. Blessed wallets and their exact 15-minute windows are public (`PleaAnswered` events), so targets are easy to find. The brief says 'else tx.origin', and the README documents the attribution, so this is a consequence of the requested rule rather than an implementation slip; it is reported because it directly contradicts the brief's first principle ('approved sinners pay 1%, everyone else pays 50%') and the uniswap-v4-security reference's rule against tx.origin for authorization. Loss per incident: 49% of the stranger's sell (FWA that should have gone 90/10 to the burn vault and treasury) plus the victim's whole remaining blessing. A design-preserving fix: honor `tx.origin` only when the blessed wallet has opted in (e.g. `registry.allowOriginAttribution[tx.origin]`, settable by that wallet), or honor it only when `tx.origin` itself is the swap `sender` (an EOA calling the PoolManager cannot be, so in practice: trust only the PRAY router and treat every other sender as unblessed).","line":149,"path":"src/Hook.sol","reproduction":"State: pool seeded, 30 min after launch, swarm awake; Alice blessed for 100 PRAY (registry.available(alice)==100e18). A contract Leech holds 100 PRAY. Alice sends a tx calling Leech.claim(); Leech does manager.unlock -> manager.swap(key, SwapParams(PRAY->FWA, -100e18, limit), \"\") and settles with its own PRAY. Expected (brief): Leech is not an approved sinner -> fee == 50% of FWA out (~97.76 FWA fee on ~97.76 received), Alice keeps 100e18 available. Actual: hook.totalFees() rises by 0.987 FWA (1%), Leech receives 97.76 FWA, registry.available(alice)==0. Run: forge test --match-path test/scratch/OriginBlessing.t.sol (fails on current code with 'stranger's sell was charged the 1% blessed rate').","severity":"low","snippet":"        address seller = sender == address(router) ? abi.decode(data, (address)) : tx.origin;","title":"tx.origin attribution lets any contract a blessed wallet calls sell its own PRAY at 1% and spend that wallet's blessing"},{"citation":"resolved","description":"The cooldown is global and is set by any successful call regardless of size, and the only lower bound on `budget` is non-zero. With budget = 201 wei: spend = 200, reward = 1, floor = buybackFloor(200) which is >= 1 whenever the TWAP quote of 196 wei net is >= 2 wei of PRAY, and the swap output (about 194 wei) clears it. The call succeeds, burns 194 wei of PRAY and sets `lastBuyback = block.timestamp`; a 50,000 FWA buyback in the same minute reverts `InvalidBuyback`. Anyone who prefers the vault not to buy (e.g. someone short PRAY, or a seller who wants the vault's FWA to stay unspent) can send one dust call per minute, paying only gas, and the honest keeper has to win a priority-fee race each window. The vault is never drained and nothing is lost, which is why this is low, but the brief's 'up to 50,000 FWA per call, once per 60 s' burn rate becomes a race rather than a guarantee. Fix: require a minimum spend (e.g. >= 1 FWA or a fraction of `balance()`), or make the cooldown proportional to spend, or let a larger budget within the same window supersede a dust one.","line":39,"path":"src/BurnVault.sol","reproduction":"State: a buy of 10,000 FWA has funded the vault, 30 min after launch. Bob calls vault.buyback(201): succeeds (burned 194 wei), vault.lastBuyback()==block.timestamp. Any caller in the same block or within 60 s calls vault.buyback(50_000 ether): expected (brief) a buyback up to 50,000 FWA; actual revert InvalidBuyback. Reproduced in test/scratch/Probe.t.sol::testDustBuybackOccupiesCooldown.","severity":"low","snippet":"        if (budget == 0 || budget > MAX_SPEND || block.timestamp < lastBuyback + 60) revert InvalidBuyback();","title":"A 201-wei buyback satisfies every check and occupies the global 60-second cooldown, so dust calls can starve real buybacks"},{"citation":"resolved","description":"`filePlea` checks `amount <= balanceOf(msg.sender)/10` only at filing; the PRAY is not locked, the cap is not re-checked when the blessing is granted or when it is consumed, and the 'one open plea / one blessing per 24 h' limits are per wallet. A holder of N PRAY therefore files for N/10 from wallet A, transfers all N to wallet B, files for N/10 again, and so on, k times in one block, paying 0.5 IMD per plea. Each approved plea blesses N/10, so the actor can sell k*N/10 at 1% within the windows (moving the tokens back to each wallet before its sell). The only remaining gate is the panel's judgement of k different texts. The brief phrases the limits per wallet, so this is a limitation of the specified design more than a coding error, but it means the 10% cap provides no bound on how much of a position can be sold cheaply per day; the README should say so, or the registry should lock the pleaded PRAY (e.g. escrow `amount` in the registry until the blessing expires or the plea settles), which makes the cap bind.","line":95,"path":"src/PleaRegistry.sol","reproduction":"State: alice holds 10,000,000 PRAY; five fresh wallets w0..w4 each hold 1 IMD and have approved the registry. In one block: alice.filePlea(1_000_000e18, text0); alice.transfer(w0, 10_000_000e18); w0.filePlea(1_000_000e18, text1); w0.transfer(w1, ...); ... w4.filePlea(...). Expected (brief's intent): a single holder can have at most 10% of their stack under plea. Actual: six open pleas totalling 60% of the same 10,000,000 PRAY (registry.openPlea(alice)==1, registry.openPlea(w4)==6). Reproduced in test/scratch/Probe2.t.sol::testTenPercentCapDefeatedByMovingTokensBetweenOwnWallets.","severity":"low","snippet":"            amount == 0 || amount > token.balanceOf(msg.sender) / 10 || openPlea[msg.sender] != 0","title":"The 10%-of-balance cap on a plea is a filing-time snapshot that one actor defeats in a single block by moving tokens between their own wallets"},{"citation":"resolved","description":"First-principles note rather than a code defect: the launch kind requires a standard, unrestricted ERC-20 (the protected token floor checks exact transfer semantics), so nothing stops anyone from initializing a PRAY/FWA or PRAY/WETH pool without this hook on the same PoolManager (or on Uniswap v2/v3) and selling there at 0% hook fee. 'Selling is a prayer' therefore holds only while this pool is the only liquid market. The README and site should state this plainly so holders and the buyback (which only reads this pool's TWAP) are not surprised; no fix inside the contracts is possible without a transfer-restricted token, which the launch policy forbids.","line":6,"path":"src/Token.sol","reproduction":"On the same PoolManager, initialize PoolKey(currency0=min(PRAY,FWA), currency1=max, fee=3000, tickSpacing=60, hooks=address(0)), add liquidity, and swap PRAY->FWA. Expected under the brief's premise: an unblessed sell pays 50%. Actual: hook.totalFees() unchanged, 0% hook fee, no plea needed.","severity":"info","snippet":"contract Token is ERC20 {","title":"The sinner tax is enforced only on the hooked pool; PRAY is a plain ERC-20, so any second market sells it with no plea"}],"hash":"00db2803f151e21bdb991e529e06be7aee1aa3300d205ea262fab4f38df9febe","nodeId":"3985d3e3-070e-49cd-aac2-3036f48e28c8","outcome":"completed","summary":"Review complete. The findings file is written at `.imd-findings.json` with 4 findings and 22 coverage rows, every snippet verified against the tree, and the one proof test confirmed failing on current code.\n\n**What I covered**\n\nI traced all 18 verifier-listed entry points through the Execution Trace, Periphery and First Principles lenses, checking the hook's before/after-swap deltas against the vendored v4-core accounting, the router's sync/settle ordering, the vault's unlock commitment, the registry's attestation checks, and the canonical question-hash construction against the live oracle fixture. The existing 71-test suite passes, and my scratch probes confirmed the transient swap context resets correctly across multiple swaps in one unlock.\n\n**Findings (none above low)**\n\n- **Low, `src/Hook.sol:149`.** Any contract a blessed wallet transacts with during its 15-minute window can sell its own PRAY at 1% and consume the victim's blessing, because non-router sells are attributed to `tx.origin`. This follows the brief's \"else tx.origin\" rule, so I flagged the tradeoff and proposed an opt-in. Proof test attached and failing on current code.\n- **Low, `src/BurnVault.sol:39`.** A 201-wei buyback passes every check and occupies the global 60-second cooldown, so dust calls can turn the 50,000 FWA burn rate into a priority-fee race.\n- **Low, `src/PleaRegistry.sol:95`.** The 10%-of-balance cap is a filing-time snapshot; one actor moves the same stack through several wallets in one block and files for 10% from each.\n- **Info, `src/Token.sol:6`.** The sinner tax exists only on the hooked pool. A standard ERC-20 can be sold on any second market with no plea, which the launch policy makes unavoidable but the README should state.\n\n**What held**\n\nFee math on all four swap paths and the launch decay, the exact 90/10 claim split, callback authentication, question-hash binding across pleas, replay consumption, the 24-hour sleep and wake, blessing consumption bounds, buyback limits and burns, and the rule that no function moves FWA or PRAY to a caller-chosen address. The oracle callback stays under the 200k stipend in the cold case.\n\n**Not reached**\n\nLive Intake admission behavior (whether the wording screen refuses pleas) and the site's browser flows, both of which need network access I did not use for verification.","treeHash":null,"usage":{"cachedInputTokens":3160946,"inputTokens":514,"model":"claude-fable-5-1","outputTokens":73717,"runtime":"claude","turns":54,"wallClockMs":1128255}},{"artifacts":[],"attempt":1,"bundleHash":"1c109c1e1efc1bd4cbbe9a640c14cd92e8eb6e761bd2ab527d80bcc946edd8e8","device":"1d2eacca0a6e8449","findings":[],"hash":"078d226ac5710d4505f91045096c3ac822b237535415aeedfd5b46403a63e06b","nodeId":"d6c26aa7-27f2-467d-8575-9820bec63729","outcome":"completed","summary":"Added adversarial, fuzz, invariant, and mainnet fork tests under `test/`.\n\n- Build passed.\n- Offline: **104 passed**, 0 failed; optional fork suite skipped.\n- Mainnet fork: **3 passed**.\n- Invariants: **49,152 random calls**, 0 reverts.\n\nNo production defects confirmed. Implementation and configuration unchanged. Details in [test/README.md](test/README.md).","treeHash":"f5dd07788fec0bbf3899f341feb0cd3d6d12f053","usage":{"cachedInputTokens":4946688,"inputTokens":182760,"model":"gpt-6-astra","outputTokens":28269,"runtime":"codex","turns":8,"wallClockMs":1197615}},{"artifacts":[],"attempt":1,"bundleHash":"9370f0a4f9c5fb1ac767d46b3a7daa7445414f8749be5b497f52678992924a23","device":"96f910d48c47b222","findings":[],"hash":"27ff85e7466d223540d24f5ff505e393d63113d055613ab2c6d6cd069785c054","nodeId":"796a64a2-be19-4d86-a1f1-e1b9c778288a","outcome":"completed","summary":"Implemented PRAY’s contracts, site, launch tooling, vendored dependencies, and documentation. Independent review completed; all findings repaired.\n\nPassed: `forge build`, `forge test`, `forge fmt --check`, 39 delivered Solidity tests, two mainnet-fork tests, six site tests, and browser flows.\n\nMainnet deployment and public hosting remain pending the launch service’s live addresses.\n\n[Documentation](/home/imd-worker/.identitymd/work/46c695fa-7061-4e9f-a06a-659e1f72ee3b/796a64a2-be19-4d86-a1f1-e1b9c778288a/README.md) · [Independent review](/home/imd-worker/.identitymd/work/46c695fa-7061-4e9f-a06a-659e1f72ee3b/796a64a2-be19-4d86-a1f1-e1b9c778288a/docs/INDEPENDENT_REVIEW.md)","treeHash":"eebbc18665bcc6311de1a93d700123646c67ccc9","usage":{"cachedInputTokens":9230464,"inputTokens":283968,"model":"gpt-6-astra","outputTokens":87245,"runtime":"codex","turns":14,"wallClockMs":4388720}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"d00f790fc692b1a4","findings":[{"citation":"resolved","description":"filePlea enforces the brief's 'max amount: 10% of the seller's PRAY balance' only by reading token.balanceOf(msg.sender) at the moment of filing. Nothing later re-checks the seller's real holding: not onOracleResult/submit when the blessing is granted, not Hook.afterSwap when registry.consume spends it. The PoolManager holds the pool's entire PRAY reserve and lends it to anyone inside an unlock at zero cost: take(PRAY) creates a negative delta that is settled by transferring the tokens back before the unlock ends, with no fee and no counterparty. A contract can therefore take the pool's PRAY, call filePlea for 10% of the inflated balance, return the PRAY in the same transaction, and after the panel approves sell its entire real holding (100%, not 10%) at the blessed 1% rate instead of 50%. The 10% rule is the only quantitative limit on how much an approved sinner may dump cheaply per day; it is bypassed with one unlock callback. The same snapshot weakness lets one bag back several wallets' pleas by transferring it between them between filings (scratch run: 10,000,000 PRAY moved through three wallets produced 4,610,000 PRAY of blessings in one day, each paying 0.5 IMD). Minimal fix that keeps the design: snapshot the balance at filing, and require at answer time (and/or cap at consume time) that the seller still holds at least 10x the plea amount, or refuse filePlea while poolManager.isUnlocked() is true (which still leaves the multi-wallet shuffle).","line":95,"path":"src/PleaRegistry.sol","reproduction":"State: pool seeded, 30 minutes after launch, attacker contract F holds 1,000,000 PRAY and 0.5 IMD. (1) F calls poolManager.unlock; in unlockCallback: poolManager.take(PRAY, F, 9,000,000e18) from the pool's reserve, then registry.filePlea(1,000,000e18, text) succeeds because balanceOf(F) is now 10,000,000e18 so the cap is 1,000,000e18 (the honest cap is 100,000e18), then sync/transfer/settle returns the 9,000,000 PRAY; F's balance after the tx is unchanged at 1,000,000e18. (2) The oracle approves; submit/onOracleResult sets available(F) = 1,000,000e18. (3) F sells 1,000,000 PRAY exact-input through PrayRouter: Hook.beforeSwap sees available >= amount and sets rate = 100 bps; hook fee is 1% of gross FWA (totalFees == received/99, 968,374 FWA fee on 96.8M gross in the proof run). Expected per brief: at most 100,000 PRAY of that holding could be blessed and the other 900,000 should pay 50%. Proof test test/scratch/PleaCapFlashProof.t.sol fails on the current code with 'whole bag sold at the blessed 1% rate'.","severity":"medium","snippet":"            amount == 0 || amount > token.balanceOf(msg.sender) / 10 || openPlea[msg.sender] != 0","title":"Plea cap of 10% of PRAY balance is a filing-time balanceOf snapshot; a PoolManager flash take inflates it for free so a whole holding sells at 1%"},{"citation":"resolved","description":"The 60-second cooldown is global and is reset by any successful buyback regardless of size, and the only lower bound on budget is budget != 0 (plus floor > 0 and non-zero output). A griefer can therefore call buyback with a dust budget once per minute; each call succeeds (spends 2000 wei FWA, burns ~1947 wei PRAY, pays 10 wei reward) and sets lastBuyback, so every meaningful buyback submitted in that minute reverts with InvalidBuyback. Cost to the griefer is gas only; the vault's FWA (the sinners' tax) sits unspent for as long as the griefer keeps it up, and keepers who would earn the 0.5% reward on a 50,000 FWA call are front-run by the dust call in the same block. Minimal fix: require a minimum spend (for example a fraction of the vault balance or an absolute floor) or scale the cooldown by the fraction of MAX_SPEND actually spent.","line":39,"path":"src/BurnVault.sol","reproduction":"State: vault holds 90,000 FWA of claims (after a 200,000 FWA buy at 1% + 30 minutes). (1) bob calls vault.buyback(2010): spend = 2010*200/201 = 2000 wei, reward = 10 wei, buybackFloor(2000) = 1857 wei > 0, swap output 1947 wei >= floor, success; lastBuyback = now. (2) In the same minute alice calls vault.buyback(50_000 ether): reverts InvalidBuyback at the cooldown check. (3) bob repeats buyback(2010) at now+60, now+120, now+180; each succeeds and alice's 50,000 FWA buyback reverts every time; vault balance after four minutes is still 89,999.99 FWA. Scratch test testDustBuybackBlocksRealBuyback reproduced this against the delivered code.","severity":"low","snippet":"        if (budget == 0 || budget > MAX_SPEND || block.timestamp < lastBuyback + 60) revert InvalidBuyback();","title":"buyback has no minimum budget: a 2010-wei call every 60 s resets the cooldown and starves real buybacks"},{"citation":"resolved","description":"The floor protects the vault only against a spot move inside the same transaction. The tick cumulative is advanced with lastTick (the tick after the previous swap) for the whole elapsed time, and the reference observation is simply the newest one at least 30 minutes old. In a pool where nobody else trades for 30 minutes, the time-weighted average collapses to the attacker's own post-swap tick, so the floor follows a single pump exactly and the vault then buys PRAY at the pumped price with buyback output far below what the pre-pump price would have returned. Because every unblessed sell costs 50%, nobody arbitrages a pumped price back down unless the deviation exceeds about 52%, so the pumped tick is self-sustaining without further attacker trades; this is the state the README calls 'sustained manipulation of a thin pool', but it needs only one swap and silence. While the swarm sleeps (1% sells) the pumper can also exit into the buyback's own impact. Minimal fix: use a longer or multi-observation window (for example the minimum of several 30-minute averages, or the older of two observations), or bound buyback size relative to pool liquidity.","line":262,"path":"src/Hook.sol","reproduction":"State: pool 50,000,000 full-range liquidity at 1:1, 30 minutes after launch, vault holds fees. (1) buybackFloor(10_000 ether) = 9,285.58 PRAY. (2) bob buys with 20,000,000 FWA exact-input (PRAY price up ~1.96x); in the same block buybackFloor(10_000 ether) is unchanged at 9,285.58 PRAY. (3) Nobody trades for 30 minutes + 1 s. buybackFloor(10_000 ether) is now 4,798.94 PRAY. (4) alice calls vault.buyback(10_000 ether): passes the floor and burns 5,025.72 PRAY, versus at least 9,285.58 PRAY the fair floor would have required. Scratch test testFloorFollowsSingleSwapAfterQuietHalfHour reproduced these numbers against the delivered code.","severity":"low","snippet":"        int256 current = int256(cumulative) + int256(lastTick) * int256(block.timestamp - observedAt);","title":"buybackFloor TWAP is the last post-swap tick extended over any quiet period: one pump plus 30 silent minutes sets the floor at the pumped price"}],"hash":"35fd767b2491636250cb259532b3d742e582cba287018022bfa8aa9e02d07cd3","nodeId":"430aa474-97f5-458b-9c0f-11439109f451","outcome":"completed","summary":"The review is complete. The findings file is written and validated, the scratch directory is removed, and the working tree contains no new files outside the findings file.\n\n**Outcome.** Three substantiated findings in the Economic Security, Invariant and Flow Gap area, written to `.imd-findings.json` with a coverage row for all 18 entry points plus four invariants. The existing 71-test suite passes on the delivered code. The live oracle question-hash canonicalization reproduces with the captured fixture.\n\n**Findings**\n\n- **Medium, PleaRegistry.filePlea.** The 10% plea cap reads `balanceOf` only at filing time. The PoolManager lends the pool's PRAY reserve for free inside an unlock, so a contract can flash-take 9M PRAY, file for 1M against a real 1M holding, return the PRAY, and after approval sell 100% of its holding at 1%. Self-contained proof included; it fails on the current code with the whole bag taxed at 1% instead of a blended rate near 50%. The same snapshot lets one bag back several wallets' pleas by shuffling it between filings.\n- **Low, BurnVault.buyback.** No minimum budget. A 2010-wei call every 60 seconds succeeds and resets the global cooldown, so every real buyback that minute reverts. Reproduced for four consecutive minutes with the vault's 90,000 FWA left unspent.\n- **Low, Hook.buybackFloor.** The TWAP extends the last post-swap tick across any quiet period and references the newest observation at least 30 minutes old. One 20M FWA pump plus 30 silent minutes moved the floor from 9,286 to 4,799 PRAY per 10,000 FWA, and the buyback then burned 5,026 PRAY instead of at least 9,286. The 50% sell fee suppresses arbitrage, so the pumped price sustains itself.\n\n**What holds.** Fee math on all four swap paths, the exact 90/10 claim split, hook delta neutrality, partial-fill protection, router and vault settlement, FWA transfer-rule compliance, treasury redemption to the fixed recipient, oracle callback checks, blessing consumption and expiry, and the sleep/wake rule all traced as intended.\n\n**Not reached.** The mainnet fork rehearsal was not run (network), and the site was not reviewed beyond its economic claims.","treeHash":null,"usage":{"cachedInputTokens":4819798,"inputTokens":802,"model":"claude-fable-5-1","outputTokens":75318,"runtime":"claude","turns":54,"wallClockMs":1148277}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"f47f953f8e35921e","findings":[{"citation":"resolved","description":"filePlea bounds the pleaded amount to 10% of token.balanceOf(msg.sender) at the moment of filing and never again (not at the grant in _answer, not at consume). The balance is freely movable, so the invariant the brief states ('Max amount: 10% of the seller's PRAY balance', one blessing per wallet per 24 h) only holds per wallet-snapshot, not per holding. Boundary x invariant seam: the guard reads a value that the caller controls between the check and the use. A holder of B PRAY files for B/10 from wallet A, transfers B to wallet B, files for B/10 there (each wallet is a fresh 24 h window), and after both approvals redistributes the PRAY so both wallets sell B/10 at 1%: 20% of one holding sells cheaply inside one day, and with k wallets the cap becomes k x 10% for k pleas (0.5 IMD and one panel approval each). Nothing else in the registry or hook re-reads the seller's balance.","line":95,"path":"src/PleaRegistry.sol","reproduction":"Foundry, from test/TestBase.sol setUp (mature()): holding = token.balanceOf(alice) = 10,000,000e18, cap = holding/10. (1) file(alice, cap) -> ok. (2) alice transfers 10,000,000e18 PRAY to bob; bob burns/transfers away his own 10M so balanceOf(bob) == holding. (3) file(bob, cap) -> ok (cap check passes against the same tokens). (4) submit approving attestations for both pleas -> available(alice) == cap and available(bob) == cap. (5) bob transfers cap back to alice. (6) trade(alice,false,-cap) and trade(bob,false,-cap) both pay hook fee == received/99 (1%), i.e. 2,000,000e18 PRAY (20% of the original single holding) sold at 1% within one day. Expected under the brief: at most 10% of the holding blessed per 24 h. Scratch test test/scratch/MathProbe.t.sol::testCapSnapshotMovesWithBalance executes exactly this sequence and passes (demonstrating the bypass) on the current code. Suggested fix: re-check the seller's balance when the blessing is granted and/or when consumed (e.g. require token.balanceOf(seller) >= 10 * amount at grant), or compute the cap against a balance that cannot be moved during the plea (e.g. escrow the pleaded PRAY in the registry until answered).","severity":"low","snippet":"            amount == 0 || amount > token.balanceOf(msg.sender) / 10 || openPlea[msg.sender] != 0","title":"Blessing cap is checked against a transient PRAY balance snapshot, so one holding can be blessed several times over by moving it between wallets"}],"hash":"5e6977f2282623305514c42024afdd01d8d259d1f010b8fd0534c83ab3ded1aa","nodeId":"40831bc5-bd86-4e68-8308-b60892b66f4c","outcome":"completed","summary":"The review is complete. The findings file is written and the working tree shows no tracked changes (the findings file and scratch tests are gitignored or untracked, as intended).\n\n**Outcome.** The arithmetic in the pray contracts holds up. I found one low-severity defect in my area and nothing at medium or above.\n\n**What I checked, with results**\n\n- **Hook fee math on all four paths.** A fuzz over exact-input and exact-output buys, blessed sells and unblessed sells shows the hook fee equals the rate times the gross FWA side within 1 wei. Rounding goes against the user on gross-up paths and loses at most 1 wei on the sell-output path. The 90/10 split is exact, with remainder dust going to the vault.\n- **Launch decay.** The buy fee is 4998 bps one second after launch, 103 bps at 1799 seconds, and 100 bps from 1800 seconds. It rounds upward along the curve as the README states.\n- **Delta accounting.** I traced the specified-side and unspecified-side hook deltas through the vendored v4-core for both token orderings. The partial-fill equality check and the int128 guard behave as intended.\n- **Buyback floor.** The time-weighted tick average, the sign-aware floor rounding, the sqrt-price squaring branches and the price-direction formulas are correct in both orderings. Measured output divided by floor is 1.0526, which is exactly the 95% margin.\n- **Buyback budget split.** The spend and reward never exceed the budget, including at 201 wei and at the 50,000 FWA maximum. A budget below 201 wei reverts cleanly.\n- **Oracle question hash.** I fetched three live oracle requests containing emoji, guillemets, an inverted question mark and newlines, and reproduced their published hashes with the registry's canonicalization. Escaping non-ASCII does not match, so the registry's raw-UTF-8 approach is the right one.\n- **Static-analysis leads.** The flagged strict equality, unused `getSlot0` return and the tick cast are all bounded or intentional. None became a finding.\n\n**The one finding (low).** The 10% blessing cap in `filePlea` reads the seller's PRAY balance only at filing. Moving the same tokens to a second wallet lets that wallet file for another 10%, so one holding can be blessed k times with k pleas. The scratch test reproduces 20% of a single holding selling at 1% within one day. The suggested fix is to re-check the balance at grant or consume time, or escrow the pleaded PRAY.\n\n**Not reported, for lack of a reproduction.** A plea whose text trips the oracle's wording screen would be refused without a callback, costing 0.5 IMD and locking the wallet's plea slot for 24 hours. The README documents the MEV exposure of the buyback during a sleeping swarm. Both are outside the math area and I could not verify the first against the live service.","treeHash":null,"usage":{"cachedInputTokens":2753664,"inputTokens":514,"model":"claude-fable-5-1","outputTokens":58389,"runtime":"claude","turns":47,"wallClockMs":1014897}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"ddc5542bddcad225","findings":[{"citation":"resolved","description":"Trust gap (access x asymmetry). onOracleResult is bound to the registry's own Intake request: only the Intake calls it, and the Intake's writer pairs the answer with the request id. submit() is the mirror path for a missed callback, but it keeps none of that binding. _answer (src/PleaRegistry.sol:160-172) accepts any attestation that (a) is signed by the oracle signer in this registry's EIP-712 domain, (b) has a.questionHash == expectedQuestionHash(id, a.fromBlock, a.toBlock) for any block window, and (c) carries an oracle UUID (a.requestId) not yet consumed. None of those three is specific to the registry's own request. The canonical question document is public: PleaFiled carries the text, questionPrefix(id) returns the escaped prefix, and PleaText.body appends a fixed suffix naming the registry as `consumer` (src/PleaText.sol:116-123). The oracle signs attestations in whatever domain the body's `consumer` names (test/fixtures/live-request.json shows `consumer.verifyingContract` driving the signed domain), and every oracle request gets a fresh UUID. So anyone can re-ask the identical question through the Intake or the HTTP door, paying 0.5 IMD, and obtain a second signer-signed verdict with a new UUID and a new window that _answer accepts against the still-pending plea. Consequences: (1) the pleader re-rolls the panel for 0.5 IMD per attempt until one says true, defeating 'a panel judges it'; (2) a third party denies a competitor's plea (victim loses 0.5 IMD, plea settles Denied) or forces an approval at a moment the victim cannot sell, which starts the 15-minute window and sets lastGranted so the victim cannot re-file for 24 h; (3) the registry's own Intake callback then reverts with InvalidPlea (status != Pending) and is recorded as not delivered. Minimal fix that keeps the missed-callback feature: restrict submit() to msg.sender == pleas[id].seller (removes every third-party vector), and document that a seller can commission repeated panels for the same question; a fuller fix needs a per-request binding the question itself cannot carry (e.g. the writer-delivered Intake path only, with submit limited to a window after the callback demonstrably failed).","line":150,"path":"src/PleaRegistry.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\nimport {Test} from \"forge-std/Test.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {ECDSA} from \"@openzeppelin/contracts/utils/cryptography/ECDSA.sol\";\nimport {Token} from \"src/Token.sol\";\nimport {PleaRegistry, IIntake} from \"src/PleaRegistry.sol\";\nimport {OracleAttestation} from \"src/OracleAttestation.sol\";\n\n/// @dev ERC-1271 stand-in etched at the production signer address so a test key can sign for it.\ncontract ProofSigner {\n    address public immutable signer;\n\n    constructor(address s) {\n        signer = s;\n    }\n\n    function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4) {\n        (address recovered, ECDSA.RecoverError err,) = ECDSA.tryRecover(hash, signature);\n        return\n            err == ECDSA.RecoverError.NoError && recovered == signer ? bytes4(0x1626ba7e) : bytes4(0xffffffff);\n    }\n}\n\ncontract ProofIMD is ERC20 {\n    constructor() ERC20(\"IMD\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\ncontract ProofIntake is IIntake {\n    uint256 public nonce;\n\n    function priceOf(bytes32, address) external pure returns (uint256) {\n        return 0.5 ether;\n    }\n\n    function request(bytes32, bytes calldata, Callback calldata, address asset, uint256 amount)\n        external\n        payable\n        returns (bytes32 id)\n    {\n        require(IERC20(asset).transferFrom(msg.sender, address(this), amount), \"payment\");\n        id = keccak256(abi.encode(msg.sender, ++nonce));\n    }\n}\n\n/// @title A second oracle request for the same canonical question settles someone else's plea\n/// @notice PleaRegistry.submit() binds an attestation to a plea only by (signer, questionHash).\n/// The canonical question is public (PleaFiled + questionPrefix), the body's `consumer` field\n/// makes the oracle sign in this registry's domain, and the oracle UUID is fresh per request, so a\n/// third party's re-ask of the same question yields a signer-signed attestation the registry\n/// accepts for the victim's pending plea. Fails on the current code: bob settles alice's plea.\ncontract ReaskProofTest is Test {\n    address constant IMD = 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7;\n    address constant INTAKE = 0x1397434cd35e8a9C8aC312A61D3A285EB31dea56;\n    address constant SIGNER = 0x5598Aa9146215Bc13eb26f2c692Ad1461Fd32982;\n    uint256 constant SIGNER_KEY = 0x123456;\n    string constant PLEA =\n        \"I must sell a small part of my savings today to repair the leaking roof over my family and keep our home warm.\";\n\n    Token token;\n    PleaRegistry registry;\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n\n    function setUp() public {\n        vm.chainId(1);\n        vm.warp(1_800_000_000);\n        vm.roll(30_000_000);\n        token = new Token();\n        registry = new PleaRegistry(IERC20(address(token)), address(this));\n        vm.etch(IMD, address(new ProofIMD()).code);\n        vm.etch(INTAKE, address(new ProofIntake()).code);\n        vm.etch(SIGNER, address(new ProofSigner(vm.addr(SIGNER_KEY))).code);\n        ProofIMD(IMD).mint(alice, 10 ether);\n        token.transfer(alice, 1_000 ether);\n        vm.startPrank(alice);\n        IERC20(IMD).approve(address(registry), 10 ether);\n        vm.stopPrank();\n    }\n\n    function attest(uint256 id, bytes32 uuid, uint64 fromBlock, uint64 toBlock, bool approved)\n        internal\n        view\n        returns (OracleAttestation.Attestation memory a)\n    {\n        a = OracleAttestation.Attestation(\n            uuid,\n            1,\n            registry.expectedQuestionHash(id, fromBlock, toBlock),\n            0,\n            abi.encode(approved),\n            0,\n            fromBlock,\n            toBlock,\n            bytes32(uint256(7)),\n            bytes32(uint256(8)),\n            7,\n            5,\n            5,\n            uint64(block.timestamp),\n            uint64(block.timestamp + 900)\n        );\n    }\n\n    function sign(OracleAttestation.Attestation memory a) internal view returns (bytes memory) {\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(SIGNER_KEY, registry.attestationDigest(a));\n        return abi.encodePacked(r, s, v);\n    }\n\n    /// @dev Alice files; her own request is the Intake id `req` with oracle UUID U1 (not yet\n    /// delivered). Bob re-asks the identical canonical question (he can build it from public data),\n    /// receiving a signer-signed attestation with a fresh UUID U2 and a later block window, and\n    /// submits a *denial* against Alice's pending plea. Expected: the registry refuses an answer\n    /// that is not the one bound to its own request (or at least refuses it from a third party).\n    /// Actual: the plea is settled as Denied by Bob and Alice's own answer can never land.\n    function testThirdPartyReaskSettlesVictimPlea() public {\n        vm.prank(alice);\n        uint256 id = registry.filePlea(100 ether, PLEA);\n        bytes32 req = keccak256(abi.encode(address(registry), uint256(1)));\n        assertEq(registry.requestPlea(req), id);\n\n        // Bob's re-ask: different oracle UUID, different resolved window, same question document.\n        vm.roll(block.number + 50);\n        OracleAttestation.Attestation memory theirs =\n            attest(id, keccak256(\"bob's re-ask uuid\"), uint64(block.number - 300), uint64(block.number), false);\n        bytes memory sig = sign(theirs);\n\n        vm.prank(bob);\n        (bool ok,) = address(registry).call(abi.encodeCall(PleaRegistry.submit, (req, theirs, sig)));\n        assertFalse(ok, \"a third party's re-asked verdict settled the victim's pending plea\");\n\n        // The plea must still be pending so that Alice's own oracle answer (UUID U1) can land.\n        (,, PleaRegistry.Status status,,,) = registry.pleas(id);\n        assertEq(uint8(status), uint8(PleaRegistry.Status.Pending));\n        OracleAttestation.Attestation memory own =\n            attest(id, keccak256(\"alice's own uuid\"), uint64(block.number - 300), uint64(block.number), true);\n        vm.prank(INTAKE);\n        registry.onOracleResult(req, own, sign(own));\n        assertEq(registry.available(alice), 100 ether);\n    }\n}","reproduction":"State: alice holds 1,000 PRAY, has 10 IMD approved to the registry. 1) alice: registry.filePlea(100e18, <20-word plea>) -> plea id 1, Intake request id R. 2) bob reads PleaFiled / registry.questionPrefix(1), builds the identical body (PleaText.body(prefix, registry)) and asks the oracle for it; the oracle returns an attestation A2 signed by 0x5598...2982 in the registry's domain with requestId = fresh UUID U2, fromBlock/toBlock = its own resolved window, questionHash = registry.expectedQuestionHash(1, fromBlock, toBlock), answer = abi.encode(false), panelSize 7, quorum 5, agreed >= 5, issuedAt >= filedAt, expiresAt = issuedAt + 900. 3) bob: registry.submit(R, A2, sig2). Expected: revert (answer is not the one bound to the registry's request / caller is not the Intake or the seller). Actual: pleas[1].status = Denied, openPlea[alice] = 0, lastAnswer updated, PleaAnswered emitted; the Intake's later onOracleResult(R, A1, sig1) with the real verdict reverts InvalidPlea. The same call with answer = true blesses alice at bob's chosen time. The attached test reproduces step 3 with the test signer key standing in for the oracle signer (same ERC-1271 stand-in the repository's own tests use).","severity":"medium","snippet":"    function submit(bytes32 requestId, OracleAttestation.Attestation calldata a, bytes calldata signature)","title":"Public submit() lets anyone settle a pending plea with a re-asked panel verdict (approval re-roll, third-party denial or forced early blessing)"},{"citation":"resolved","description":"Asymmetry between request and fulfilment (economics x asymmetry). filePlea checks amount <= balanceOf(msg.sender)/10 once, at filing. _answer grants blessings[seller].remaining = p.amount regardless of the seller's balance at approval, and Hook.afterSwap -> consume() only checks the blessing, never the balance. Tokens are freely transferable, wallets are free, and the one-plea-per-wallet and 24-hour rules are per wallet, so one holder can move the same tokens through N wallets, file a 10% plea from each in the same hour, and after N approvals sell N x 10% of the holding at 1% inside overlapping 15-minute windows. The 'max 10% of the seller's PRAY balance' rule therefore bounds nothing beyond the per-plea cost of 0.5 IMD and a distinct plea text per wallet. Mitigation options: enforce the cap at consumption as well (consume only while balanceOf(seller) >= 10 x consumed, or cap the blessing to balanceOf(seller)/10 at approval) and state in the README that the limit is per wallet; neither fully closes a sybil path but both remove the trivial move-and-refile route.","line":95,"path":"src/PleaRegistry.sol","reproduction":"State: alice holds 10,000,000 PRAY (10% = 1,000,000), swarm awake, 30 minutes after launch. 1) alice: filePlea(1_000_000e18, textA); oracle approves -> available(alice) = 1,000,000. 2) alice: token.transfer(carol, 10_000_000e18). 3) carol: filePlea(1_000_000e18, textB) (passes: balance/10 = 1,000,000); oracle approves -> available(carol) = 1,000,000. 4) carol: token.transfer(alice, 1_000_000e18). 5) alice and carol each sell 1,000,000 PRAY exact-input through PrayRouter within 15 minutes. Expected: at most 1,000,000 PRAY (10% of the holding) sells at the 1% rate. Actual: 2,000,000 PRAY (20%) sells at 1%; hook fees are 1% of both sells (scratch test testSameTokensBackMultipleTenPercentPleas asserts fees == (gotA+gotC)/99 within 0.1%). With N wallets the blessed share is N x 10%.","severity":"low","snippet":"            amount == 0 || amount > token.balanceOf(msg.sender) / 10 || openPlea[msg.sender] != 0","title":"10%-of-balance plea cap is enforced per wallet at filing only; the same PRAY can back pleas from several wallets and the blessing is never re-checked against the seller's balance"},{"citation":"resolved","description":"Access x asymmetry seam. The router path is authenticated (sender == router, hookData written by the router from its msg.sender, whose PRAY is what is sold). The fallback path trusts tx.origin, which says nothing about whose PRAY is being sold: any contract that a blessed wallet merely transacts with (an airdrop claim, an NFT mint, a 'verify wallet' button) can run a PoolManager unlock, sell its own PRAY, and the hook attributes the sell to tx.origin, prices it at 1% instead of 50%, and consumes the wallet's blessing in afterSwap (src/Hook.sol:193). The wallet signs nothing about PRAY and grants no approval. This is the brief's stated fallback ('else tx.origin') and the README documents that other routers can consume the originating wallet's blessing, so it is reported as a trust assumption with a concrete abuse path rather than a bypass. If the requester wants the blessing to be usable only for the blessed wallet's own tokens, the hook should honour blessings only on the router path (or only when hookData is empty and sender == tx.origin, i.e. an EOA-driven foreign router with no intermediary contract), leaving foreign-router sells at 50%.","line":149,"path":"src/Hook.sol","reproduction":"State: alice is blessed for 100 PRAY (available(alice) = 100e18); contract P holds 100 PRAY of its own and exposes claimAirdrop() that unlocks the PoolManager, swaps 100 PRAY exact-input for FWA in the PRAY pool with empty hookData, settles and takes the FWA for itself. 1) alice (EOA, tx.origin = alice) calls P.claimAirdrop(). Expected: P's sell is unblessed (50% hook fee) and alice's blessing is untouched. Actual: Hook.beforeSwap sees sender = P != router, seller = tx.origin = alice, available = 100e18 >= 100e18 -> rate 100 bps; afterSwap calls registry.consume(alice, 100e18). Scratch run: P received 97.76 FWA, hook fee 0.9875 FWA (= received/99), registry.available(alice) == 0 afterwards.","severity":"low","snippet":"        address seller = sender == address(router) ? abi.decode(data, (address)) : tx.origin;","title":"tx.origin attribution lets any contract a blessed wallet calls sell the contract's own PRAY at 1% and spend the wallet's blessing"}],"hash":"8609882aa695710ae78859e1093672abf99ec38079d769e0abf220c6eda0203c","nodeId":"7b64310b-7f86-4818-8b09-937ecc346638","outcome":"completed","summary":"The review is complete. The findings file is at `.imd-findings.json`, the only new file in the tree, and the scratch directory is removed.\n\n**Outcome.** The permission model itself holds: every hook callback is PoolManager-only, initialization is factory-bound and single-shot, the two unlock callbacks verify a commitment hash, the router pulls only from its caller, every token take goes to a fixed or committed recipient, and there is no owner, setter, upgrade or pause anywhere. The defects I could substantiate all live at the trust-gap seams the assignment named, not in a missing modifier.\n\n**Findings (3), each reproduced in Foundry against the current tree:**\n\n- **Medium. Public `submit()` settles a pending plea with a re-asked verdict.** The callback is bound to the registry's Intake request, but `submit` binds only by signer and question hash. The question document is public, the body's `consumer` field makes the oracle sign in the registry's domain, and each oracle request has a fresh UUID. So a pleader can re-roll the panel for 0.5 IMD per try, and a third party can deny a competitor's plea or force an approval at a bad moment, which also locks the victim out of re-filing for 24 hours. A proof test is attached that fails now and passes once `submit` is bound to the seller or the Intake path.\n- **Low. The 10% cap is checked only at filing, per wallet.** The same PRAY moved through N wallets backs N pleas of 10% each, and consumption never re-checks balance. Reproduced with two wallets selling 20% of one holding at 1%.\n- **Low. The `tx.origin` fallback lets any contract a blessed wallet calls sell its own PRAY at 1% and spend the wallet's blessing.** This is the brief's stated fallback, so it is reported as a trust assumption with a concrete abuse path rather than a bypass.\n\n**Coverage.** All 18 listed entry points have a row plus two invariant rows. Fifteen hold, three carry findings, none unreached. Things I checked and did not report: buyback sandwiching is bounded by the 5% floor minus the buyback's own impact against a 4.5% round-trip fee even while the swarm sleeps, the `BlessingUnavailable` revert in `_answer` is unreachable, and the fee delta accounting nets to zero on all four swap paths.\n\n**Not covered in depth.** Site code, the exact canonical-JSON hash reproduction against the live oracle (relied on the repository's check script and fixture), and the mainnet fork rehearsal, which I did not re-run.","treeHash":null,"usage":{"cachedInputTokens":1980783,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":61038,"runtime":"claude","turns":44,"wallClockMs":949665}},{"artifacts":[],"attempt":2,"bundleHash":"70515e16b0da47ef709886f1414bb2e96c25e94eac7940d9f4771b7fb7e37be8","device":"61794f1d37e04d2f","findings":[],"hash":"88eb13467209315434218c2a8999c40107c0aecfc8c5031752f54a61631e9dba","nodeId":"796a64a2-be19-4d86-a1f1-e1b9c778288a","outcome":"completed","summary":"Implemented the contracts, site, tests, vendored dependencies, and launch handoff. Fixed the arbitrary-payer router flaw.\n\nPassed: `forge build`, `forge fmt --check`, 71 local tests, and 3 mainnet fork tests. Real oracle-signature verification and independent review are documented.\n\nDeployment and hosting remain with the launch workflow; no live addresses were fabricated.\n\nDetails: [README.md](/home/imd-worker/.identitymd/work/46c695fa-7061-4e9f-a06a-659e1f72ee3b/796a64a2-be19-4d86-a1f1-e1b9c778288a/README.md).","treeHash":"3f6ffaa2026687e23bfd98a156f6ae13f603412d","usage":{"cachedInputTokens":8841856,"inputTokens":179393,"model":"gpt-6-astra","outputTokens":61102,"runtime":"codex","turns":16,"wallClockMs":2476196}}],"verification":[{"checks":[{"durationMs":70975,"exitCode":0,"name":"build","output":"Compiling 111 files with Solc 0.8.26\nSolc 0.8.26 finished in 70.68s\nCompiler run successful with warnings:\nWarning (2018): Function state mutability can be restricted to pure\n   --> test/Registry.t.sol:224:5:\n    |\n224 |     function testFuzzJsonEscapeRoundTrip(bytes calldata raw) public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nwarning[missing-events-access-control]: `active` is changed without an event but is used for access control\n   ╭▸ src/PrayRouter.sol:64:16\n   │\n64 │         delete active;\n   │                ━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `active` is changed without an event but is used for access control\n   ╭▸ src/BurnVault.sol:58:16\n   │\n58 │         delete active;\n   │                ━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaRegistry.sol:29:38\n   │\n29 │     bytes32 public constant ACTION = bytes32(\"oracle.request@oracle-1\");\n   │                                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes32' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/PrayRouter.sol:42:13\n   │\n42 │             block.timestamp > trade.deadline || trade.maxInput == 0\n   │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/PleaText.sol:31:17\n   │\n31 │                 revert InvalidText();\n   │                 ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/PleaText.sol:33:36\n   │\n33 │             if (i + n >= s.length) revert InvalidText();\n   │                                    ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/PleaText.sol:36:43\n   │\n36 │                 if (d < 0x80 || d > 0xbf) revert InvalidText();\n   │                                           ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/BurnVault.sol:39:50\n   │\n39 │         if (budget == 0 || budget > MAX_SPEND || block.timestamp < lastBuyback + 60) revert InvalidBuyback();\n   │                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/PleaRegistry.sol:83:16\n   │\n83 │         return block.timestamp >= lastAnswer + 24 hours;\n   │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/PrayRouter.sol:56:9\n   │\n56 │         emit Traded(msg.sender, trade.buy, paid, received);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/PleaRegistry.sol:88:16\n   │\n88 │         return block.timestamp < b.until ? b.remaining : 0;\n   │                ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/PleaText.sol:42:15\n   │\n42 │             ) revert InvalidText();\n   │               ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/BurnVault.sol:51:9\n   │\n51 │         emit BoughtAndBurned(msg.sender, spend, reward, burned);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PrayRouter.sol:43:37\n   │\n43 │                 || trade.maxInput > uint256(uint128(type(int128).max)) || trade.minOutput == 0\n   │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PrayRouter.sol:43:45\n   │\n43 │                 || trade.maxInput > uint256(uint128(type(int128).max)) || trade.minOutput == 0\n   │                                             ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint128' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/PleaRegistry.sol:96:43\n   │\n96 │                 || (b.lastGranted != 0 && block.timestamp < uint256(b.lastGranted) + 24 hours)\n   │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaText.sol:62:27\n   │\n62 │                         ? bytes1(\"b\")\n   │                           ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaText.sol:63:36\n   │\n63 │                         : c == 9 ? bytes1(\"t\") : c == 10 ? bytes1(\"n\") : c == 12 ? bytes1(\"f\") : bytes1(\"r\");\n   │                                    ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaText.sol:63:60\n   │\n63 │                         : c == 9 ? bytes1(\"t\") : c == 10 ? bytes1(\"n\") : c == 12 ? bytes1(\"f\") : bytes1(\"r\");\n   │                                                            ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/BurnVault.sol:67:18\n   │\n67 │                 -int256(spend),\n   │                  ━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaText.sol:63:84\n   │\n63 │                         : c == 9 ? bytes1(\"t\") : c == 10 ? bytes1(\"n\") : c == 12 ? bytes1(\"f\") : bytes1(\"r\");\n   │                                                                                    ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaText.sol:63:98\n   │\n63 │                         : c == 9 ? bytes1(\"t\") : c == 10 ? bytes1(\"n\") : c == 12 ? bytes1(\"f\") : bytes1(\"r\");\n   │                                                                                                  ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/BurnVault.sol:74:23\n   │\n74 │         if (input != -int256(spend) || output <= 0 || uint256(int256(output)) < floor) {\n   │                       ━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/BurnVault.sol:74:55\n   │\n74 │         if (input != -int256(spend) || output <= 0 || uint256(int256(output)) < floor) {\n   │                                                       ━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/BurnVault.sol:79:26\n   │\n79 │         uint256 burned = uint256(int256(output));\n   │                          ━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PrayRouter.sol:74:24\n   │\n74 │         uint256 paid = uint256(-int256(inputDelta));\n   │                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PleaRegistry.sol:122:9\n    │\n122 │         emit PleaFiled(id, msg.sender, requestId, amount, text);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PrayRouter.sol:75:28\n   │\n75 │         uint256 received = uint256(int256(outputDelta));\n   │                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PrayRouter.sol:78:58\n   │\n78 │                 || (t.amountSpecified > 0 && received != uint256(t.amountSpecified))\n   │                                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/PleaText.sol:107:16\n    │\n107 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n108 │ ┃             '{\"answerType\":\"bool\",\"chainId\":1,\"definitions\":{\"rubric\":\"',\n109 │ ┃             RUBRIC,\n110 │ ┃             '\"},\"evidence\":\"panel\",\"question\":\"',\n111 │ ┃             escape(q),\n112 │ ┃             '\",\"v\":1,\"window\":'\n113 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/PleaText.sol:117:16\n    │\n117 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n118 │ ┃             p,\n119 │ ┃             '{\"hours\":1},\"panelSize\":7,\"quorum\":5,\"validForSeconds\":900,\"consumer\":{\"chainId\":1,\"verifyingContract\":\"',\n120 │ ┃             Strings.toHexString(registry),\n121 │ ┃             '\"}}'\n122 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PleaRegistry.sol:103:13\n    │\n103 │             uint64(block.timestamp),\n    │             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/PleaText.sol:127:13\n    │\n127 │ ┏             abi.encodePacked(\n128 │ ┃                 p,\n129 │ ┃                 '{\"fromBlock\":',\n130 │ ┃                 Strings.toString(fromBlock),\n    ‡ ┃\n133 │ ┃                 \"}}\"\n134 │ ┃             )\n    │ ┗━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PleaRegistry.sol:162:43\n    │\n162 │         if (p.status != Status.Pending || block.timestamp >= uint256(p.filedAt) + PENDING_LIFETIME) {\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PleaRegistry.sol:180:39\n    │\n180 │             if (b.lastGranted != 0 && block.timestamp < uint256(b.lastGranted) + 24 hours) {\n    │                                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PleaRegistry.sol:187:9\n    │\n187 │ ┏         emit PleaAnswered(\n188 │ ┃             id, a.requestId, approved, a.agreed, uint64(block.timestamp + (approved ? 15 minutes : 0))\n189 │ ┃         );\n    │ ┗━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PleaRegistry.sol:184:23\n    │\n184 │             b.until = uint64(block.timestamp + 15 minutes);\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PleaRegistry.sol:185:29\n    │\n185 │             b.lastGranted = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PleaRegistry.sol:188:50\n    │\n188 │             id, a.requestId, approved, a.agreed, uint64(block.timestamp + (approved ? 15 minutes : 0))\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PleaRegistry.sol:194:43\n    │\n194 │         if (p.status != Status.Pending || block.timestamp < uint256(p.filedAt) + PENDING_LIFETIME) {\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[missing-events-arithmetic]: `cumulative` is changed without an event but is used in arithmetic\n    ╭▸ src/Hook.sol:246:9\n    │\n246 │         cumulative += int128(int256(lastTick) * int256(elapsed));\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/Hook.sol:247:9\n    │\n247 │         observedAt = uint64(block.timestamp);\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `lastTick` is changed without an event but is used in arithmetic\n    ╭▸ src/Hook.sol:210:9\n    │\n210 │         lastTick = tick;\n    │         ━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/Hook.sol:69:40\n   │\n69 │     constructor(IPoolManager manager_, address token_, address factory_) {\n   │                                        ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Hook.sol:118:9\n    │\n118 │         emit PoolOpened(PoolId.unwrap(poolId), address(registry), address(router), address(vault));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:115:22\n    │\n115 │         observedAt = uint64(block.timestamp);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:117:39\n    │\n117 │         observations[0] = Observation(uint64(block.timestamp), 0);\n    │                                       ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Hook.sol:124:16\n    │\n124 │         return elapsed >= 30 minutes ? 100 : 5000 - 4900 * elapsed / 30 minutes;\n    │                ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Hook.sol:222:9\n    │\n222 │         emit FeeCollected(seller, buy, fee, vaultPart, treasuryPart, rate);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:154:66\n    │\n154 │             blessed = available > 0 && (p.amountSpecified > 0 || uint256(-p.amountSpecified) <= available);\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:166:54\n    │\n166 │             uint256 amount = p.amountSpecified < 0 ? uint256(-p.amountSpecified) : uint256(p.amountSpecified);\n    │                                                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:166:84\n    │\n166 │             uint256 amount = p.amountSpecified < 0 ? uint256(-p.amountSpecified) : uint256(p.amountSpecified);\n    │                                                                                    ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:170:23\n    │\n170 │             if (fee > uint256(uint128(type(int128).max))) revert InvalidSwap();\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:170:31\n    │\n170 │             if (fee > uint256(uint128(type(int128).max))) revert InvalidSwap();\n    │                               ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:176:63\n    │\n176 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);\n    │                                                               ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:176:70\n    │\n176 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);\n    │                                                                      ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:191:34\n    │\n191 │         address seller = address(uint160(_load(SELLER)));\n    │                                  ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:193:67\n    │\n193 │         if (!buy && _load(BLESSED) != 0) registry.consume(seller, uint256(-int256(prayDelta)));\n    │                                                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:198:51\n    │\n198 │             int256 expected = p.amountSpecified + int256(_load(UPFRONT));\n    │                                                   ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:201:36\n    │\n201 │             uint256 amount = buy ? uint256(-int256(fwaDelta)) : uint256(int256(fwaDelta));\n    │                                    ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:201:65\n    │\n201 │             uint256 amount = buy ? uint256(-int256(fwaDelta)) : uint256(int256(fwaDelta));\n    │                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:205:23\n    │\n205 │             if (fee > uint256(uint128(type(int128).max))) revert InvalidSwap();\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:205:31\n    │\n205 │             if (fee > uint256(uint128(type(int128).max))) revert InvalidSwap();\n    │                               ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:212:44\n    │\n212 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\n    │                                            ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:212:51\n    │\n212 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\n    │                                                   ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Hook.sol:240:9\n    │\n240 │         emit TreasuryPaid(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Hook.sol:248:13\n    │\n248 │         if (block.timestamp >= uint256(observations[observationIndex].time) + 60) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:246:23\n    │\n246 │         cumulative += int128(int256(lastTick) * int256(elapsed));\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:246:49\n    │\n246 │         cumulative += int128(int256(lastTick) * int256(elapsed));\n    │                                                 ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:247:22\n    │\n247 │         observedAt = uint64(block.timestamp);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:250:58\n    │\n250 │             observations[observationIndex] = Observation(uint64(block.timestamp), cumulative);\n    │                                                          ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Hook.sol:255:29\n    │\n255 │         if (!initialized || block.timestamp < launchedAt + 30 minutes) revert HistoryTooShort();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Hook.sol:259:38\n    │\n259 │             if (o.time > old.time && uint256(o.time) + 30 minutes <= block.timestamp) old = o;\n    │                                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:262:66\n    │\n262 │         int256 current = int256(cumulative) + int256(lastTick) * int256(block.timestamp - observedAt);\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:265:37\n    │\n265 │         int256 average = sum < 0 ? -int256((uint256(-sum) + elapsed - 1) / elapsed) : sum / int256(elapsed);\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:265:45\n    │\n265 │         int256 average = sum < 0 ? -int256((uint256(-sum) + elapsed - 1) / elapsed) : sum / int256(elapsed);\n    │                                             ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:265:93\n    │\n265 │         int256 average = sum < 0 ? -int256((uint256(-sum) + elapsed - 1) / elapsed) : sum / int256(elapsed);\n    │                                                                                             ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:266:52\n    │\n266 │         uint160 sqrt = TickMath.getSqrtPriceAtTick(int24(average));\n    │                                                    ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:205:78\n    │\n205 │         return router.swap(PrayRouter.Trade(buy, amount, max, 1, limit(buy), block.timestamp));\n    │                                                                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Hook.t.sol:86:9\n    │\n 86 │         vm.warp(hook.launchedAt() + 900);\n    │         ──────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:205:78\n    │\n205 │         return router.swap(PrayRouter.Trade(buy, amount, max, 1, limit(buy), block.timestamp));\n    │                                                                              ━━━━━━━━━━━━━━━\n    ‡\n250 │         vm.warp(hook.launchedAt() + 1800);\n    │         ───────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:233:20\n    │\n233 │             uint64(block.timestamp),\n    │                    ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Hook.t.sol:174:9\n    │\n174 │         vm.warp(block.timestamp + 900);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:234:20\n    │\n234 │             uint64(block.timestamp + 900)\n    │                    ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Hook.t.sol:174:9\n    │\n174 │         vm.warp(block.timestamp + 900);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Hook.t.sol:174:17\n    │\n174 │         vm.warp(block.timestamp + 900);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:205:78\n    │\n205 │         return router.swap(PrayRouter.Trade(buy, amount, max, 1, limit(buy), block.timestamp));\n    │                                                                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Hook.t.sol:178:9\n    │\n178 │         vm.warp(registry.lastAnswer() + 86400);\n    │         ────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:233:20\n    │\n233 │             uint64(block.timestamp),\n    │                    ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Registry.t.sol:94:9\n    │\n 94 │         vm.warp(block.timestamp + 901);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:234:20\n    │\n234 │             uint64(block.timestamp + 900)\n    │                    ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Registry.t.sol:94:9\n    │\n 94 │         vm.warp(block.timestamp + 901);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/Registry.t.sol:94:17\n   │\n94 │         vm.warp(block.timestamp + 901);\n   │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:233:20\n    │\n233 │             uint64(block.timestamp),\n    │                    ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Registry.t.sol:178:9\n    │\n178 │         vm.warp(block.timestamp + 86399);\n    │         ──────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:234:20\n    │\n234 │             uint64(block.timestamp + 900)\n    │                    ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Registry.t.sol:178:9\n    │\n178 │         vm.warp(block.timestamp + 86399);\n    │         ──────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Registry.t.sol:178:17\n    │\n178 │         vm.warp(block.timestamp + 86399);\n    │         ────────━━━━━━━━━━━━━━━───────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Registry.t.sol:238:17\n    │\n238 │         vm.warp(block.timestamp + 86400);\n    │         ────────━━━━━━━━━━━━━━━───────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":9489,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetRehearsal.t.sol:MainnetRehearsalTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 482.02µs (0.00ns CPU time)\n\nRan 2 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] testProtocolDigestSignatureAndSelector() (gas: 715841)\n[PASS] testRealMainnetSignerAttestation() (gas: 3355590)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.44ms (1.55ms CPU time)\n\nRan 5 tests for test/InitializationAdversarial.t.sol:InitializationAdversarialTest\n[PASS] testAllProductionRuntimeCodeHasNoUpgradeOrDestructionOpcode() (gas: 10573011)\n[PASS] testEveryLaunchParameterCheckedBeforeFirstInitialization() (gas: 643621)\n[PASS] testOnlyFactoryCanInitializeThroughRealManager() (gas: 341101)\n[PASS] testPermissionsExactlyMatchRequiredCallbacks() (gas: 8953)\n[PASS] testUninitializedPoolCannotBeRead() (gas: 11461)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 15.62ms (15.52ms CPU time)\n\nRan 15 tests for test/Registry.t.sol:RegistryTest\n[PASS] testCallbackUnknownRequestAndUnauthorized() (gas: 1498545)\n[PASS] testCallbackWithinStipendAndPublicFallback() (gas: 2551650)\n[PASS] testEscapedTextIsValidJson() (gas: 1397628)\n[PASS] testExpirePendingNoRefundAndWakeOnNextAnswer() (gas: 2813848)\n[PASS] testExpiredAndFutureAttestations() (gas: 1516777)\n[PASS] testFalseAnswerStoresVerdictAndDoesNotBless() (gas: 1540623)\n[PASS] testFilePleaPaysAndPinsTemplate() (gas: 1506099)\n[PASS] testFuzzJsonEscapeRoundTrip(bytes) (runs: 128, μ: 47405, ~: 53873)\n[PASS] testMaxEscapedTextCallbackUnder200k() (gas: 5787991)\n[PASS] testOneOpenAndOneBlessingPer24Hours() (gas: 2535656)\n[PASS] testOnlyHookCanConsume() (gas: 1681728)\n[PASS] testPleaSizeLimitBalanceAndUtf8() (gas: 1567901)\n[PASS] testReplayAndWrongPleaCannotBorrowApproval() (gas: 2534474)\n[PASS] testWrongSignerAndDomain() (gas: 1603694)\n[PASS] testWrongTypeChainPanelAndWindow() (gas: 1808081)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 74.84ms (29.41ms CPU time)\n\nRan 8 tests for test/SwapAdversarial.t.sol:SwapAdversarialTest\n[PASS] testBuybackCooldownAt59And60Seconds() (gas: 1236467)\n[PASS] testDustFeeRoundingAndSplitRemainder() (gas: 1588864)\n[PASS] testFuzzBuySellRoundTripNeverCreatesFwa(uint96,bool) (runs: 1000, μ: 1363865, ~: 614938)\nLogs:\n  Bound result 10482891843881\n\n[PASS] testFuzzPoolDeltasFeeModesAndExactSplit(uint96,uint16,uint8) (runs: 1000, μ: 840897, ~: 481997)\nLogs:\n  Bound result 126985762618918822\n  Bound result 62\n\n[PASS] testManagerCannotInvokeIdleUnlockCallbacks() (gas: 144668)\n[PASS] testNoPublicAdminOrArbitraryAssetExit() (gas: 1537403)\n[PASS] testOutputFailureRestoresBlessingPoolClaimsAndBalances() (gas: 2343143)\n[PASS] testRouterInvalidInputsNeverMoveTokens() (gas: 680108)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 260.90ms (423.24ms CPU time)\n\nRan 10 tests for test/RegistryAdversarial.t.sol:RegistryAdversarialTest\n[PASS] testBlessingExpiresAt900AndZeroConsumeDoesNotSpend() (gas: 1658744)\n[PASS] testEveryEip712DomainFieldIsBound() (gas: 1833299)\n[PASS] testFuzzBodyCannotInjectTemplate(bytes32,uint16) (runs: 1000, μ: 2192753, ~: 1821191)\nLogs:\n  Bound result 1\n\n[PASS] testFuzzEverySignedFieldIsBound(uint8) (runs: 1000, μ: 1523438, ~: 1523454)\n[PASS] testIntakeFailureAndDuplicateRequestRollBackPayment() (gas: 3516900)\n[PASS] testInvalidUtf8ContinuationAndTruncationMatrix() (gas: 255866)\n[PASS] testPaymentFailureRollsBackPleaAndCanBeRetried() (gas: 2088396)\n[PASS] testSignedInvalidPanelAndTimeBoundariesAreAtomic() (gas: 2501728)\n[PASS] testSignedMalformedBooleansCannotSettleOrWake() (gas: 2049659)\n[PASS] testSleepAndPendingExpiryExactBoundaries() (gas: 2510608)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 260.93ms (415.18ms CPU time)\n\nRan 27 tests for test/Hook.t.sol:HookTest\n[PASS] testBlessedExactOutput() (gas: 1931570)\n[PASS] testBlessedSellConsumesOnlyActualAmount() (gas: 2364979)\n[PASS] testBlessingWrongSellerDoesNotDiscount() (gas: 1923871)\n[PASS] testBuyDecayAndFreshManagerHasNoETH() (gas: 817391)\n[PASS] testBuyExactOutputAndRefund() (gas: 374951)\n[PASS] testBuybackBurnsAndPaysFromManager() (gas: 1240533)\n[PASS] testBuybackCapsHistoryAndBalance() (gas: 461553)\n[PASS] testBuybackMaximumBudgetIncludesReward() (gas: 817220)\n[PASS] testBuybackResistsSameBlockSpotManipulation() (gas: 1263573)\n[PASS] testExpiredBlessingAndSleeping() (gas: 2190636)\n[PASS] testForeignRouterCannotSpoofHookData() (gas: 1918036)\n[PASS] testFuzzSequenceConservesClaimShares(uint256) (runs: 128, μ: 3351262, ~: 3353486)\n[PASS] testFuzzSplitConservation(uint96) (runs: 128, μ: 377663, ~: 377770)\nLogs:\n  Bound result 1519162949498447115785\n\n[PASS] testFwaWalletToRouterReallyReverts() (gas: 33214)\n[PASS] testInitializationAndFlags() (gas: 16800)\n[PASS] testOneSidedPoolNoFwaCashBeforeSwap() (gas: 705179)\n[PASS] testOverAmountExactInputPaysHighAndKeepsBlessing() (gas: 1922925)\n[PASS] testOverAmountExactOutputRevertsAtomically() (gas: 1906032)\n[PASS] testPartialBuyRevertsNoUpfrontTaxLost() (gas: 326836)\n[PASS] testRejectOtherPoolAndReinitialize() (gas: 78493)\n[PASS] testRouterCannotPullVictimAllowance() (gas: 396870)\n[PASS] testRouterSlippageDeadlineAndFailedRefundAtomicity() (gas: 401262)\n[PASS] testSplitAndTreasuryOnlyFixedRecipient() (gas: 539911)\n[PASS] testSupplyTransferAndBurn() (gas: 134096)\n[PASS] testUnauthorizedCallbacks() (gas: 180808)\n[PASS] testUnblessedSellExactInput() (gas: 354959)\n[PASS] testUnblessedSellExactOutput() (gas: 357808)\nSuite result: ok. 27 passed; 0 failed; 0 skipped; finished in 330.19ms (312.53ms CPU time)\n\nRan 8 tests for test/SwapAdversarial.t.sol:ReverseOrderingSwapAdversarialTest\n[PASS] testBuybackCooldownAt59And60Seconds() (gas: 1236467)\n[PASS] testDustFeeRoundingAndSplitRemainder() (gas: 1588864)\n[PASS] testFuzzBuySellRoundTripNeverCreatesFwa(uint96,bool) (runs: 1000, μ: 1363830, ~: 614938)\nLogs:\n  Bound result 9999999999999999997768\n\n[PASS] testFuzzPoolDeltasFeeModesAndExactSplit(uint96,uint16,uint8) (runs: 1000, μ: 838903, ~: 481562)\nLogs:\n  Bound result 10056\n  Bound result 60\n\n[PASS] testManagerCannotInvokeIdleUnlockCallbacks() (gas: 144668)\n[PASS] testNoPublicAdminOrArbitraryAssetExit() (gas: 1537403)\n[PASS] testOutputFailureRestoresBlessingPoolClaimsAndBalances() (gas: 2343155)\n[PASS] testRouterInvalidInputsNeverMoveTokens() (gas: 680108)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 330.49ms (554.21ms CPU time)\n\nRan 27 tests for test/Hook.t.sol:ReverseOrderingHookTest\n[PASS] testBlessedExactOutput() (gas: 1931582)\n[PASS] testBlessedSellConsumesOnlyActualAmount() (gas: 2364991)\n[PASS] testBlessingWrongSellerDoesNotDiscount() (gas: 1923883)\n[PASS] testBuyDecayAndFreshManagerHasNoETH() (gas: 817391)\n[PASS] testBuyExactOutputAndRefund() (gas: 374951)\n[PASS] testBuybackBurnsAndPaysFromManager() (gas: 1240533)\n[PASS] testBuybackCapsHistoryAndBalance() (gas: 461553)\n[PASS] testBuybackMaximumBudgetIncludesReward() (gas: 817220)\n[PASS] testBuybackResistsSameBlockSpotManipulation() (gas: 1263573)\n[PASS] testExpiredBlessingAndSleeping() (gas: 2190648)\n[PASS] testForeignRouterCannotSpoofHookData() (gas: 1918036)\n[PASS] testFuzzSequenceConservesClaimShares(uint256) (runs: 128, μ: 3350776, ~: 3353486)\n[PASS] testFuzzSplitConservation(uint96) (runs: 128, μ: 377665, ~: 377770)\nLogs:\n  Bound result 3916086284800616756314\n\n[PASS] testFwaWalletToRouterReallyReverts() (gas: 33214)\n[PASS] testInitializationAndFlags() (gas: 16800)\n[PASS] testOneSidedPoolNoFwaCashBeforeSwap() (gas: 705179)\n[PASS] testOverAmountExactInputPaysHighAndKeepsBlessing() (gas: 1922913)\n[PASS] testOverAmountExactOutputRevertsAtomically() (gas: 1906020)\n[PASS] testPartialBuyRevertsNoUpfrontTaxLost() (gas: 326836)\n[PASS] testRejectOtherPoolAndReinitialize() (gas: 78493)\n[PASS] testRouterCannotPullVictimAllowance() (gas: 396870)\n[PASS] testRouterSlippageDeadlineAndFailedRefundAtomicity() (gas: 401262)\n[PASS] testSplitAndTreasuryOnlyFixedRecipient() (gas: 539911)\n[PASS] testSupplyTransferAndBurn() (gas: 134096)\n[PASS] testUnauthorizedCallbacks() (gas: 180808)\n[PASS] testUnblessedSellExactInput() (gas: 354959)\n[PASS] testUnblessedSellExactOutput() (gas: 357808)\nSuite result: ok. 27 passed; 0 failed; 0 skipped; finished in 330.46ms (283.93ms CPU time)\n\nRan 1 test for test/PrayInvariant.t.sol:PrayInvariantTest\n[PASS] invariantClaimsSupplyPaymentsAndBlessings() (runs: 256, calls: 24576, reverts: 0)\n\n╭-------------+-----------------+-------+---------+----------╮\n| Contract    | Selector        | Calls | Reverts | Discards |\n+============================================================+\n| PrayHandler | advance         | 2094  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | answer          | 2057  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | burnOwn         | 2044  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | buyback         | 2080  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | donatePray      | 2110  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | expire          | 2019  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | filePlea        | 2064  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | redeemTreasury  | 2029  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | swapExactInput  | 4069  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | swapExactOutput | 2022  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | transferPray    | 1988  | 0       | 0        |\n╰-------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 1800\n  Bound result 100000000000000000000\n  Bound result 10000000000000000000\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000\n  Bound result 7500000000000012987\n  Bound result 42973990474041843578\n  Bound result 567385551661130341111586\n  Bound result 887220\n  Bound result 200\n  Bound result 11695\n  Bound result 21895\n  Bound result 99999999999999999921\n  Bound result 427592078285904786914\n  Bound result 1791446260\n  Bound result 17266\n  Bound result 655851283820887520207\n  Bound result 4460\n  Bound result 4096\n  Bound result 665518356740125622096\n  Bound result 11407\n  Bound result 5622\n  Bound result 199\n  Bound result 22241721316406446639\n  Bound result 641109858063417846137\n  Bound result 68568318524448546459\n  Bound result 957608170972567106600\n  Bound result 789780324641068935497\n  Bound result 57343\n  Bound result 1839521547922559278896736\n  Bound result 2215\n  Bound result 35909\n  Bound result 14264337592751668711\n  Bound result 1018223407653572843576\n  Bound result 4214338013096390\n  Bound result 42973990474041844812\n  Bound result 86400\n  Bound result 100000000000000000000\n  Bound result 12345\n  Bound result 90000000000000000000\n  Bound result 4294967295\n  Bound result 63031\n  Bound result 7283\n  Bound result 99999999999999999215\n  Bound result 512\n  Bound result 999999999999999999274\n  Bound result 50000000000000000000000\n  Bound result 30000000\n  Bound result 5911\n  Bound result 5039\n  Bound result 7928\n  Bound result 21741\n  Bound result 20000000000000000000\n  Bound result 1914211904\n  Bound result 98860763092048774912\n  Bound result 901\n  Bound result 490\n  Bound result 99999999999999999016\n  Bound result 10000000000000000\n  Bound result 100000000000000000000\n  Bound result 2406\n  Bound result 1002\n  Bound result 1800\n  Bound result 21351\n  Bound result 4581\n  Bound result 4478\n  Bound result 4876\n  Bound result 99999999999999999738\n  Bound result 1\n  Bound result 156375512518472225413\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 9.13s (9.09s CPU time)\n\nRan 1 test for test/PrayInvariant.t.sol:ReverseOrderingPrayInvariantTest\n[PASS] invariantClaimsSupplyPaymentsAndBlessings() (runs: 256, calls: 24576, reverts: 0)\n\n╭-------------+-----------------+-------+---------+----------╮\n| Contract    | Selector        | Calls | Reverts | Discards |\n+============================================================+\n| PrayHandler | advance         | 2094  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | answer          | 2057  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | burnOwn         | 2044  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | buyback         | 2080  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | donatePray      | 2110  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | expire          | 2019  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | filePlea        | 2064  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | redeemTreasury  | 2029  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | swapExactInput  | 4069  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | swapExactOutput | 2022  | 0       | 0        |\n|-------------+-----------------+-------+---------+----------|\n| PrayHandler | transferPray    | 1988  | 0       | 0        |\n╰-------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 1800\n  Bound result 100000000000000000000\n  Bound result 10000000000000000000\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000\n  Bound result 7500000000000012987\n  Bound result 42973990474041843578\n  Bound result 567385551661130341111586\n  Bound result 887220\n  Bound result 200\n  Bound result 269060822\n  Bound result 19365\n  Bound result 1122\n  Bound result 4066\n  Bound result 1791446260\n  Bound result 19166\n  Bound result 999999999999999999739\n  Bound result 4096\n  Bound result 665518356740125622096\n  Bound result 3611\n  Bound result 999999999999999999117\n  Bound result 199\n  Bound result 33729597519414196878\n  Bound result 50019468882450585066\n  Bound result 68568318524448546459\n  Bound result 957608170972567106600\n  Bound result 789780324641068935497\n  Bound result 57343\n  Bound result 7870868611099814780249487\n  Bound result 3650\n  Bound result 35909\n  Bound result 14264337592751668711\n  Bound result 834187126383095622532\n  Bound result 4214338013096390\n  Bound result 42973990474041844812\n  Bound result 86400\n  Bound result 100000000000000000000\n  Bound result 12345\n  Bound result 90000000000000000000\n  Bound result 4294967295\n  Bound result 14956\n  Bound result 2120\n  Bound result 12499\n  Bound result 75193314715958846485\n  Bound result 512\n  Bound result 543125502876225626017\n  Bound result 23018238871816108338700\n  Bound result 30000000\n  Bound result 2349\n  Bound result 5700\n  Bound result 4607\n  Bound result 18525\n  Bound result 20000000000000000000\n  Bound result 2787\n  Bound result 78217875184172486074\n  Bound result 901\n  Bound result 9999009776441151024642686\n  Bound result 99999999999999999016\n  Bound result 10000000000000000\n  Bound result 100000000000000000000\n  Bound result 14701\n  Bound result 1002\n  Bound result 1800\n  Bound result 75834\n  Bound result 404834543\n  Bound result 841265250206603217135\n  Bound result 9369\n  Bound result 13175\n  Bound result 1\n  Bound result 156375512518472225413\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 9.38s (9.35s CPU time)\n\nRan 11 test suites in 9.38s (20.11s CPU time): 104 tests passed, 0 failed, 1 skipped (105 total tests)\n","passed":true},{"durationMs":68,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"BurnVault.buyback(uint256)\",\"BurnVault.unlockCallback(bytes)\",\"Hook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"Hook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"Hook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"Hook.redeemTreasury()\",\"Hook.unlockCallback(bytes)\",\"PleaRegistry.consume(address,uint256)\",\"PleaRegistry.expire(uint256)\",\"PleaRegistry.filePlea(uint256,string)\",\"PleaRegistry.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"PleaRegistry.submit(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"PrayRouter.swap((bool,int256,uint256,uint256,uint160,uint256))\",\"PrayRouter.unlockCallback(bytes)\",\"Token.approve(address,uint256)\",\"Token.burn(uint256)\",\"Token.transfer(address,uint256)\",\"Token.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"DEPENDENCIES.md\":15,\"README.md\":110,\"deployment-parameters.json\":10,\"docs/REVIEW.md\":54,\"docs/VALIDATION.md\":42,\"fork/Mainnet.t.sol\":51,\"foundry.toml\":19,\"remappings.txt\":4,\"scripts/check-build.mjs\":15,\"scripts/check-oracle.mjs\":11,\"scripts/configure-site.mjs\":18,\"scripts/mine-hook.mjs\":9,\"site/app.js\":61,\"site/deployment.json\":7,\"site/index.html\":23,\"site/style.css\":1,\"site/vendor/ethers.LICENSE.md\":21,\"site/vendor/ethers.js\":1,\"src/BurnVault.sol\":84,\"src/Hook.sol\":281,\"src/HookFlags.sol\":30,\"src/IPrayPool.sol\":10,\"src/OracleAttestation.sol\":239,\"src/PleaRegistry.sol\":208,\"src/PleaText.sol\":146,\"src/PrayRouter.sol\":84,\"src/Token.sol\":14,\"test/Hook.t.sol\":365,\"test/InitializationAdversarial.t.sol\":120,\"test/MainnetRehearsal.t.sol\":82,\"test/OracleConformance.t.sol\":100,\"test/PrayInvariant.t.sol\":430,\"test/README.md\":73,\"test/Registry.t.sol\":271,\"test/RegistryAdversarial.t.sol\":257,\"test/SwapAdversarial.t.sol\":267,\"test/TestBase.sol\":252,\"test/fixtures/live-attestation.json\":1,\"test/fixtures/live-request.json\":1,\"test/mocks/MockERC20.sol\":29,\"test/mocks/MockIntake.sol\":36,\"test/site/quote-race.mjs\":30},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"078d226ac5710d4505f91045096c3ac822b237535415aeedfd5b46403a63e06b","verifiedTreeHash":"f5dd07788fec0bbf3899f341feb0cd3d6d12f053","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":4215,"exitCode":0,"name":"build","output":"Compiling 119 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.04s\nCompiler run successful!\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/PrayRouter.sol:40:57\n   │\n40 │     constructor(IPoolManager manager_, IPrayPool hook_, address fwa_) {\n   │                                                         ━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-events-arithmetic]: `lastAnswerAt` is changed without an event but is used in arithmetic\n    ╭▸ src/PleaRegistry.sol:105:9\n    │\n105 │         lastAnswerAt = uint64(block.timestamp);\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/PrayRouter.sol:47:54\n   │\n47 │         if (trade.amount == 0 || trade.limit == 0 || block.timestamp > trade.deadline) revert InvalidTrade();\n   │                                                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[arbitrary-send-erc20]: `transferFrom` uses an arbitrary `from`; require it to equal `msg.sender` or `address(this)`\n   ╭▸ src/PrayRouter.sol:80:9\n   │\n80 │         IERC20(Currency.unwrap(inputCurrency)).safeTransferFrom(payer, address(manager), paid);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-erc20\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaRegistry.sol:28:38\n   │\n28 │     bytes32 public constant ACTION = bytes32(\"oracle.request@oracle-1\");\n   │                                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes32' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PrayRouter.sol:72:24\n   │\n72 │         uint256 paid = uint256(-int256(input));\n   │                        ━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PrayRouter.sol:73:28\n   │\n73 │         uint256 received = uint128(output);\n   │                            ━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint128' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n   ╭▸ src/PrayRouter.sol:81:9\n   │\n81 │         manager.settle();\n   │         ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[uninitialized-local]: local variable is read before being initialized\n   ╭▸ src/PleaText.sol:14:25\n   │\n14 │         for (uint256 i; i < s.length;) {\n   │                         ━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/PleaText.sol:32:17\n   │\n32 │                 revert InvalidText();\n   │                 ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/PleaText.sol:34:35\n   │\n34 │             if (i + n > s.length) revert InvalidText();\n   │                                   ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/PleaText.sol:37:42\n   │\n37 │                 if (next & 0xc0 != 0x80) revert InvalidText();\n   │                                          ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/PleaText.sol:43:15\n   │\n43 │             ) revert InvalidText();\n   │               ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PleaRegistry.sol:105:24\n    │\n105 │         lastAnswerAt = uint64(block.timestamp);\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PleaRegistry.sol:109:27\n    │\n109 │         return started && block.timestamp >= uint256(lastAnswerAt) + 24 hours;\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PleaRegistry.sol:114:16\n    │\n114 │         return block.timestamp < b.expiresAt ? b.remaining : 0;\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n   ╭▸ src/PleaText.sol:54:13\n   │\n54 │         out[k++] = '\"';\n   │             ━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaText.sol:63:23\n   │\n63 │                     ? bytes1(\"b\")\n   │                       ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaText.sol:64:32\n   │\n64 │                     : c == 9 ? bytes1(\"t\") : c == 10 ? bytes1(\"n\") : c == 12 ? bytes1(\"f\") : bytes1(\"r\");\n   │                                ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaText.sol:64:56\n   │\n64 │                     : c == 9 ? bytes1(\"t\") : c == 10 ? bytes1(\"n\") : c == 12 ? bytes1(\"f\") : bytes1(\"r\");\n   │                                                        ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaText.sol:64:80\n   │\n64 │                     : c == 9 ? bytes1(\"t\") : c == 10 ? bytes1(\"n\") : c == 12 ? bytes1(\"f\") : bytes1(\"r\");\n   │                                                                                ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaText.sol:64:94\n   │\n64 │                     : c == 9 ? bytes1(\"t\") : c == 10 ? bytes1(\"n\") : c == 12 ? bytes1(\"f\") : bytes1(\"r\");\n   │                                                                                              ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PleaRegistry.sol:128:29\n    │\n128 │         if (granted != 0 && block.timestamp < uint256(granted) + 24 hours) revert DailyLimit();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/PleaText.sol:101:16\n    │\n101 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n102 │ ┃             '{\"answerType\":\"bool\",\"chainId\":1,\"definitions\":{\"rubric\":',\n103 │ ┃             quote(RUBRIC),\n104 │ ┃             '},\"evidence\":\"panel\",\"question\":',\n105 │ ┃             quote(q),\n106 │ ┃             ',\"v\":1,\"window\":{\"fromBlock\":'\n107 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PleaRegistry.sol:149:9\n    │\n149 │         emit PleaFiled(id, requestId, msg.sender, amount, text);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/PleaText.sol:113:17\n    │\n113 │                 abi.encodePacked(pre, Strings.toString(from), ',\"toBlock\":', Strings.toString(to), \"}}\")\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/PleaText.sol:118:16\n    │\n118 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n119 │ ┃             '{\"v\":1,\"chainId\":1,\"window\":{\"hours\":1},\"answerType\":\"bool\",',\n120 │ ┃             '\"evidence\":\"panel\",\"panelSize\":7,\"quorum\":5,\"validForSeconds\":900,\"consumer\":{\"chainId\":1,\"verifyingContract\":\"',\n121 │ ┃             Strings.toHexString(registry),\n    ‡ ┃\n126 │ ┃             \"}\"\n127 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PleaRegistry.sol:148:30\n    │\n148 │             Plea(msg.sender, uint64(block.timestamp), Verdict.Pending, uint128(amount), document, requestId);\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PleaRegistry.sol:148:72\n    │\n148 │             Plea(msg.sender, uint64(block.timestamp), Verdict.Pending, uint128(amount), document, requestId);\n    │                                                                        ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/BurnVault.sol:57:35\n   │\n57 │         if (lastBuybackAt != 0 && block.timestamp < uint256(lastBuybackAt) + 60) revert Cooldown();\n   │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/BurnVault.sol:68:9\n   │\n68 │         emit BoughtAndBurned(msg.sender, spend, reward, burned);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/BurnVault.sol:62:25\n   │\n62 │         lastBuybackAt = uint64(block.timestamp);\n   │                         ━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint64' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/BurnVault.sol:81:18\n   │\n81 │                 -int256(spend),\n   │                  ━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/BurnVault.sol:88:23\n   │\n88 │         if (input != -int256(spend) || output <= 0 || uint128(output) < minimumPray) revert Slippage();\n   │                       ━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/BurnVault.sol:88:55\n   │\n88 │         if (input != -int256(spend) || output <= 0 || uint128(output) < minimumPray) revert Slippage();\n   │                                                       ━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint128' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/BurnVault.sol:91:67\n   │\n91 │         manager.take(Currency.wrap(address(pray)), address(this), uint128(output));\n   │                                                                   ━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint128' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/BurnVault.sol:92:19\n   │\n92 │         pray.burn(uint128(output));\n   │                   ━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint128' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/BurnVault.sol:93:27\n   │\n93 │         return abi.encode(uint256(uint128(output)));\n   │                           ━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/BurnVault.sol:93:35\n   │\n93 │         return abi.encode(uint256(uint128(output)));\n   │                                   ━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint128' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PleaRegistry.sol:187:13\n    │\n187 │         if (block.timestamp >= uint256(p.filedAt) + PENDING_LIFETIME) revert NotExpired();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PleaRegistry.sol:202:33\n    │\n202 │             if (granted != 0 && block.timestamp < uint256(granted) + 24 hours) revert DailyLimit();\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PleaRegistry.sol:206:9\n    │\n206 │         emit PleaJudged(id, a.requestId, approved, a.panelJobId, a.agreed);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PleaRegistry.sol:199:24\n    │\n199 │         lastAnswerAt = uint64(block.timestamp);\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PleaRegistry.sol:204:36\n    │\n204 │                 Blessing(p.amount, uint64(block.timestamp + 15 minutes), uint64(block.timestamp));\n    │                                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PleaRegistry.sol:204:74\n    │\n204 │                 Blessing(p.amount, uint64(block.timestamp + 15 minutes), uint64(block.timestamp));\n    │                                                                          ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PleaRegistry.sol:211:45\n    │\n211 │         if (p.verdict != Verdict.Pending || block.timestamp < uint256(p.filedAt) + PENDING_LIFETIME) {\n    │                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PleaRegistry.sol:223:24\n    │\n223 │         b.remaining -= uint128(amount);\n    │                        ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[missing-events-arithmetic]: `cumulativeTick` is changed without an event but is used in arithmetic\n    ╭▸ src/Hook.sol:266:9\n    │\n266 │         cumulativeTick += int128(lastTick) * int128(uint128(time - lastTime));\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `lastTime` is changed without an event but is used in arithmetic\n    ╭▸ src/Hook.sol:267:9\n    │\n267 │         lastTime = time;\n    │         ━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `lastTick` is changed without an event but is used in arithmetic\n    ╭▸ src/Hook.sol:222:9\n    │\n222 │         (, lastTick,,) = poolManager.getSlot0(poolId);\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/Hook.sol:71:40\n   │\n71 │     constructor(IPoolManager manager_, address token_, address factory_) BaseHook(manager_) {\n   │                                        ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Hook.sol:123:9\n    │\n123 │         emit Launched(PoolId.unwrap(poolId), address(registry), address(router), address(vault));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:117:22\n    │\n117 │         launchedAt = uint64(block.timestamp);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:118:20\n    │\n118 │         lastTime = uint64(block.timestamp);\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Hook.sol:129:16\n    │\n129 │         return elapsed >= 30 minutes ? 100 : uint16(5000 - 4900 * elapsed / 30 minutes);\n    │                ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:129:46\n    │\n129 │         return elapsed >= 30 minutes ? 100 : uint16(5000 - 4900 * elapsed / 30 minutes);\n    │                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint16' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/Hook.sol:186:24\n    │\n186 │         _setContext(3, blessed ? 1 : 0);\n    │                        ━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/Hook.sol:187:24\n    │\n187 │         _setContext(4, fee);\n    │                        ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:154:68\n    │\n154 │             p.amountSpecified == 0 || p.amountSpecified <= -int256(uint256(uint128(type(int128).max)))\n    │                                                                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:154:76\n    │\n154 │             p.amountSpecified == 0 || p.amountSpecified <= -int256(uint256(uint128(type(int128).max)))\n    │                                                                            ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:155:47\n    │\n155 │                 || p.amountSpecified > int256(uint256(uint128(type(int128).max)))\n    │                                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:155:55\n    │\n155 │                 || p.amountSpecified > int256(uint256(uint128(type(int128).max)))\n    │                                                       ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:169:65\n    │\n169 │                     allowance != 0 && (p.amountSpecified > 0 || uint256(-p.amountSpecified) <= allowance);\n    │                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:177:30\n    │\n177 │             uint256 amount = uint256(p.amountSpecified < 0 ? -p.amountSpecified : p.amountSpecified);\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:179:23\n    │\n179 │             if (fee > uint128(type(int128).max) || (p.amountSpecified < 0 && fee >= amount)) {\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:189:63\n    │\n189 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);\n    │                                                               ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:189:70\n    │\n189 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);\n    │                                                                      ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Hook.sol:245:9\n    │\n245 │         emit HookFee(seller, buy, fee, burnShare, treasury, rate);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:208:47\n    │\n208 │         int256 expected = p.amountSpecified + int256(beforeFee);\n    │                                               ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:212:23\n    │\n212 │         uint16 rate = uint16(_context(2));\n    │                       ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint16' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:213:34\n    │\n213 │         address seller = address(uint160(_context(1)));\n    │                                  ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:217:32\n    │\n217 │                 buy ? _ceilFee(uint256(-int256(fd)), rate, 10000 - rate) : _ceilFee(uint128(fd), rate, 10000);\n    │                                ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:217:85\n    │\n217 │                 buy ? _ceilFee(uint256(-int256(fd)), rate, 10000 - rate) : _ceilFee(uint128(fd), rate, 10000);\n    │                                                                                     ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:219:19\n    │\n219 │         if (fee > uint128(type(int128).max)) revert InvalidSwap();\n    │                   ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:220:56\n    │\n220 │         if (_context(3) != 0) registry.consume(seller, uint256(-int256(td)));\n    │                                                        ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:224:71\n    │\n224 │         return (IHooks.afterSwap.selector, fwaSpecified ? int128(0) : int128(int256(fee)));\n    │                                                                       ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:224:78\n    │\n224 │         return (IHooks.afterSwap.selector, fwaSpecified ? int128(0) : int128(int256(fee)));\n    │                                                                              ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `redeeming` is updated\n    ╭▸ src/Hook.sol:251:9\n    │\n251 │         poolManager.unlock(\"\");\n    │         ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/Hook.sol:251:9\n    │\n251 │         poolManager.unlock(\"\");\n    │         ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:265:23\n    │\n265 │         uint64 time = uint64(block.timestamp);\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/Hook.sol:290:17\n    │\n290 │         input = input * 9875 / 10000; // The immutable 1.25% LP fee.\n    │                 ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Hook.sol:280:17\n    │\n280 │             if (block.timestamp >= uint256(o.time) + 30 minutes && o.time > old.time) old = o;\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:284:57\n    │\n284 │             int256(cumulativeTick) + int256(lastTick) * int256(block.timestamp - lastTime) - old.cumulative;\n    │                                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:285:26\n    │\n285 │         int256 elapsed = int256(block.timestamp - old.time);\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:286:26\n    │\n286 │         int24 meanTick = int24(sum / elapsed);\n    │                          ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/PrayBase.t.sol:128:20\n    │\n128 │             uint64(block.timestamp),\n    │                    ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Hook.t.sol:51:9\n    │\n 51 │         vm.warp(initialTime + 1800);\n    │         ─────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/PrayBase.t.sol:158:29\n    │\n158 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Hook.t.sol:83:9\n    │\n 83 │         vm.warp(block.timestamp + 15 minutes);\n    │         ───────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/PrayBase.t.sol:159:30\n    │\n159 │         a.expiresAt = uint64(block.timestamp + 900);\n    │                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Hook.t.sol:83:9\n    │\n 83 │         vm.warp(block.timestamp + 15 minutes);\n    │         ───────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/Hook.t.sol:83:17\n   │\n83 │         vm.warp(block.timestamp + 15 minutes);\n   │         ────────━━━━━━━━━━━━━━━────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/PrayBase.t.sol:128:20\n    │\n128 │             uint64(block.timestamp),\n    │                    ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Hook.t.sol:223:9\n    │\n223 │         vm.warp(initialTime + 1800);\n    │         ─────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/PrayBase.t.sol:128:20\n    │\n128 │             uint64(block.timestamp),\n    │                    ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Hook.t.sol:249:9\n    │\n249 │         vm.warp(initialTime + 1800);\n    │         ─────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/PrayBase.t.sol:158:29\n    │\n158 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Registry.t.sol:63:9\n    │\n 63 │         vm.warp(block.timestamp + 30);\n    │         ───────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/PrayBase.t.sol:159:30\n    │\n159 │         a.expiresAt = uint64(block.timestamp + 900);\n    │                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Registry.t.sol:63:9\n    │\n 63 │         vm.warp(block.timestamp + 30);\n    │         ───────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/Registry.t.sol:63:17\n   │\n63 │         vm.warp(block.timestamp + 30);\n   │         ────────━━━━━━━━━━━━━━━────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Registry.t.sol:177:17\n    │\n177 │         vm.warp(block.timestamp + 1 hours);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/PrayBase.t.sol:158:29\n    │\n158 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Registry.t.sol:192:9\n    │\n192 │         vm.warp(block.timestamp + 24 hours - 1);\n    │         ─────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/PrayBase.t.sol:159:30\n    │\n159 │         a.expiresAt = uint64(block.timestamp + 900);\n    │                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Registry.t.sol:192:9\n    │\n192 │         vm.warp(block.timestamp + 24 hours - 1);\n    │         ─────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Registry.t.sol:192:17\n    │\n192 │         vm.warp(block.timestamp + 24 hours - 1);\n    │         ────────━━━━━━━━━━━━━━━──────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Registry.t.sol:195:17\n    │\n195 │         vm.warp(block.timestamp + 1);\n    │         ────────━━━━━━━━━━━━━━━───── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Registry.t.sol:203:17\n    │\n203 │         vm.warp(block.timestamp + 1);\n    │         ────────━━━━━━━━━━━━━━━───── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":753,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Token.t.sol:TokenTest\n[PASS] test_standardSupplyTransferAndBurn() (gas: 172875)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 447.02µs (317.39µs CPU time)\n\nRan 3 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] test_questionHashMatchesLiveOracleCanonicalDocument() (gas: 4555)\n[PASS] test_realMainnetOracleSignerAndWrongDomain() (gas: 901289)\n[PASS] test_registryDigestAndSelectorMatchProtocolVector() (gas: 751571)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 708.12µs (983.68µs CPU time)\n\nRan 1 test for test/Ordering.t.sol:FwaCurrencyOneTest\n[PASS] test_fourSwapModesBlessingAndBuybackForThisCurrencyOrder() (gas: 3657703)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 7.31ms (3.34ms CPU time)\n\nRan 1 test for test/FreshManager.t.sol:FreshManagerTest\n[PASS] test_feeClaimFallbackOnManagerWithNoFwaThenPermissionlessRedemption() (gas: 478577)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 20.86ms (327.19µs CPU time)\n\nRan 1 test for test/Ordering.t.sol:FwaCurrencyZeroTest\n[PASS] test_fourSwapModesBlessingAndBuybackForThisCurrencyOrder() (gas: 3657595)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 22.82ms (4.66ms CPU time)\n\nRan 13 tests for test/Registry.t.sol:RegistryTest\n[PASS] test_amountTextAndBalanceGuards() (gas: 4890426)\n[PASS] test_approvalCallbackUnder200kAndSeparatePublicSubmit() (gas: 3817379)\n[PASS] test_crossPleaSubstitutionAndUnrelatedQuestionFail() (gas: 3598511)\n[PASS] test_expiryTypeChainAndQuorumGuards() (gas: 2658278)\n[PASS] test_falseAnswerIsStoredAndCannotBless() (gas: 3599318)\n[PASS] test_maxEscapedPleaCallbackFitsStipend() (gas: 5904690)\n[PASS] test_oneBlessingPer24HoursNotCalendarDay() (gas: 3818718)\n[PASS] test_oneOpenPleaAndTimeoutWithoutRefund() (gas: 3599641)\n[PASS] test_onlyHookConsumesBlessingAndStartsClock() (gas: 2353460)\n[PASS] test_paysExactlyHalfImdAndBuildsValidEscapedJson() (gas: 2157682)\n[PASS] test_sleepExactlyAfter24HoursAndWakeOnNextFalseAnswer() (gas: 2225672)\n[PASS] test_unknownSettledAndReusedRequestIdsFail() (gas: 3748890)\n[PASS] test_wrongSignerDomainAndTamperedAnswerFail() (gas: 2276739)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 27.94ms (52.56ms CPU time)\n\nRan 1 test for test/Runtime.t.sol:RuntimeTest\n[PASS] test_runtimeSizeAndNoEscapeOpcodes() (gas: 5251635)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 29.34ms (7.19ms CPU time)\n\nRan 17 tests for test/Hook.t.sol:HookTest\n[PASS] testFuzz_feeAndConservation(uint96,bool,bool,uint16) (runs: 256, μ: 354819, ~: 358525)\n[PASS] test_blessedSellBothModesAndSpent() (gas: 3484638)\n[PASS] test_blessingExpiresAtBoundary() (gas: 2633087)\n[PASS] test_buyAtLaunchBothModes() (gas: 672604)\n[PASS] test_buybackCannotIgnoreHistoricalPriceFloor() (gas: 891109)\n[PASS] test_buybackCapCooldownRewardAndTrueBurn() (gas: 963797)\n[PASS] test_callbacksAndInitializationAccess() (gas: 18737068)\n[PASS] test_fwaRejectsWalletTransfersButSwapSettles() (gas: 404308)\n[PASS] test_linearDecayHalfwayAndEnd() (gas: 1052256)\n[PASS] test_noAdministrativeOrRescueFunctions() (gas: 378722)\n[PASS] test_partialFillsAndSlippageRevertAtomically() (gas: 406046)\n[PASS] test_routerDeadlineAndCannotSpendAnotherWallet() (gas: 282492)\n[PASS] test_sleepingSellsBothModes() (gas: 727237)\n[PASS] test_unblessedSellBothModes() (gas: 697458)\n[PASS] test_untrustedRouterCannotSpoofSeller() (gas: 3951483)\n[PASS] test_untrustedRouterUsesOriginBlessing() (gas: 3928282)\n[PASS] test_wrongSellerAndOverAmountCannotUseCheapRate() (gas: 2856015)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 45.19ms (69.34ms CPU time)\n\nRan 1 test for test/Conservation.t.sol:ConservationTest\n[PASS] invariant_allFeesHaveExactlyOneDestination() (runs: 64, calls: 2048, reverts: 0)\n\n╭-------------+----------+-------+---------+----------╮\n| Contract    | Selector | Calls | Reverts | Discards |\n+=====================================================+\n| SwapHandler | trade    | 2048  | 0       | 0        |\n╰-------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 660.97ms (620.31ms CPU time)\n\nRan 9 test suites in 662.36ms (815.58ms CPU time): 39 tests passed, 0 failed, 0 skipped (39 total tests)\n","passed":true},{"durationMs":55,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"BurnVault.buyback()\",\"BurnVault.buyback(uint256)\",\"BurnVault.unlockCallback(bytes)\",\"Hook.afterAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"Hook.afterDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)\",\"Hook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"Hook.afterRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"Hook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"Hook.beforeAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"Hook.beforeDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)\",\"Hook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"Hook.beforeRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"Hook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"Hook.redeemTreasury()\",\"Hook.unlockCallback(bytes)\",\"LaunchToken.approve(address,uint256)\",\"LaunchToken.burn(uint256)\",\"LaunchToken.transfer(address,uint256)\",\"LaunchToken.transferFrom(address,address,uint256)\",\"PleaRegistry.consume(address,uint256)\",\"PleaRegistry.expire(uint256)\",\"PleaRegistry.filePlea(uint256,string)\",\"PleaRegistry.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"PleaRegistry.start()\",\"PleaRegistry.submit(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"PrayRouter.swap((bool,bool,uint128,uint128,uint160,uint64,uint16))\",\"PrayRouter.unlockCallback(bytes)\"],\"files\":{\".gitignore\":5,\"LICENSE\":23,\"README.md\":123,\"deployment/launch-spec.json\":15,\"docs/DEPENDENCIES.md\":21,\"docs/INDEPENDENT_REVIEW.md\":66,\"docs/VERIFICATION.md\":61,\"foundry.toml\":23,\"integration/Mainnet.t.sol\":101,\"remappings.txt\":6,\"scripts/configure-site.cjs\":66,\"scripts/prepare-launch.cjs\":29,\"scripts/site.test.mjs\":40,\"site/abi.json\":1,\"site/app.mjs\":168,\"site/deployment.json\":1,\"site/index.html\":76,\"site/logic.mjs\":15,\"site/style.css\":4,\"site/vendor/ETHERS-LICENSE.md\":21,\"site/vendor/ethers.umd.min.js\":1,\"src/BurnVault.sol\":95,\"src/Hook.sol\":304,\"src/HookFlags.sol\":30,\"src/LaunchToken.sol\":16,\"src/OracleAttestation.sol\":239,\"src/PleaRegistry.sol\":226,\"src/PleaText.sol\":137,\"src/PrayRouter.sol\":85,\"test/Conservation.t.sol\":61,\"test/FreshManager.t.sol\":36,\"test/Hook.t.sol\":287,\"test/OracleConformance.t.sol\":98,\"test/Ordering.t.sol\":41,\"test/PrayBase.t.sol\":176,\"test/Registry.t.sol\":273,\"test/Runtime.t.sol\":24,\"test/Token.t.sol\":24,\"test/fixtures/LiveOracleFixture.sol\":48,\"test/fixtures/live-attestation.json\":1,\"test/fixtures/live-question.json\":18,\"test/fixtures/second-live-question.json\":25,\"test/mocks/MockERC20.sol\":13,\"test/mocks/MockFWA.sol\":19,\"test/mocks/MockIntake.sol\":34,\"test/mocks/MockSigner.sol\":16},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":4131,"exitCode":0,"name":"slither","output":"[high/high] arbitrary-send-erc20 at src/PrayRouter.sol:54: PrayRouter.unlockCallback(bytes) (src/PrayRouter.sol#54-84) uses arbitrary from in transferFrom: IERC20(Currency.unwrap(inputCurrency)).safeTransferFrom(payer,address(manager),paid) (src/PrayRouter.sol#80)\n[high/medium] weak-prng at src/Hook.sol:276: Hook.buybackFloor(uint256) (src/Hook.sol#276-303) uses a weak PRNG: \"sum < 0 && sum % elapsed != 0 (src/Hook.sol#287)\"\n[medium/medium] divide-before-multiply at src/Hook.sol:276: Hook.buybackFloor(uint256) (src/Hook.sol#276-303) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/Hook.sol:276: Hook.buybackFloor(uint256) (src/Hook.sol#276-303) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/Hook.sol:146: Hook._beforeSwap(address,PoolKey,SwapParams,bytes) (src/Hook.sol#146-190) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/Hook.sol:248: Reentrancy in Hook.redeemTreasury() (src/Hook.sol#248-253):\n[medium/medium] uninitialized-local at src/Hook.sol:277: Hook.buybackFloor(uint256).old (src/Hook.sol#277) is a local variable never initialized\n[medium/medium] uninitialized-local at src/PleaText.sol:20: PleaText.validate(bytes).n (src/PleaText.sol#20) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Hook.sol:161: Hook._beforeSwap(address,PoolKey,SwapParams,bytes).blessed (src/Hook.sol#161) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Hook.sol:175: Hook._beforeSwap(address,PoolKey,SwapParams,bytes).fee (src/Hook.sol#175) is a local variable never initialized\n[medium/medium] uninitialized-local at src/PleaText.sol:21: PleaText.validate(bytes).code (src/PleaText.sol#21) is a local variable never initialized\n[medium/medium] unused-return at src/Hook.sol:248: Hook.redeemTreasury() (src/Hook.sol#248-253) ignores return value by poolManager.unlock() (src/Hook.sol#251)\n[medium/medium] unused-return at src/PrayRouter.sol:54: PrayRouter.unlockCallback(bytes) (src/PrayRouter.sol#54-84) ignores return value by manager.settle() (src/PrayRouter.sol#81)\n[medium/medium] unused-return at src/Hook.sol:192: Hook._afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/Hook.sol#192-225) ignores return value by (None,lastTick,None,None) = poolManager.getSlot0(poolId) (src/Hook.sol#222)\n[medium/high] write-after-write at src/PrayRouter.sol:25: PrayRouter.active (src/PrayRouter.sol#25) is written in both\n[low/medium] missing-zero-check at src/PrayRouter.sol:40: PrayRouter.constructor(IPoolManager,IPrayPool,address).fwa_ (src/PrayRouter.sol#40) lacks a zero-check on :\n[low/medium] reentrancy-benign at src/BurnVault.sol:56: Reentrancy in BurnVault.buyback(uint256) (src/BurnVault.sol#56-69):\n[low/medium] reentrancy-benign at src/Hook.sol:231: Reentrancy in Hook._collect(uint256,address,bool,uint16) (src/Hook.sol#231-246):\n[low/medium] reentrancy-benign at src/PrayRouter.sol:46: Reentrancy in PrayRouter.swap(PrayRouter.Trade) (src/PrayRouter.sol#46-52):\n[low/medium] reentrancy-benign at src/Hook.sol:192: Reentrancy in Hook._afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/Hook.sol#192-225):\n[low/medium] reentrancy-benign at src/Hook.sol:231: Reentrancy in Hook._collect(uint256,address,bool,uint16) (src/Hook.sol#231-246):\n[low/medium] reentrancy-benign at src/PleaRegistry.sol:117: Reentrancy in PleaRegistry.filePlea(uint256,string) (src/PleaRegistry.sol#117-150):\n[low/medium] reentrancy-events at src/Hook.sol:103: Reentrancy in Hook._beforeInitialize(address,PoolKey,uint160) (src/Hook.sol#103-125):\n[low/medium] reentrancy-events at src/Hook.sol:192: Reentrancy in Hook._afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/Hook.sol#192-225):\n[low/medium] reentrancy-events at src/Hook.sol:231: Reentrancy in Hook._collect(uint256,address,bool,uint16) (src/Hook.sol#231-246):\n[low/medium] timestamp at src/Hook.sol:127: Hook.buyFeeBps() (src/Hook.sol#127-130) uses timestamp for comparisons\n[low/medium] timestamp at src/PleaRegistry.sol:112: PleaRegistry.available(address) (src/PleaRegistry.sol#112-115) uses timestamp for comparisons\n[low/medium] timestamp at src/PleaRegistry.sol:117: PleaRegistry.filePlea(uint256,string) (src/PleaRegistry.sol#117-150) uses timestamp for comparisons\n[low/medium] timestamp at src/PrayRouter.sol:46: PrayRouter.swap(PrayRouter.Trade) (src/PrayRouter.sol#46-52) uses timestamp for comparisons\n[low/medium] timestamp at src/PleaRegistry.sol:209: PleaRegistry.expire(uint256) (src/PleaRegistry.sol#209-217) uses timestamp for comparisons\n[low/medium] timestamp at src/Hook.sol:264: Hook._observe() (src/Hook.sol#264-273) uses timestamp for comparisons\n[low/medium] timestamp at src/PleaRegistry.sol:181: PleaRegistry._answer(bytes32,OracleAttestation.Attestation,bytes) (src/PleaRegistry.sol#181-207) uses timestamp for comparisons\n[low/medium] timestamp at src/Hook.sol:146: Hook._beforeSwap(address,PoolKey,SwapParams,bytes) (src/Hook.sol#146-190) uses timestamp for comparisons\n[low/medium] timestamp at src/PleaRegistry.sol:152: PleaRegistry.expectedQuestionHash(uint256,uint64,uint64) (src/PleaRegistry.sol#152-162) uses timestamp for comparisons\n[low/medium] timestamp at src/PleaRegistry.sol:108: PleaRegistry.sleeping() (src/PleaRegistry.sol#108-110) uses timestamp for comparisons\n[low/medium] timestamp at src/Hook.sol:103: Hook._beforeInitialize(address,PoolKey,uint160) (src/Hook.sol#103-125) uses timestamp for comparisons\n[low/medium] timestamp at src/BurnVault.sol:56: BurnVault.buyback(uint256) (src/BurnVault.sol#56-69) uses timestamp for comparisons\n[low/medium] timestamp at src/Hook.sol:276: Hook.buybackFloor(uint256) (src/Hook.sol#276-303) uses timestamp for comparisons\n[low/medium] timestamp at src/OracleAttestation.sol:164: OracleAttestationConsumer._verifyAttestation(OracleAttestation.Attestation,bytes) (src/OracleAttestation.sol#164-175) uses timestamp for comparisons","passed":false}],"detail":"static analysis found arbitrary-send-erc20 at src/PrayRouter.sol:54: PrayRouter.unlockCallback(bytes) (src/PrayRouter.sol#54-84) uses arbitrary from in transferFrom: IERC20(Currency.unwrap(inputCurrency)).safeTransferFrom(payer,address(manager),paid) (src/PrayRouter.sol#80)","evaluation":"checks","profile":"foundry","status":"rejected","submissionHash":"27ff85e7466d223540d24f5ff505e393d63113d055613ab2c6d6cd069785c054","verifiedTreeHash":"eebbc18665bcc6311de1a93d700123646c67ccc9","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":32322,"exitCode":0,"name":"build","output":"Compiling 106 files with Solc 0.8.26\nSolc 0.8.26 finished in 32.14s\nCompiler run successful with warnings:\nWarning (2018): Function state mutability can be restricted to pure\n   --> test/Registry.t.sol:224:5:\n    |\n224 |     function testFuzzJsonEscapeRoundTrip(bytes calldata raw) public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nwarning[missing-events-access-control]: `active` is changed without an event but is used for access control\n   ╭▸ src/PrayRouter.sol:64:16\n   │\n64 │         delete active;\n   │                ━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/PleaText.sol:31:17\n   │\n31 │                 revert InvalidText();\n   │                 ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/PleaText.sol:33:36\n   │\n33 │             if (i + n >= s.length) revert InvalidText();\n   │                                    ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/PleaText.sol:36:43\n   │\n36 │                 if (d < 0x80 || d > 0xbf) revert InvalidText();\n   │                                           ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/PrayRouter.sol:42:13\n   │\n42 │             block.timestamp > trade.deadline || trade.maxInput == 0\n   │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/PleaText.sol:42:15\n   │\n42 │             ) revert InvalidText();\n   │               ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[missing-events-access-control]: `active` is changed without an event but is used for access control\n   ╭▸ src/BurnVault.sol:58:16\n   │\n58 │         delete active;\n   │                ━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/PrayRouter.sol:56:9\n   │\n56 │         emit Traded(msg.sender, trade.buy, paid, received);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PrayRouter.sol:43:37\n   │\n43 │                 || trade.maxInput > uint256(uint128(type(int128).max)) || trade.minOutput == 0\n   │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PrayRouter.sol:43:45\n   │\n43 │                 || trade.maxInput > uint256(uint128(type(int128).max)) || trade.minOutput == 0\n   │                                             ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint128' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaText.sol:62:27\n   │\n62 │                         ? bytes1(\"b\")\n   │                           ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaText.sol:63:36\n   │\n63 │                         : c == 9 ? bytes1(\"t\") : c == 10 ? bytes1(\"n\") : c == 12 ? bytes1(\"f\") : bytes1(\"r\");\n   │                                    ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaText.sol:63:60\n   │\n63 │                         : c == 9 ? bytes1(\"t\") : c == 10 ? bytes1(\"n\") : c == 12 ? bytes1(\"f\") : bytes1(\"r\");\n   │                                                            ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaText.sol:63:84\n   │\n63 │                         : c == 9 ? bytes1(\"t\") : c == 10 ? bytes1(\"n\") : c == 12 ? bytes1(\"f\") : bytes1(\"r\");\n   │                                                                                    ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaText.sol:63:98\n   │\n63 │                         : c == 9 ? bytes1(\"t\") : c == 10 ? bytes1(\"n\") : c == 12 ? bytes1(\"f\") : bytes1(\"r\");\n   │                                                                                                  ━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes1' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/BurnVault.sol:39:50\n   │\n39 │         if (budget == 0 || budget > MAX_SPEND || block.timestamp < lastBuyback + 60) revert InvalidBuyback();\n   │                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PrayRouter.sol:74:24\n   │\n74 │         uint256 paid = uint256(-int256(inputDelta));\n   │                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PrayRouter.sol:75:28\n   │\n75 │         uint256 received = uint256(int256(outputDelta));\n   │                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/BurnVault.sol:51:9\n   │\n51 │         emit BoughtAndBurned(msg.sender, spend, reward, burned);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PrayRouter.sol:78:58\n   │\n78 │                 || (t.amountSpecified > 0 && received != uint256(t.amountSpecified))\n   │                                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/PleaText.sol:107:16\n    │\n107 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n108 │ ┃             '{\"answerType\":\"bool\",\"chainId\":1,\"definitions\":{\"rubric\":\"',\n109 │ ┃             RUBRIC,\n110 │ ┃             '\"},\"evidence\":\"panel\",\"question\":\"',\n111 │ ┃             escape(q),\n112 │ ┃             '\",\"v\":1,\"window\":'\n113 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/PleaText.sol:117:16\n    │\n117 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n118 │ ┃             p,\n119 │ ┃             '{\"hours\":1},\"panelSize\":7,\"quorum\":5,\"validForSeconds\":900,\"consumer\":{\"chainId\":1,\"verifyingContract\":\"',\n120 │ ┃             Strings.toHexString(registry),\n121 │ ┃             '\"}}'\n122 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/PleaText.sol:127:13\n    │\n127 │ ┏             abi.encodePacked(\n128 │ ┃                 p,\n129 │ ┃                 '{\"fromBlock\":',\n130 │ ┃                 Strings.toString(fromBlock),\n    ‡ ┃\n133 │ ┃                 \"}}\"\n134 │ ┃             )\n    │ ┗━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/BurnVault.sol:67:18\n   │\n67 │                 -int256(spend),\n   │                  ━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/BurnVault.sol:74:23\n   │\n74 │         if (input != -int256(spend) || output <= 0 || uint256(int256(output)) < floor) {\n   │                       ━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'int256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/BurnVault.sol:74:55\n   │\n74 │         if (input != -int256(spend) || output <= 0 || uint256(int256(output)) < floor) {\n   │                                                       ━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/BurnVault.sol:79:26\n   │\n79 │         uint256 burned = uint256(int256(output));\n   │                          ━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint256' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/PleaRegistry.sol:29:38\n   │\n29 │     bytes32 public constant ACTION = bytes32(\"oracle.request@oracle-1\");\n   │                                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'bytes32' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/PleaRegistry.sol:83:16\n   │\n83 │         return block.timestamp >= lastAnswer + 24 hours;\n   │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/PleaRegistry.sol:88:16\n   │\n88 │         return block.timestamp < b.until ? b.remaining : 0;\n   │                ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/PleaRegistry.sol:96:43\n   │\n96 │                 || (b.lastGranted != 0 && block.timestamp < uint256(b.lastGranted) + 24 hours)\n   │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PleaRegistry.sol:122:9\n    │\n122 │         emit PleaFiled(id, msg.sender, requestId, amount, text);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PleaRegistry.sol:103:13\n    │\n103 │             uint64(block.timestamp),\n    │             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PleaRegistry.sol:162:43\n    │\n162 │         if (p.status != Status.Pending || block.timestamp >= uint256(p.filedAt) + PENDING_LIFETIME) {\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PleaRegistry.sol:180:39\n    │\n180 │             if (b.lastGranted != 0 && block.timestamp < uint256(b.lastGranted) + 24 hours) {\n    │                                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/PleaRegistry.sol:187:9\n    │\n187 │ ┏         emit PleaAnswered(\n188 │ ┃             id, a.requestId, approved, a.agreed, uint64(block.timestamp + (approved ? 15 minutes : 0))\n189 │ ┃         );\n    │ ┗━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PleaRegistry.sol:184:23\n    │\n184 │             b.until = uint64(block.timestamp + 15 minutes);\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PleaRegistry.sol:185:29\n    │\n185 │             b.lastGranted = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/PleaRegistry.sol:188:50\n    │\n188 │             id, a.requestId, approved, a.agreed, uint64(block.timestamp + (approved ? 15 minutes : 0))\n    │                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/PleaRegistry.sol:194:43\n    │\n194 │         if (p.status != Status.Pending || block.timestamp < uint256(p.filedAt) + PENDING_LIFETIME) {\n    │                                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[missing-events-arithmetic]: `cumulative` is changed without an event but is used in arithmetic\n    ╭▸ src/Hook.sol:246:9\n    │\n246 │         cumulative += int128(int256(lastTick) * int256(elapsed));\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/Hook.sol:247:9\n    │\n247 │         observedAt = uint64(block.timestamp);\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `lastTick` is changed without an event but is used in arithmetic\n    ╭▸ src/Hook.sol:210:9\n    │\n210 │         lastTick = tick;\n    │         ━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/Hook.sol:69:40\n   │\n69 │     constructor(IPoolManager manager_, address token_, address factory_) {\n   │                                        ━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Hook.sol:118:9\n    │\n118 │         emit PoolOpened(PoolId.unwrap(poolId), address(registry), address(router), address(vault));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:115:22\n    │\n115 │         observedAt = uint64(block.timestamp);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:117:39\n    │\n117 │         observations[0] = Observation(uint64(block.timestamp), 0);\n    │                                       ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Hook.sol:124:16\n    │\n124 │         return elapsed >= 30 minutes ? 100 : 5000 - 4900 * elapsed / 30 minutes;\n    │                ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Hook.sol:222:9\n    │\n222 │         emit FeeCollected(seller, buy, fee, vaultPart, treasuryPart, rate);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:154:66\n    │\n154 │             blessed = available > 0 && (p.amountSpecified > 0 || uint256(-p.amountSpecified) <= available);\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:166:54\n    │\n166 │             uint256 amount = p.amountSpecified < 0 ? uint256(-p.amountSpecified) : uint256(p.amountSpecified);\n    │                                                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:166:84\n    │\n166 │             uint256 amount = p.amountSpecified < 0 ? uint256(-p.amountSpecified) : uint256(p.amountSpecified);\n    │                                                                                    ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:170:23\n    │\n170 │             if (fee > uint256(uint128(type(int128).max))) revert InvalidSwap();\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:170:31\n    │\n170 │             if (fee > uint256(uint128(type(int128).max))) revert InvalidSwap();\n    │                               ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:176:63\n    │\n176 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);\n    │                                                               ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:176:70\n    │\n176 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);\n    │                                                                      ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:191:34\n    │\n191 │         address seller = address(uint160(_load(SELLER)));\n    │                                  ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:193:67\n    │\n193 │         if (!buy && _load(BLESSED) != 0) registry.consume(seller, uint256(-int256(prayDelta)));\n    │                                                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:198:51\n    │\n198 │             int256 expected = p.amountSpecified + int256(_load(UPFRONT));\n    │                                                   ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:201:36\n    │\n201 │             uint256 amount = buy ? uint256(-int256(fwaDelta)) : uint256(int256(fwaDelta));\n    │                                    ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:201:65\n    │\n201 │             uint256 amount = buy ? uint256(-int256(fwaDelta)) : uint256(int256(fwaDelta));\n    │                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:205:23\n    │\n205 │             if (fee > uint256(uint128(type(int128).max))) revert InvalidSwap();\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:205:31\n    │\n205 │             if (fee > uint256(uint128(type(int128).max))) revert InvalidSwap();\n    │                               ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:212:44\n    │\n212 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\n    │                                            ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:212:51\n    │\n212 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\n    │                                                   ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/Hook.sol:240:9\n    │\n240 │         emit TreasuryPaid(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Hook.sol:248:13\n    │\n248 │         if (block.timestamp >= uint256(observations[observationIndex].time) + 60) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:246:23\n    │\n246 │         cumulative += int128(int256(lastTick) * int256(elapsed));\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:246:49\n    │\n246 │         cumulative += int128(int256(lastTick) * int256(elapsed));\n    │                                                 ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:247:22\n    │\n247 │         observedAt = uint64(block.timestamp);\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:250:58\n    │\n250 │             observations[observationIndex] = Observation(uint64(block.timestamp), cumulative);\n    │                                                          ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Hook.sol:255:29\n    │\n255 │         if (!initialized || block.timestamp < launchedAt + 30 minutes) revert HistoryTooShort();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/Hook.sol:259:38\n    │\n259 │             if (o.time > old.time && uint256(o.time) + 30 minutes <= block.timestamp) old = o;\n    │                                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:262:66\n    │\n262 │         int256 current = int256(cumulative) + int256(lastTick) * int256(block.timestamp - observedAt);\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:265:37\n    │\n265 │         int256 average = sum < 0 ? -int256((uint256(-sum) + elapsed - 1) / elapsed) : sum / int256(elapsed);\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:265:45\n    │\n265 │         int256 average = sum < 0 ? -int256((uint256(-sum) + elapsed - 1) / elapsed) : sum / int256(elapsed);\n    │                                             ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:265:93\n    │\n265 │         int256 average = sum < 0 ? -int256((uint256(-sum) + elapsed - 1) / elapsed) : sum / int256(elapsed);\n    │                                                                                             ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/Hook.sol:266:52\n    │\n266 │         uint160 sqrt = TickMath.getSqrtPriceAtTick(int24(average));\n    │                                                    ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:205:78\n    │\n205 │         return router.swap(PrayRouter.Trade(buy, amount, max, 1, limit(buy), block.timestamp));\n    │                                                                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Hook.t.sol:86:9\n    │\n 86 │         vm.warp(hook.launchedAt() + 900);\n    │         ──────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:205:78\n    │\n205 │         return router.swap(PrayRouter.Trade(buy, amount, max, 1, limit(buy), block.timestamp));\n    │                                                                              ━━━━━━━━━━━━━━━\n    ‡\n250 │         vm.warp(hook.launchedAt() + 1800);\n    │         ───────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:233:20\n    │\n233 │             uint64(block.timestamp),\n    │                    ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Hook.t.sol:174:9\n    │\n174 │         vm.warp(block.timestamp + 900);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:234:20\n    │\n234 │             uint64(block.timestamp + 900)\n    │                    ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Hook.t.sol:174:9\n    │\n174 │         vm.warp(block.timestamp + 900);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Hook.t.sol:174:17\n    │\n174 │         vm.warp(block.timestamp + 900);\n    │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:205:78\n    │\n205 │         return router.swap(PrayRouter.Trade(buy, amount, max, 1, limit(buy), block.timestamp));\n    │                                                                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Hook.t.sol:178:9\n    │\n178 │         vm.warp(registry.lastAnswer() + 86400);\n    │         ────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:233:20\n    │\n233 │             uint64(block.timestamp),\n    │                    ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Registry.t.sol:94:9\n    │\n 94 │         vm.warp(block.timestamp + 901);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:234:20\n    │\n234 │             uint64(block.timestamp + 900)\n    │                    ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Registry.t.sol:94:9\n    │\n 94 │         vm.warp(block.timestamp + 901);\n    │         ────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/Registry.t.sol:94:17\n   │\n94 │         vm.warp(block.timestamp + 901);\n   │         ────────━━━━━━━━━━━━━━━─────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:233:20\n    │\n233 │             uint64(block.timestamp),\n    │                    ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Registry.t.sol:178:9\n    │\n178 │         vm.warp(block.timestamp + 86399);\n    │         ──────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/TestBase.sol:234:20\n    │\n234 │             uint64(block.timestamp + 900)\n    │                    ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Registry.t.sol:178:9\n    │\n178 │         vm.warp(block.timestamp + 86399);\n    │         ──────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Registry.t.sol:178:17\n    │\n178 │         vm.warp(block.timestamp + 86399);\n    │         ────────━━━━━━━━━━━━━━━───────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Registry.t.sol:238:17\n    │\n238 │         vm.warp(block.timestamp + 86400);\n    │         ────────━━━━━━━━━━━━━━━───────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":340,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] testProtocolDigestSignatureAndSelector() (gas: 715841)\n[PASS] testRealMainnetSignerAttestation() (gas: 3355590)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 853.63µs (1.08ms CPU time)\n\nRan 27 tests for test/Hook.t.sol:ReverseOrderingHookTest\n[PASS] testBlessedExactOutput() (gas: 1931582)\n[PASS] testBlessedSellConsumesOnlyActualAmount() (gas: 2364991)\n[PASS] testBlessingWrongSellerDoesNotDiscount() (gas: 1923883)\n[PASS] testBuyDecayAndFreshManagerHasNoETH() (gas: 817391)\n[PASS] testBuyExactOutputAndRefund() (gas: 374951)\n[PASS] testBuybackBurnsAndPaysFromManager() (gas: 1240533)\n[PASS] testBuybackCapsHistoryAndBalance() (gas: 461553)\n[PASS] testBuybackMaximumBudgetIncludesReward() (gas: 817220)\n[PASS] testBuybackResistsSameBlockSpotManipulation() (gas: 1263573)\n[PASS] testExpiredBlessingAndSleeping() (gas: 2190648)\n[PASS] testForeignRouterCannotSpoofHookData() (gas: 1918036)\n[PASS] testFuzzSequenceConservesClaimShares(uint256) (runs: 128, μ: 3352146, ~: 3353468)\n[PASS] testFuzzSplitConservation(uint96) (runs: 128, μ: 377660, ~: 377770)\nLogs:\n  Bound result 9821025405724163615322\n\n[PASS] testFwaWalletToRouterReallyReverts() (gas: 33214)\n[PASS] testInitializationAndFlags() (gas: 16800)\n[PASS] testOneSidedPoolNoFwaCashBeforeSwap() (gas: 705179)\n[PASS] testOverAmountExactInputPaysHighAndKeepsBlessing() (gas: 1922913)\n[PASS] testOverAmountExactOutputRevertsAtomically() (gas: 1906020)\n[PASS] testPartialBuyRevertsNoUpfrontTaxLost() (gas: 326836)\n[PASS] testRejectOtherPoolAndReinitialize() (gas: 78493)\n[PASS] testRouterCannotPullVictimAllowance() (gas: 396870)\n[PASS] testRouterSlippageDeadlineAndFailedRefundAtomicity() (gas: 401262)\n[PASS] testSplitAndTreasuryOnlyFixedRecipient() (gas: 539911)\n[PASS] testSupplyTransferAndBurn() (gas: 134096)\n[PASS] testUnauthorizedCallbacks() (gas: 180808)\n[PASS] testUnblessedSellExactInput() (gas: 354959)\n[PASS] testUnblessedSellExactOutput() (gas: 357808)\nSuite result: ok. 27 passed; 0 failed; 0 skipped; finished in 211.61ms (226.46ms CPU time)\n\nRan 15 tests for test/Registry.t.sol:RegistryTest\n[PASS] testCallbackUnknownRequestAndUnauthorized() (gas: 1498545)\n[PASS] testCallbackWithinStipendAndPublicFallback() (gas: 2551650)\n[PASS] testEscapedTextIsValidJson() (gas: 1397628)\n[PASS] testExpirePendingNoRefundAndWakeOnNextAnswer() (gas: 2813848)\n[PASS] testExpiredAndFutureAttestations() (gas: 1516777)\n[PASS] testFalseAnswerStoresVerdictAndDoesNotBless() (gas: 1540623)\n[PASS] testFilePleaPaysAndPinsTemplate() (gas: 1506099)\n[PASS] testFuzzJsonEscapeRoundTrip(bytes) (runs: 128, μ: 49053, ~: 53873)\n[PASS] testMaxEscapedTextCallbackUnder200k() (gas: 5787991)\n[PASS] testOneOpenAndOneBlessingPer24Hours() (gas: 2535656)\n[PASS] testOnlyHookCanConsume() (gas: 1681728)\n[PASS] testPleaSizeLimitBalanceAndUtf8() (gas: 1567901)\n[PASS] testReplayAndWrongPleaCannotBorrowApproval() (gas: 2534474)\n[PASS] testWrongSignerAndDomain() (gas: 1603694)\n[PASS] testWrongTypeChainPanelAndWindow() (gas: 1808081)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 211.61ms (194.42ms CPU time)\n\nRan 27 tests for test/Hook.t.sol:HookTest\n[PASS] testBlessedExactOutput() (gas: 1931570)\n[PASS] testBlessedSellConsumesOnlyActualAmount() (gas: 2364979)\n[PASS] testBlessingWrongSellerDoesNotDiscount() (gas: 1923871)\n[PASS] testBuyDecayAndFreshManagerHasNoETH() (gas: 817391)\n[PASS] testBuyExactOutputAndRefund() (gas: 374951)\n[PASS] testBuybackBurnsAndPaysFromManager() (gas: 1240533)\n[PASS] testBuybackCapsHistoryAndBalance() (gas: 461553)\n[PASS] testBuybackMaximumBudgetIncludesReward() (gas: 817220)\n[PASS] testBuybackResistsSameBlockSpotManipulation() (gas: 1263573)\n[PASS] testExpiredBlessingAndSleeping() (gas: 2190636)\n[PASS] testForeignRouterCannotSpoofHookData() (gas: 1918036)\n[PASS] testFuzzSequenceConservesClaimShares(uint256) (runs: 128, μ: 3352229, ~: 3353879)\n[PASS] testFuzzSplitConservation(uint96) (runs: 128, μ: 377643, ~: 377752)\nLogs:\n  Bound result 7507626885104787109663\n\n[PASS] testFwaWalletToRouterReallyReverts() (gas: 33214)\n[PASS] testInitializationAndFlags() (gas: 16800)\n[PASS] testOneSidedPoolNoFwaCashBeforeSwap() (gas: 705179)\n[PASS] testOverAmountExactInputPaysHighAndKeepsBlessing() (gas: 1922925)\n[PASS] testOverAmountExactOutputRevertsAtomically() (gas: 1906032)\n[PASS] testPartialBuyRevertsNoUpfrontTaxLost() (gas: 326836)\n[PASS] testRejectOtherPoolAndReinitialize() (gas: 78493)\n[PASS] testRouterCannotPullVictimAllowance() (gas: 396870)\n[PASS] testRouterSlippageDeadlineAndFailedRefundAtomicity() (gas: 401262)\n[PASS] testSplitAndTreasuryOnlyFixedRecipient() (gas: 539911)\n[PASS] testSupplyTransferAndBurn() (gas: 134096)\n[PASS] testUnauthorizedCallbacks() (gas: 180808)\n[PASS] testUnblessedSellExactInput() (gas: 354959)\n[PASS] testUnblessedSellExactOutput() (gas: 357808)\nSuite result: ok. 27 passed; 0 failed; 0 skipped; finished in 211.65ms (207.48ms CPU time)\n\nRan 4 test suites in 213.02ms (635.73ms CPU time): 71 tests passed, 0 failed, 0 skipped (71 total tests)\n","passed":true},{"durationMs":56,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"BurnVault.buyback(uint256)\",\"BurnVault.unlockCallback(bytes)\",\"Hook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"Hook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"Hook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"Hook.redeemTreasury()\",\"Hook.unlockCallback(bytes)\",\"PleaRegistry.consume(address,uint256)\",\"PleaRegistry.expire(uint256)\",\"PleaRegistry.filePlea(uint256,string)\",\"PleaRegistry.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"PleaRegistry.submit(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"PrayRouter.swap((bool,int256,uint256,uint256,uint160,uint256))\",\"PrayRouter.unlockCallback(bytes)\",\"Token.approve(address,uint256)\",\"Token.burn(uint256)\",\"Token.transfer(address,uint256)\",\"Token.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"DEPENDENCIES.md\":15,\"README.md\":110,\"deployment-parameters.json\":10,\"docs/REVIEW.md\":54,\"docs/VALIDATION.md\":42,\"fork/Mainnet.t.sol\":51,\"foundry.toml\":19,\"remappings.txt\":4,\"scripts/check-build.mjs\":15,\"scripts/check-oracle.mjs\":11,\"scripts/configure-site.mjs\":18,\"scripts/mine-hook.mjs\":9,\"site/app.js\":61,\"site/deployment.json\":7,\"site/index.html\":23,\"site/style.css\":1,\"site/vendor/ethers.LICENSE.md\":21,\"site/vendor/ethers.js\":1,\"src/BurnVault.sol\":84,\"src/Hook.sol\":281,\"src/HookFlags.sol\":30,\"src/IPrayPool.sol\":10,\"src/OracleAttestation.sol\":239,\"src/PleaRegistry.sol\":208,\"src/PleaText.sol\":146,\"src/PrayRouter.sol\":84,\"src/Token.sol\":14,\"test/Hook.t.sol\":365,\"test/OracleConformance.t.sol\":100,\"test/Registry.t.sol\":271,\"test/TestBase.sol\":252,\"test/fixtures/live-attestation.json\":1,\"test/fixtures/live-request.json\":1,\"test/mocks/MockERC20.sol\":29,\"test/mocks/MockIntake.sol\":36,\"test/site/quote-race.mjs\":30},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":8228,"exitCode":0,"name":"slither","output":"[medium/high] incorrect-equality at src/PleaRegistry.sol:125: PleaRegistry.questionPrefix(uint256) (src/PleaRegistry.sol#125-131) uses a dangerous strict equality:\n[medium/medium] unused-return at src/Hook.sol:179: Hook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/Hook.sol#179-213) ignores return value by (None,tick,None,None) = poolManager.getSlot0(poolId) (src/Hook.sol#209)\n[low/medium] reentrancy-benign at src/BurnVault.sol:38: Reentrancy in BurnVault.buyback(uint256) (src/BurnVault.sol#38-52):\n[low/medium] reentrancy-benign at src/PleaRegistry.sol:91: Reentrancy in PleaRegistry.filePlea(uint256,string) (src/PleaRegistry.sol#91-123):\n[low/medium] reentrancy-benign at src/Hook.sol:179: Reentrancy in Hook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/Hook.sol#179-213):\n[low/medium] reentrancy-benign at src/Hook.sol:228: Reentrancy in Hook.redeemTreasury() (src/Hook.sol#228-232):\n[low/medium] reentrancy-benign at src/PrayRouter.sol:40: Reentrancy in PrayRouter.swap(PrayRouter.Trade) (src/PrayRouter.sol#40-57):\n[low/medium] reentrancy-events at src/Hook.sol:179: Reentrancy in Hook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/Hook.sol#179-213):\n[low/medium] reentrancy-events at src/Hook.sol:234: Reentrancy in Hook.unlockCallback(bytes) (src/Hook.sol#234-242):\n[low/medium] timestamp at src/PleaRegistry.sol:82: PleaRegistry.sleeping() (src/PleaRegistry.sol#82-84) uses timestamp for comparisons\n[low/medium] timestamp at src/PleaRegistry.sol:157: PleaRegistry._answer(bytes32,OracleAttestation.Attestation,bytes) (src/PleaRegistry.sol#157-190) uses timestamp for comparisons\n[low/medium] timestamp at src/Hook.sol:254: Hook.buybackFloor(uint256) (src/Hook.sol#254-280) uses timestamp for comparisons\n[low/medium] timestamp at src/PleaRegistry.sol:125: PleaRegistry.questionPrefix(uint256) (src/PleaRegistry.sol#125-131) uses timestamp for comparisons\n[low/medium] timestamp at src/Hook.sol:122: Hook.buyFeeBps() (src/Hook.sol#122-125) uses timestamp for comparisons\n[low/medium] timestamp at src/PrayRouter.sol:40: PrayRouter.swap(PrayRouter.Trade) (src/PrayRouter.sol#40-57) uses timestamp for comparisons\n[low/medium] timestamp at src/PleaRegistry.sol:91: PleaRegistry.filePlea(uint256,string) (src/PleaRegistry.sol#91-123) uses timestamp for comparisons\n[low/medium] timestamp at src/Hook.sol:244: Hook._observe() (src/Hook.sol#244-252) uses timestamp for comparisons\n[low/medium] timestamp at src/PleaRegistry.sol:86: PleaRegistry.available(address) (src/PleaRegistry.sol#86-89) uses timestamp for comparisons\n[low/medium] timestamp at src/PleaRegistry.sol:192: PleaRegistry.expire(uint256) (src/PleaRegistry.sol#192-200) uses timestamp for comparisons\n[low/medium] timestamp at src/OracleAttestation.sol:164: OracleAttestationConsumer._verifyAttestation(OracleAttestation.Attestation,bytes) (src/OracleAttestation.sol#164-175) uses timestamp for comparisons\n[low/medium] timestamp at src/BurnVault.sol:38: BurnVault.buyback(uint256) (src/BurnVault.sol#38-52) uses timestamp for comparisons","passed":true},{"durationMs":730,"exitCode":0,"name":"aderyn","output":"[high] abi-encode-packed-hash-collision at src/PleaText.sol:107: `abi.encodePacked()` Hash Collision (3 places)\n[high] reentrancy-state-change at src/BurnVault.sol:43: Reentrancy: State change after external call (10 places)\n[high] unsafe-casting at src/Hook.sol:266: Unsafe Casting of integers\n[high] yul-return at src/PleaText.sol:144: Yul block contains `return`\n[low] internal-function-used-once at src/HookFlags.sol:23: Internal Function Used Only Once (3 places)\n[low] large-numeric-literal at src/BurnVault.sol:20: Large Numeric Literal (9 places)\n[low] literal-instead-of-constant at src/BurnVault.sol:40: Literal Instead of Constant (51 places)\n[low] local-variable-shadowing at src/Hook.sol:45: Local Variable Shadows State Variable\n[low] require-revert-in-loop at src/PleaText.sol:13: Loop Contains `require`/`revert` (2 places)\n[low] state-change-without-event at src/BurnVault.sol:54: State Change Without Event (3 places)\n[low] unused-public-function at src/Hook.sol:88: Public Function Not Used Internally (2 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"88eb13467209315434218c2a8999c40107c0aecfc8c5031752f54a61631e9dba","verifiedTreeHash":"3f6ffaa2026687e23bfd98a156f6ae13f603412d","verifierVersion":"0.1.0+ad90ce4c"}]}