{"assessments":[],"deployments":[{"attestationHash":"8bb3d7f981a63cc57672b141d7d2f445e36adb3710b55d64c45b08e7b8fe32a3","chainId":1,"contracts":[{"address":"0x79731050ffe7911a0ed4beee706c319f334bf3a8","blockNumber":26210271,"name":"WebsiteBuyer","role":"other","txHash":"0x7324afdee819537e186817874bba94588e1aa900a13683d33c33536377f7e745"}],"id":"b6f14292-434a-4839-b780-effee70db820","manifestHash":"ce881e0b761e1cd7ee2a142648e7a237d1896ab2c2140e94387d895f57225916","status":"live"}],"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":"a07b01eb-c2ad-4a47-9925-8e287ec6b0dc","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"7b7993ea982794b9e93dfdb42e3cb27334caa1be2523c95500e85cc92d99f571","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":"22b0ee990e94a6dadab7b6679f45e7fd56d2e7c1559ff1d8283dbf73d8d86ac1","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":"483c6fa317d19f8b77e70402fda53200fb30404163c5b427e8b5da61c43d5d28","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"2905aa811dcf1ac767eb9a8f5f37819206bb6b1b0565b87d5608e5f1febcb8f3","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":"16b174707b0d4ff16e3772ef16c4e294e28e0aaf388a882b6e4404ae20b82da3","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":"5dece06b828104f6e4e135a93d1b6353fe34b76281cec128738e2a673692e5d9","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"d59561da3b378461b7c749f6bd5084477d44993aa94972ddea90f542535751df","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"d59561da3b378461b7c749f6bd5084477d44993aa94972ddea90f542535751df","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"59f11f0b7294d56aed393a13021ad844ddf5b01483aca865c7e078ba4e9dca7a","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"4eb2502fb52e5bfed15e6fd392d393a4ab9a972531cd0695c0b62195a0af10aa","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"4c76766b00591dfe982fe85d3de589fdb2f810e5137c301155d1c5e36a1d3b23","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"4c76766b00591dfe982fe85d3de589fdb2f810e5137c301155d1c5e36a1d3b23","skillId":"write-foundry-tests","state":"accepted"}],"objective":"A contract on Ethereum where anyone pays 0.003 eth (and we have a setter to change it price) and it goes buys with that imd on a univ4 pool so we have 0.5 imd or more (extra stays in the contract) and describes a website in a few words, and the contract buys a job.open from the IdentityMD Intake (v2, 0xa43e6f75ee006411f79ac1c84120606c2330de82) that builds and hosts that site for them. the project stays theirs so we must pass the owner as the user executing the tx. Body template: {\"objective\":\"Build a one-page website: <words>\",\"skill\":\"build-website\",\"host\":true}. Store each request's result hash and URI and emit them. eth price, Intake, IMD token and templates are owner-settable.","parentJobId":null,"planHash":"94dcd3beaf230cee7d6a8b474613397094949b3e5cf6e11cb740462c481359f9","previousHash":"490766f61bc955d47c33a64d7d5a7863130f3aafb5325bcbcaf089da2423e041","projectId":"a07b01eb-c2ad-4a47-9925-8e287ec6b0dc","publication":{"commit":"88b8c3c79fbd44c32de318a043df565690207d14","deliveredAt":"2026-10-17T05:44:52.413Z","repoUrl":"https://github.com/identity-md-launches/launch-2054-contract-ethereum-where-anyone"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[],"site":null,"snapshotHash":"0456194a5dd0f19cb62c76db1eb41c2785532f4e3cb8634ece0fa3353555005c","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"1ca477e8d9b58040","findings":[{"citation":"resolved","description":"bodyOf() appends ',\"owner\":\"<caller>\"' to every job.open body after the template parts. The brief's template and the intake-client reference's only accepted job.open body are {\"objective\":\"Build a one-page website: <words>\",\"skill\":\"build-website\",\"host\":true}; the reference lists `owner` only for launch.open kinds (\"Set owner where the kind takes one\") and says an unexpected key refuses the whole ask with status 1 while the price is spent and nothing is refunded. The reference also says project ownership for a job comes from the payer, which this contract already handles with the per-user IntakeAccount, so the key is not needed for 'the project stays theirs'. Two defects combine: (a) trust gap: the body departs from the checked shape and was never preflighted (README admits the checker returned 404; this review could not reach it either), so if the plane refuses `owner` on a plain job every purchase spends 0.003 ETH plus 0.5 IMD and gets onIntakeFailure(status 1); (b) access-control asymmetry: setTemplate(id,prefix,skill,host,enabled) only controls the prefix, skill and host, while the suffix with the owner key is a constant in bodyOf, so the contract owner cannot repair the body without a redeploy, contradicting the reference rule that every body or template is owner-settable and never a constant. Fix: drop the owner suffix from job bodies (the account already isolates projects) or move the whole closing part of the body into the Template so the owner can set a body with or without it.","line":294,"path":"src/WebsiteBuyer.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {WebsiteBuyer} from \"src/WebsiteBuyer.sol\";\nimport {IIntake} from \"src/interfaces/IIntake.sol\";\n\n/// @dev Minimal Intake stand-in: the hub only needs priceOf in the view path exercised here.\ncontract StubIntake is IIntake {\n    function priceOf(bytes32, address) external pure returns (uint256) {\n        return 0.5 ether;\n    }\n\n    function requestWithFailure(bytes32, bytes calldata, Callback calldata, bytes4, address, uint256)\n        external\n        payable\n        returns (bytes32)\n    {\n        return bytes32(uint256(1));\n    }\n}\n\ncontract StubToken {}\n\n/// @notice The job.open body must be the brief's / reference's checked template:\n///         {\"objective\":\"Build a one-page website: <words>\",\"skill\":\"build-website\",\"host\":true}\n///         The hub appends an immutable `\"owner\"` key that no template setting can remove, so the owner\n///         can never ship the canonical body if the plane refuses the extra key.\ncontract OwnerKeyInJobBodyTest is Test {\n    address internal owner = makeAddr(\"owner\");\n    address internal alice = makeAddr(\"alice\");\n    WebsiteBuyer internal buyer;\n\n    function setUp() public {\n        buyer = new WebsiteBuyer(owner, IIntake(address(new StubIntake())), address(new StubToken()));\n    }\n\n    function testDefaultJobBodyMatchesCanonicalTemplate() public view {\n        bytes memory canonical =\n            bytes('{\"objective\":\"Build a one-page website: a bakery in Lisbon\",\"skill\":\"build-website\",\"host\":true}');\n        assertEq(buyer.bodyOf(0, alice, \"a bakery in Lisbon\"), canonical);\n    }\n\n    function testOwnerCannotConfigureAwayTheOwnerKey() public {\n        // Even a template whose prefix/skill are freely set still ends with the hard-coded owner suffix.\n        vm.prank(owner);\n        buyer.setTemplate(7, \"Build a one-page website: \", \"build-website\", true, true);\n        bytes memory body = buyer.bodyOf(7, alice, \"x\");\n        bytes memory tail = bytes(',\"owner\":\"');\n        bool found;\n        for (uint256 i; i + tail.length <= body.length; ++i) {\n            bool match_ = true;\n            for (uint256 j; j < tail.length; ++j) {\n                if (body[i + j] != tail[j]) {\n                    match_ = false;\n                    break;\n                }\n            }\n            if (match_) {\n                found = true;\n                break;\n            }\n        }\n        assertFalse(found, \"job.open body carries a non-template owner key\");\n    }\n}","reproduction":"Deploy WebsiteBuyer(owner, intake, imd). Call bodyOf(0, alice, \"a bakery in Lisbon\"). Expected (brief and reference): {\"objective\":\"Build a one-page website: a bakery in Lisbon\",\"skill\":\"build-website\",\"host\":true}. Actual: the same bytes followed by ,\"owner\":\"0x3288...dac6\"} (see test output). Then as owner call setTemplate(7, \"Build a one-page website: \", \"build-website\", true, true) and bodyOf(7, alice, \"x\"): the owner key is still present; no template value removes it. Consequence if the plane refuses the key: buyWebsite{value: 0.003 ether}(words, purchase) succeeds on chain, the account pays 0.5 IMD to the treasury, and the writer later calls onIntakeFailure(id, 1, \"body_refused\"/\"unexpected_owner\", ...) for every purchase; the owner has no setter that stops it.","severity":"high","snippet":"            ',\"owner\":\"',\n            Strings.toHexString(user),\n            '\"}'","title":"job.open body carries a hard-coded \"owner\" key outside the owner-settable template; the canonical checked body can never be produced"},{"citation":"resolved","description":"onIntakeResult rejects any uri above 192 bytes. The Intake attempts the callback exactly once under 200,000 gas, so a revert means the result hash is never recorded on chain and the record stays State.Pending with no way to settle it (there is no retry, no owner settlement and no timeout). onIntakeFailure has no equivalent limit, so the two halves of the request/fulfil pair are asymmetric: a refusal is always recorded, a success with a long URI never is. The brief asks to store each request's result hash and URI and emit them; losing the hash because the URI is long defeats that. Fix: always store resultHash and set state Done; store the URI only when it fits and emit the full URI in the event (event data is not subject to the storage limit), or store keccak256(uri) when it is long.","line":311,"path":"src/WebsiteBuyer.sol","reproduction":"alice buys (id). Writer calls intake.complete(id, 0, keccak256(\"result\"), <193-byte uri>, 0). Expected: hash recorded, WebsiteResult emitted, state Done. Actual: callback reverts URITooLong, complete() returns delivered=false, requestOf(intake,id).state == Pending and resultHash == 0 permanently. The project's own test testOversizedURIIsNotStoredAndMissingCallbackNeverBlocksPurchases demonstrates the stranded Pending state.","severity":"low","snippet":"        if (bytes(uri).length > MAX_RESULT_URI_BYTES) revert URITooLong();","title":"Result callback reverts on a URI longer than 192 bytes, leaving the request Pending forever while the failure path has no such limit"},{"citation":"resolved","description":"Every purchase swaps the full 0.003 ETH, pays exactly the live price (0.5 IMD) to the Intake and leaves the rest in WebsiteBuyer. There is no owner sweep, no use of surplus to pay later requests (the code requires fresh output >= price) and no other transfer out, so the surplus is unrecoverable by anyone. Economics x asymmetry: the caller chooses minImdOut but only the contract's dead surplus is at stake above max(0.5 IMD, price), so callers have no incentive to set a tight bound and searchers can sandwich each purchase down to the 0.5 IMD floor. The brief does say extra stays in the contract, so this is reported as a design consequence for the owner to confirm rather than a bypass: if the surplus is meant to have any use, add an owner-only withdrawal of IMD above zero pending obligations (there are none, since nothing is escrowed).","line":27,"path":"src/WebsiteBuyer.sol","reproduction":"Owner configures a pool; alice calls buyWebsite{value: 0.003 ether}(\"a bakery in Lisbon\", Purchase(0, 0, 0.5e18, deadline, version)) when 0.003 ETH buys 1 IMD. Expected per brief: 0.5 IMD paid, 0.5 IMD kept. Actual matches, but afterwards no function in the ABI can move the 0.5 IMD (the project's testPurchasePaysExactLivePriceAndRetainsSurplus asserts imd.balanceOf(buyer)==0.5e18 and the owner has no transfer entry point). With minImdOut=0 a sandwich that moves the pool so the swap returns exactly 0.5 IMD still succeeds, and the 0.5 IMD difference goes to the searcher instead of the contract.","severity":"low","snippet":"/// @notice Each caller owns a distinct payer account. Excess IMD remains here; no funds wait on callbacks.","title":"Swap surplus IMD accumulates in WebsiteBuyer with no path out, and the user who sets the slippage bound does not bear the loss from sandwiching it"},{"citation":"resolved","description":"Documented privileged powers, not bypasses. setPool accepts any manager address with code and any hook/hookData, so the owner can point purchases at a contract that keeps the ETH settled to it; setIntake/setIMD can likewise name contracts the owner controls. Users are protected only by Purchase.configurationVersion: every setter bumps the version, so a purchase signed against the reviewed configuration reverts ConfigurationChanged if the owner changes anything first, and users' own IntakeAccount balances are never approved beyond the price the hub just sent. renounceOwnership (inherited, not overridden) permanently removes the ability to pause, re-point dependencies or disable templates. These are consistent with the brief's owner-settable design; frontends must display the version they quote.","line":393,"path":"src/WebsiteBuyer.sol","reproduction":"Owner calls setPool(maliciousManager, address(0), 3000, 60, IHooks(0), \"\") where maliciousManager implements unlock/swap/sync/settle/take returning the committed deltas and keeps the ETH. A user who submits buyWebsite with the new configurationVersion sends 0.003 ETH to that manager. A user holding the previous version reverts ConfigurationChanged and loses nothing. Owner calls renounceOwnership(): owner()==0 and every onlyOwner setter reverts thereafter.","severity":"info","snippet":"        poolManager = manager;\n        inputCurrency = input;","title":"Trust assumptions: owner can route users' ETH and IMD to arbitrary contracts via setPool/setIntake/setIMD; renounceOwnership is irreversible"}],"hash":"16b174707b0d4ff16e3772ef16c4e294e28e0aaf388a882b6e4404ae20b82da3","nodeId":"9058e987-a124-418e-9d60-50b02b5b9446","outcome":"completed","summary":"Review complete. The findings file is written and every snippet matches the tree at its cited line.\n\n**Outcome**: one high finding, two low, one info note, and a coverage row for all 17 entry points plus two invariants, in `.imd-findings.json`. All 44 existing tests pass. The proof test under `test/scratch/OwnerKeyInJobBody.t.sol` fails on the current code with the expected body mismatch.\n\n**Findings**\n\n- **High. Hard-coded `owner` key in every `job.open` body.** `bodyOf` appends `,\"owner\":\"<caller>\"` after the template parts, so the body differs from the brief's template and from the only `job.open` shape the intake reference shows as accepted. The reference reserves `owner` for launch kinds and says an unexpected key refuses the whole ask with the price spent. The per-user IntakeAccount already makes the project the user's, so the key buys nothing. The worse half is access-control asymmetry: `setTemplate` cannot reach that suffix, so if the plane refuses it, every purchase burns 0.003 ETH plus 0.5 IMD and the owner cannot repair it without a redeploy. The live checker could not be reached to settle the plane's answer, which the README also admits. Proof test attached.\n- **Low. Result callback reverts on a URI over 192 bytes.** The Intake calls once, so the record stays Pending forever with no hash stored. The failure path has no equivalent limit. The project's own test already shows the stranded state.\n- **Low. Surplus IMD is dead money and sandwich-exposed.** No entry point moves IMD out of the hub, and the caller who sets `minImdOut` has nothing at stake above the 0.5 IMD floor. Reported as a design consequence to confirm, since the brief says extra stays in the contract.\n- **Info. Trust assumptions.** The owner can point purchases at a malicious pool manager, intake or token. Users are protected by the configuration version binding, and user account balances are never approved beyond the price just sent. `renounceOwnership` is irreversible.\n\n**Coverage**: all setters, both callbacks, `unlockCallback`, the two-step ownership functions and the IntakeAccount entry points hold under the Access Control, Trust Gap and Asymmetry guides. Slither's reentrancy leads are blocked by `nonReentrant`, the swap phase commitment and balance checks. Aderyn's encodePacked lead is not a hash-identity use. Nothing was left unreached.","treeHash":null,"usage":{"cachedInputTokens":972408,"inputTokens":322,"model":"claude-fable-5-1","outputTokens":28475,"runtime":"claude","turns":26,"wallClockMs":435343}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"a7979f925df7a720","findings":[{"citation":"resolved","description":"bodyOf always builds `{\"objective\":\"<prefix><words>\",\"skill\":\"<skill>\",\"host\":<bool>,\"owner\":\"<caller>\"}`. A Template only holds prefix, skill, host and enabled. The key set (`skill`, `host`, `owner`) and its order are compiled in. JsonText forbids `\"` in prefix and skill, so no setter can add, drop or rename a key. The brief's own template is `{\"objective\":\"Build a one-page website: <words>\",\"skill\":\"build-website\",\"host\":true}`, with no owner. The intake reference describes job.open as a strict CreateJobBody where an unknown key refuses the whole ask, and the price is spent. Every documented job.open body omits `owner`; it names `owner` only for univ4_hook, evm_project and evm_contracts launch bodies, and custom_token refuses it with `unexpected_owner`. The README says the owner key does not decide project access: the per-user payer does. So the key adds no protection and carries refusal risk. If the plane refuses it, or a later action version (setAction(job-2)) changes the schema, every buyWebsite spends the user's 0.003 ETH on a status-1 refusal. The owner's only remedy is to pause and redeploy, which breaks the brief's and the reference's rule that templates/bodies are owner-settable.","line":294,"path":"src/WebsiteBuyer.sol","reproduction":"1) The owner tries to make template 0 equal the brief's exact body: setTemplate(0, \"Build a one-page website: \", \"build-website\", true, true). 2) bodyOf(0, 0x00000000000000000000000000000000000000aa, \"a bakery\") returns `{\"objective\":\"Build a one-page website: a bakery\",\"skill\":\"build-website\",\"host\":true,\"owner\":\"0x00000000000000000000000000000000000000aa\"}`. Expected: some owner configuration yields the brief's body without `owner`, or with keys the plane requires such as `template`/`steps`/`deliver`. Actual: no call sequence removes `owner` or changes the keys; setTemplate(0, \"x\\\",\\\"template\\\":\\\"single\", ...) reverts InvalidText. If POST /requests/check (action job.open) refuses the owner key, every buyWebsite(words, purchase) with 0.003 ETH reaches onIntakeFailure(status 1) and the IMD price is spent, with no fix short of redeploying. Not checked against the live checker: this review had no endpoint host.","severity":"medium","snippet":"            ',\"owner\":\"',\n            Strings.toHexString(user),\n            '\"}'","title":"Body keys are hard-coded: the owner cannot remove the extra \"owner\" key or match the brief's body, so a schema refusal cannot be fixed by any setter"},{"citation":"resolved","description":"The brief says to store each request's result hash and URI and emit them. The Intake attempts the callback only once. A URI one byte over the cap makes onIntakeResult revert, so the 32-byte resultHash (cheap to store) is lost too, no WebsiteResult event is emitted, and the request stays Pending forever with no way to settle it. The cap exists for gas: the 192-byte case already uses 193,266 of the 200,000 stipend. But the fix does not need a revert. Store resultHash and state, always emit the full URI (about 8 gas per byte in the log), and store the URI only when it fits.","line":311,"path":"src/WebsiteBuyer.sol","reproduction":"buyWebsite succeeds -> requestId R. The writer completes R with status 0, resultHash H, and a 193-byte uri (for example a plane fallback `https://<host>/intake-results/<id>` URL with a longer host or path). MockIntake.complete returns delivered=false, as in testOversizedURIIsNotStoredAndMissingCallbackNeverBlocksPurchases. requestOf(intake,R): state=Pending, resultHash=0, uri=\"\"; no WebsiteResult event. Expected: H stored and the URI emitted (and stored if it fits). Actual: nothing recorded on chain, permanently.","severity":"low","snippet":"        if (bytes(uri).length > MAX_RESULT_URI_BYTES) revert URITooLong();","title":"A result URI over 192 bytes reverts the whole callback, so neither the result hash nor the URI is stored or emitted"},{"citation":"resolved","description":"Each purchase swaps the whole ethPrice and forwards exactly `price`; the rest of the output stays in WebsiteBuyer. The brief asks for this ('extra stays in the contract'). But no function ever spends, sweeps or credits that balance, even for the owner. After setIMD, the old token balance is stranded too. This is reported as a design question for the requester, not a bypass.","line":194,"path":"src/WebsiteBuyer.sol","reproduction":"Swap output 0.6 IMD with Intake price 0.5 IMD -> 0.1 IMD remains in WebsiteBuyer per purchase (testPurchasePaysExactLivePriceAndRetainsSurplus asserts the retained 0.5 IMD). No entry point in the ABI can move it. Expected (if the requester wants the surplus usable): an owner sweep. Actual: locked forever.","severity":"info","snippet":"        token.safeTransfer(address(account), price);","title":"Surplus IMD (and anything left after setIMD) has no exit path"}],"hash":"22b0ee990e94a6dadab7b6679f45e7fd56d2e7c1559ff1d8283dbf73d8d86ac1","nodeId":"c60e5f9d-0960-4f4b-9c2c-1fbc9f8e3735","outcome":"completed","summary":"I found three defects: one medium, one low and one informational. None is high or critical, so none needed a proof test. The results are in `.imd-findings.json`, with a coverage row for each of the 17 entry points plus two for checks I made of the token-balance invariant and the `JsonText` library. I changed no repository files, and the existing suite passes (44 of 44).\n\n**Findings**\n1. **Medium, `src/WebsiteBuyer.sol:294`.** `bodyOf` always adds `\"owner\":\"<caller>\"` to the body, and the set of keys is fixed in the code. A template only holds the prefix, skill, host flag and enabled flag. The text checks reject `\"`, so no setter can drop the owner key or change the keys. As a result, the owner can't produce the brief's exact body `{\"objective\":\"Build a one-page website: <words>\",\"skill\":\"build-website\",\"host\":true}`.\n   - **Why it matters:** the protocol reference never puts `owner` in a `job.open` body, and it refuses `owner` for `custom_token` (`unexpected_owner`). If the plane refuses it here, or a later action version changes the schema, every purchase spends the user's 0.003 ETH on a refusal. The only remedy is to pause and redeploy.\n   - **Not confirmed:** I couldn't run the body through the live checker (`POST /requests/check`) because nothing I was given names its host. Whether the plane actually refuses the key is still open.\n2. **Low, `src/WebsiteBuyer.sol:311`.** A result URI longer than 192 bytes makes `onIntakeResult` revert. The Intake calls back only once, so the result hash is lost along with the URI, no event is emitted, and the request stays Pending forever. A fix that fits the 200,000 gas limit: always store the hash and emit the full URI, and store the URI only when it fits.\n3. **Info, `src/WebsiteBuyer.sol:194`.** The leftover IMD stays in the contract as the brief asks, but nothing can ever move it, not even the owner. After `setIMD`, the old token's balance is stuck too. This is a design question for the requester.\n\n**What holds**\n- **Swap path (`_buyIMD` → `unlockCallback`):** the callback only runs inside the contract's own swap, the whole ETH payment must be swapped, and settlement works for both native ETH and wrapped ETH. The output must be at least the largest of 0.5 IMD, the live Intake price and the caller's minimum.\n- **Configuration version:** it is bumped by every setter, so a purchase built on old settings reverts.\n- **Payments:** each user pays through their own account, the contract's IMD balance drops by exactly the price, and no approval is left behind.\n- **Callbacks:** they are tracked per Intake and settle only once, and changing the Intake doesn't break callbacks still pending from the old one.\n- **Text checks:** `JsonText` rejects quotes, backslashes, control bytes and malformed UTF-8.\n- **Callback gas:** a 192-byte URI uses 193,266 of the 200,000 gas limit, so it fits.\n\nThe static-analysis reentrancy leads don't hold up: the guards and the swap-phase check block them.\n\n**Not covered:** I didn't check any live mainnet pool, hook or token behaviour, and I didn't run the live body checker.","treeHash":null,"usage":{"cachedInputTokens":986548,"inputTokens":28,"model":"claude-opus-5-5","outputTokens":21140,"runtime":"claude","turns":18,"wallClockMs":272144}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"934ef01306a3cc89","findings":[{"citation":"resolved","description":"bodyOf always emits {\"objective\":\"<prefix><words>\",\"skill\":\"<skill>\",\"host\":<bool>,\"owner\":\"<caller>\"}. A Template holds only objectivePrefix, skill, host and enabled, and JsonText.check forbids a quote in prefix and skill, so no owner configuration can add, drop or rename a key. The brief's template and the intake-client reference's own job.open template (REFERENCE.md line 388: '{\"objective\":\"Build a one-page website: ', '\",\"skill\":\"build-website\",\"host\":true}') carry no owner key; the reference says to set owner only where a launch kind takes one and that an unknown key refuses the whole ask with status 1 while the 0.5 IMD is already spent. The reference also says every body or template must be owner-settable and that a new action version must not need a redeploy, yet setAction(job-2) cannot be paired with any body shape other than this one. Project isolation already comes from the per-user IntakeAccount payer, so the key adds no protection. Admission of the owner key on job.open is unverified: README line 88 records that the checker was not reachable, and POST /requests/check at identity.md, api.identity.md and plane.identity.md returned 404 or no answer during this review. If the plane refuses it, every buyWebsite spends the user's 0.003 ETH on a status-1 refusal and only a redeploy fixes it. Merged from audit_flow, audit_permissions and audit_economics (same root cause). Fix that keeps the design: make the owner slot part of the template (e.g. a bool includeOwner, or owner-set JSON parts around a caller slot as in the reference hub), so the default can carry owner while the owner can remove it or change the key set without redeploying.","line":294,"path":"src/WebsiteBuyer.sol","reproduction":"Deploy WebsiteBuyer(owner, intake, imd). bodyOf(0, 0x328809bc894f92807417d2dad6b7c998c1afdac6, \"a bakery in Lisbon\") returns {\"objective\":\"Build a one-page website: a bakery in Lisbon\",\"skill\":\"build-website\",\"host\":true,\"owner\":\"0x328809bc894f92807417d2dad6b7c998c1afdac6\"} (ran the specialist proof test/scratch/Proof_78681c60834c.t.sol: both tests fail with exactly these bytes). Expected: some owner call sequence yields the reference's body {\"objective\":\"Build a one-page website: a bakery in Lisbon\",\"skill\":\"build-website\",\"host\":true}. Actual: after setTemplate(7, \"Build a one-page website: \", \"build-website\", true, true), bodyOf(7, alice, \"x\") still ends with ,\"owner\":\"0x...\"}; setTemplate(0, 'x\",\"template\":\"single', ...) reverts InvalidText. Consequence if the plane refuses the key: buyWebsite{value: 0.003 ether}(words, Purchase(0,0,0.5e18,deadline,version)) succeeds on chain, the account pays 0.5 IMD to the treasury, and onIntakeFailure(id, 1, reason, ...) arrives for every purchase with no setter that stops it.","severity":"medium","snippet":"            ',\"owner\":\"',","title":"job.open body key set is compiled in: the owner cannot remove the extra \"owner\" key or send the reference's canonical body, so a schema refusal cannot be fixed by any setter"},{"citation":"resolved","description":"The brief says the extra IMD above the Intake price stays in the contract, so that surplus is the contract's margin. The only lower bound on the swap output is max(purchase.minImdOut, MIN_IMD_OUT, price). MIN_IMD_OUT is a compile-time constant, there is no owner-settable floor or rate, and the sqrtPriceLimit is the extreme bound, so the only variable protection is minImdOut, which the buyer picks. The buyer gets the same website whether the swap returns 0.5 IMD or 3 IMD, so a frontend default of 0 is rational for them. A mempool observer can push the pool so 0.003 ETH yields just over 0.5 IMD, let the purchase execute, and unwind. The buyer is unharmed and the contract's margin goes to the searcher. Profitability depends on the pool: reproduced as profitable on a 0.05% pool and unprofitable on a 0.3% pool at the same depth, so this is a loss under specific conditions. The owner's only lever is setETHPrice, which cannot raise the floor. Merged from audit_math (medium) and the sandwich part of audit_permissions' surplus finding. Fix that keeps the design: an owner-settable minimum output (or IMD-per-wei rate) folded into the same max() and bumping configurationVersion.","line":174,"path":"src/WebsiteBuyer.sol","reproduction":"Scratch test test/scratch/Sandwich.t.sol against the vendored v4 PoolManager: native ETH/IMD pool, fee 500, tickSpacing 10, initialized at tick 69080 (~1000 IMD per ETH), full-range liquidity 30e18; WebsiteBuyer with setPool(manager, address(0), 500, 10, IHooks(0), \"\"), MockIntake price 0.5 IMD. Honest: user buyWebsite{value: 0.003 ether}(\"words\", Purchase(0, 0, 0.5e18, now+120, version)) leaves imd.balanceOf(buyer) = 2488752135237902348. Attack: (1) attacker exact-input swaps 1.3 ETH for IMD; (2) the same purchase executes without revert because 0.533 IMD >= 0.5 IMD; imd.balanceOf(buyer) = 33265831163937929; (3) attacker sells all IMD back and ends +1540317967698942 wei ETH. Expected per brief: ~2.49 IMD extra stays in the contract. Actual: 98.7% of it is paid to the sandwicher. Same scenario with fee 3000 / tickSpacing 60: attacker pnl -3090842912298899 wei, so the loss is conditional on pool fee and depth.","severity":"medium","snippet":"        uint256 minimum = purchase.minImdOut;\n        if (minimum < MIN_IMD_OUT) minimum = MIN_IMD_OUT;\n        if (minimum < price) minimum = price;","title":"Swap surplus is guarded only by the constant 0.5 IMD floor and a minimum chosen by the buyer, who has no stake in it; on a low-fee pool a sandwich takes almost the whole margin"},{"citation":"resolved","description":"The Intake attempts the callback once under 200,000 gas. onIntakeResult reverts on any uri over 192 bytes, so the 32-byte resultHash (cheap to store) is never recorded, no WebsiteResult event is emitted, and the record stays State.Pending with no retry, timeout or owner settlement, while onIntakeFailure has no such limit. The brief asks to store each request's result hash and URI and emit them. The cap exists for gas (storing a 192-byte string is the bulk of the stipend) but the revert is not needed: store resultHash and set Done always, emit the full URI (log data is cheap), and store the URI only when it fits. Expected URIs (ipfs://<CIDv1>/result.json is about 78 bytes) fit today, so impact is bounded. Merged from audit_flow and audit_permissions.","line":311,"path":"src/WebsiteBuyer.sol","reproduction":"alice buys (request id R). Writer calls intake.complete(R, 0, keccak256(\"result\"), <193-byte uri>, 0). Expected: resultHash recorded, WebsiteResult emitted, state Done. Actual: callback reverts URITooLong, complete returns delivered=false, requestOf(intake, R).state == Pending and resultHash == 0 permanently. The project's own test testOversizedURIIsNotStoredAndMissingCallbackNeverBlocksPurchases (test/WebsiteBuyer.t.sol line 196) asserts exactly this stranded state.","severity":"low","snippet":"        if (bytes(uri).length > MAX_RESULT_URI_BYTES) revert URITooLong();","title":"A result URI longer than 192 bytes reverts the whole callback, so the result hash is lost too and the request stays Pending forever"},{"citation":"resolved","description":"The only IMD outflow is the exact price sent to the user's account; the input currency goes to the pool manager. Fresh output must cover the price, so the retained balance is never spent, there is no owner sweep, and after setIMD the old token's balance is unreferenced. The brief says the extra stays in the contract and README line 84 states the lock is deliberate, so this is reported for the requester to confirm rather than as a bypass: if the extra is meant to be usable, an onlyOwner sweep is missing. It also sets the stakes of finding 2: today a sandwich takes value nobody could otherwise recover; with a sweep it becomes direct owner revenue loss. Merged from audit_flow, audit_permissions, audit_economics and audit_math.","line":194,"path":"src/WebsiteBuyer.sol","reproduction":"With MockPoolManager returning 1 IMD per purchase and Intake price 0.5 IMD, alice calls buyWebsite{value: 0.003 ether} three times: imd.balanceOf(buyer) == 1.5e18. Owner calls setIMD(newToken): the 1.5e18 old IMD remains at WebsiteBuyer. No function in the ABI (setters, buyWebsite, callbacks, unlockCallback, ownership) transfers it. Expected (one reading of the brief): the operator can recover or reuse the extra. Actual: locked permanently.","severity":"info","snippet":"        token.safeTransfer(address(account), price);","title":"Retained surplus IMD, and any balance left after setIMD, can never leave WebsiteBuyer: design confirmation needed"},{"citation":"resolved","description":"Documented privileged powers, not bypasses. setPool accepts any manager address and any hook/hookData, setIntake and setIMD any nonzero address, so the owner can point purchases at contracts the owner controls. Every setter bumps configurationVersion and buyWebsite reverts ConfigurationChanged for a stale version, so a user who reviewed the configuration cannot be silently redirected, and user IntakeAccounts are never approved beyond the price just funded. renounceOwnership is inherited and not overridden; it permanently removes pause, re-pointing and template recovery. Consistent with the brief's owner-settable design; frontends must display the version they quote. From audit_permissions.","line":393,"path":"src/WebsiteBuyer.sol","reproduction":"Owner calls setPool(maliciousManager, address(0), 3000, 60, IHooks(0), \"\") where maliciousManager implements unlock/swap/sync/settle/take and keeps the ETH. A user who submits buyWebsite with the new configurationVersion sends 0.003 ETH to it; a user holding the previous version reverts ConfigurationChanged and loses nothing. Owner calls renounceOwnership(): owner() == 0 and every onlyOwner setter reverts thereafter.","severity":"info","snippet":"        poolManager = manager;","title":"Trust assumptions: the owner can route purchases to any manager, Intake or token; users are protected only by configurationVersion; renounceOwnership is irreversible"}],"hash":"23d0b8837a116a040ca188c3030e94fbdb7f23d1dd0bd9c5ccf847fa7b394d95","nodeId":"47395c60-cb46-4a46-b2a8-a5d777b414d4","outcome":"completed","summary":"Review complete. The findings file is written at `.imd-findings.json` with five findings and a 19-row coverage record. No repository file was changed. The scratch tests were removed.\n\n**What I kept, after reproducing each against the code**\n\n- **Medium, hard-coded body keys.** The `owner` key is appended by `bodyOf` outside the template, and no setter can remove it or change the key set. I ran the specialist's proof and it fails with the exact bytes they quoted. The reference's own `job.open` hub template carries no owner key. Admission of that key stays unverified since the public checker returned 404 from three hosts. Three specialist reports merged into this one.\n- **Medium, margin extractable by sandwich.** I reproduced the attack on the vendored v4 PoolManager with a 0.05% pool. The hub's surplus fell from about 2.49 IMD to 0.03 IMD and the attacker netted ETH. With a 0.3% pool at the same depth the attacker loses, so the loss is conditional on pool fee and depth. The only floor is a compile-time constant plus a buyer-chosen minimum.\n- **Low, result URI over 192 bytes reverts the callback.** The hash is lost and the request stays Pending forever. The project's own test asserts this state. Two reports merged.\n- **Info, surplus IMD has no exit path.** The brief says the extra stays in the contract, so this is a design confirmation for the requester. Four reports merged.\n- **Info, owner trust assumptions.** Documented powers, not bypasses. Users are protected by the configuration version check.\n\n**What I checked myself beyond the specialists**\n\nThe swap callback guards, settlement order, delta checks, callback namespacing, the per-user account isolation, JSON text validation, contract sizes, and the manifest all hold. The full suite passes with 77 tests. The static analysis leads were either covered by the findings above or did not reproduce.\n\n**Open item for the author.** Before sales, run the exact `bodyOf` output through the protocol's real request checker. That answers whether finding 1 is a latent total loss per sale or only a configurability gap.","treeHash":null,"usage":{"cachedInputTokens":767979,"inputTokens":258,"model":"claude-fable-5-1","outputTokens":27267,"runtime":"claude","turns":23,"wallClockMs":458508}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"2dc755dfe7bd177c","findings":[{"citation":"resolved","description":"Boundary x economics seam. The brief says the caller pays a fixed 0.003 ETH, the contract buys IMD and 'extra stays in the contract'. The only lower bound on the swap output is max(purchase.minImdOut, MIN_IMD_OUT = 0.5 IMD, live price). MIN_IMD_OUT is a compile-time constant and there is no owner-settable floor or rate, so the only variable protection is purchase.minImdOut, which the buyer supplies. The buyer receives the same website whether the swap yields 0.5 IMD or 3 IMD, so they have zero incentive to set it above zero; a front-end default of 0 is rational for them. Everything above max(0.5 IMD, price) is therefore extractable by any mempool observer: push the pool so that 0.003 ETH yields just over 0.5 IMD, let the purchase execute, unwind. The user is unharmed, the attacker profits, and the contract (whose margin this is by the brief) receives near zero. The sqrtPriceLimit is MIN_SQRT_PRICE+1 / MAX_SQRT_PRICE-1, so nothing else bounds the price the purchase trades at. README.md line 86 acknowledges mempool ordering but relies on 'a tight user-approved minImdOut', which is exactly the control held by the uninterested party. Profitability depends on pool depth and fee: attacker cost is fee x 2 x volume needed to move the price; with a 0.05% pool and ~30e18 liquidity units the attack nets a profit (numbers below); with a 0.3% pool at the same depth the fees exceed the surplus. The owner cannot change this after deployment except by lowering ethPrice to shave the margin, which is the opposite of the brief's intent. Fix that preserves the design: add an owner-settable minimum output (e.g. minImdOutFloor, or a minimum IMD-per-wei rate) that is folded into the same max() and bumps configurationVersion, so the floor tracks the market without a redeploy; alternatively bound the executed price with a sqrtPriceLimit derived from an owner-set rate.","line":174,"path":"src/WebsiteBuyer.sol","reproduction":"Real Uniswap v4 PoolManager (lib/v4-core), native ETH / IMD pool, fee 500 (0.05%), tickSpacing 10, initialized at tick 69080 (~1000 IMD per ETH), full-range liquidity 30e18 units. WebsiteBuyer(owner, MockIntake, IMD) with setPool(manager, address(0), 500, 10, IHooks(0), ''), Intake price 0.5 IMD, ethPrice default 0.003 ETH. Honest baseline: user calls buyWebsite{value: 0.003 ether}('words', Purchase(0, minImdOut=0, maxIntakePrice=0.5e18, deadline, version)) -> hub surplus imd.balanceOf(buyer) = 2488752135237902348 (2.4888 IMD bought - 0.5 paid). Attack: (1) attacker exact-input swaps 1.3 ETH for IMD in the same pool (zeroForOne); (2) the same user purchase executes: it does NOT revert because output 0.5333 IMD >= max(0, 0.5e18, 0.5e18); hub surplus is now 33265831163937929 (0.0333 IMD); the user still gets their job opened at the same 0.003 ETH; (3) attacker sells all IMD received back: attacker ETH balance is +1540317967698942 wei (+0.00154 ETH, about 1.54 IMD at pool price) after paying 0.05% twice. Expected per the brief: the ~2.49 IMD excess stays in the contract. Actual: 98.7% of it is paid to the sandwicher. Pushing 1.5 ETH instead overshoots and the purchase reverts with InsufficientOutput(449778638281122361, 500000000000000000), which shows the 0.5 IMD constant is the exact and only line the attacker must stay above. Scratch test used: test/scratch/Margin.t.sol::testSurplusSandwichOnRealPool (a Trader helper that seeds liquidity and swaps exact-input through PoolManager.unlock).","severity":"medium","snippet":"        uint256 minimum = purchase.minImdOut;\n        if (minimum < MIN_IMD_OUT) minimum = MIN_IMD_OUT;\n        if (minimum < price) minimum = price;\n        uint256 bought = _buyIMD(minimum);","title":"Swap surplus is protected only by a constant 0.5 IMD floor and a minimum chosen by a party with no stake, so a sandwich extracts the contract's margin on every purchase"},{"citation":"resolved","description":"The brief says 'extra stays in the contract'. The implementation reads that as 'is never recoverable': there is no owner withdrawal, and _open always transfers exactly `price` from freshly bought IMD (bought >= price is enforced), so the hub's running balance only grows. After setIMD(next) the old token's balance is no longer referenced by any code path. README.md line 84 states this is deliberate. Reported as information only, because it is a scope decision for the requester: if 'stays in the contract' meant 'is retained for the operator rather than refunded', an owner sweep (or using surplus toward future prices) is missing; if it meant 'is burned in place', the code matches. Not a math defect.","line":27,"path":"src/WebsiteBuyer.sol","reproduction":"Mock suite state after two purchases at 1 IMD out each (test/WebsiteBuyer.t.sol setUp, MockPoolManager amountOut = 1 ether, price 0.5 IMD): imd.balanceOf(buyer) == 1 IMD. There is no function in the WebsiteBuyer ABI (see entry-point list) that transfers IMD out, and after owner calls setIMD(otherToken) followed by setPool(...), the 1 IMD under the old address is unreachable by any call. Expected (one reading of the brief): operator can retrieve or reuse the excess. Actual: value is permanently locked.","severity":"info","snippet":"/// @notice Each caller owns a distinct payer account. Excess IMD remains here; no funds wait on callbacks.","title":"Accumulated surplus IMD can never leave or be used: no sweep, and setIMD orphans it permanently"}],"hash":"2905aa811dcf1ac767eb9a8f5f37819206bb6b1b0565b87d5608e5f1febcb8f3","nodeId":"59946899-9b3a-4e31-ba6a-19b6c3e536ab","outcome":"completed","summary":"Review complete. The findings file is written and validated, and the working tree is untouched apart from the findings file and my scratch tests (gitignored, not kept).\n\n**What I covered**\n\nI audited the math against the Pashov Math Precision, Boundary and Numerical Gap guides. The contract has no division, scaling or decimal conversion: the only arithmetic is balance deltas with equality checks on every leg. I traced each entry point's bounds (ETH price capped at int128.max for the v4 swap amount, tick spacing and fee ranges matching v4, template and word limits against the 8000-character objective and 16 KiB body caps, UTF-8 validation at every multibyte edge), the exact-input delta equality in `unlockCallback`, and the sync-then-settle ordering against the real PoolManager. All hold.\n\n**The gas boundary that looked like a defect but isn't**\n\nThe 200k callback stipend at the maximum 192-byte URI is tight. My first measurement read 219k, but a genuinely cold transaction (purchase in `setUp`, writer's `complete` as the test body) costs 193,266 gas, matching the suite's own number. The margin is 3.4%, so the stated maximum holds today.\n\n**Findings reported**\n\n1. **Medium, `buyWebsite` slippage floor.** The swap output floor is max(caller's `minImdOut`, constant 0.5 IMD, live price). The caller gets the same website regardless of how much IMD is bought, so they have no reason to set `minImdOut`, and the owner has no setter to raise the floor. On the real v4 PoolManager with a 0.05% pool, a 1.3 ETH front-run cut the contract's surplus from 2.489 IMD to 0.033 IMD and netted the attacker 0.00154 ETH after fees. The README acknowledges sandwiching but delegates protection to the uninterested party. Proposed fix keeps the design: an owner-settable output floor or rate folded into the same max().\n2. **Info, surplus is unrecoverable.** No sweep exists and `setIMD` orphans the old balance. Documented as deliberate, flagged only because the brief's \"extra stays in the contract\" could mean retained rather than locked forever.\n\n**Coverage**\n\nAll 17 listed entry points have rows (16 hold, `buyWebsite` carries finding 1), plus two invariant rows. Nothing was left unreached.","treeHash":null,"usage":{"cachedInputTokens":1322595,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":42544,"runtime":"claude","turns":28,"wallClockMs":656428}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"bcb8a3f211778fd6","findings":[],"hash":"483c6fa317d19f8b77e70402fda53200fb30404163c5b427e8b5da61c43d5d28","nodeId":"47395c60-cb46-4a46-b2a8-a5d777b414d4","outcome":"completed","summary":"All three fixes hold and no new defect reproduced. The findings file reports an empty list with full coverage.\n\n**Settled findings**\n\n- **Owner key compiled in (medium): fixed.** A template is now owner JSON parts around Words and Caller slots. My scratch test confirmed one setTemplate call yields the reference body byte-for-byte without the owner key, the key set can be reshaped, and words with a quote still revert. Default template 0 keeps the caller as owner per the brief. Live checker admission of that key remains unverified and the README says so.\n- **Surplus sandwich (medium): fixed.** I replayed the 0.05% pool scenario on the vendored PoolManager. With the deploy-time floor the 1.3 ETH front-run still extracts the surplus, which is expected since no pool exists at deploy. With the floor at 2.36 IMD the sandwiched purchase reverts, the searcher ends below their starting ETH, and the honest purchase clears. Residual loss is bounded by the owner's tolerance and documented.\n- **Long URI strands the request (low): fixed.** The callback always stores the hash and sets Done, stores the URI only when it fits, and emits the full URI. 193 and 4096 byte URIs settle under the 200,000-gas stipend.\n- **Locked surplus and owner trust assumptions (info, disputed):** both reproduce as described and match the brief's stated design. The README documents them. No finding.\n\n**Fresh pass on the changed code.** I checked template bounds against the Intake's 16 KiB limit, reentrancy through hooks and the Intake during the swap and ask, callback namespacing, the manifest shape, and contract sizes. Nothing reproducible. Full suite passes (83 tests). Scratch files were removed and the tree is unchanged apart from the findings file.","treeHash":null,"usage":{"cachedInputTokens":1186690,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":15634,"runtime":"claude","turns":24,"wallClockMs":370540}},{"artifacts":[],"attempt":1,"bundleHash":"3ccad5642c64f8b6605ad24ffde83a3755204492ca6fc1ebfd33f9ba1ddbc961","device":"476778ae63dff113","findings":[],"hash":"4eb2502fb52e5bfed15e6fd392d393a4ab9a972531cd0695c0b62195a0af10aa","nodeId":"9747bd13-ccc2-496d-bc07-a4b68d37fd8b","outcome":"completed","summary":"I updated the test suite for the implementation's new template format, and `forge test` now passes with the verifier's fuzz seed: 83 tests in 8 suites, none failing. That includes both invariant tests, which ran 256 random call sequences of 16,384 calls with no reverts.\n\nThe build had broken because `src/WebsiteBuyer.sol` changed after my last round. Templates used to be set as a prefix, a skill and a host flag. They are now a list of JSON text pieces with gaps between them, each gap filled by either the caller's words or the caller's address.\n\nChanges:\n- **`test/helpers/SiteTemplate.sol` (new):** a small helper that builds the brief's website template in the new format. Both the edge tests and the invariant handler use it.\n- **`test/WebsiteBuyerEdges.t.sol`:**\n  - The test that changes every setting and checks an earlier quote is refused now sets the template the new way.\n  - The template-limits test now checks the new rejection error, `InvalidTemplate`. It covers a template with no gap for the caller's words, one over the 8,192-byte limit, and one whose piece and gap counts don't match. Each rejection leaves the stored template and the configuration version unchanged.\n  - The maximum-length test fills the template close to the 8,192-byte limit and adds 2,000 bytes of words. The body still parses as JSON with the user's address as owner and stays under 16 KiB.\n- **`test/invariant/WebsiteBuyerHandler.sol`:** the random sequences switch the template's host flag and enabled state through the new format. The invariant now compares the stored template's text instead of a `host` field that no longer exists.\n\nI found no new defect in the changed code, so I did not write a `.imd-findings.json`. I left `test/README.md` as it was.","treeHash":"d99d760534c1d92aaebe69f85d88d22dbc56ec74","usage":{"cachedInputTokens":284261,"inputTokens":14,"model":"claude-opus-5-5","outputTokens":3957,"runtime":"claude","turns":7,"wallClockMs":157070}},{"artifacts":[],"attempt":1,"bundleHash":"b0a7943283fbade16adb6b8448584fab563858b6cf7f58929d0790003ad7bc25","device":"7df8c87bdf5d7822","findings":[],"hash":"560004ea420abed1525f30871f92e19de5367af8efa518ba6c7eba5b59e87939","nodeId":"37d220b4-576d-4933-8f68-d0f132490e25","outcome":"completed","summary":"Implemented ETH-to-IMD swaps, per-user payer accounts, owner-settable configuration/templates, and authenticated result storage and events.\n\nVerified with Solidity 0.8.26:\n\n- `forge build` passed.\n- `forge test` passed: 44 tests, using the supplied fuzz seed.\n- `forge fmt --check` passed.\n\nDeployment and operational details are in [README.md](/root/.identitymd/work/a07b01eb-c2ad-4a47-9925-8e287ec6b0dc/37d220b4-576d-4933-8f68-d0f132490e25/README.md). Before sales, configure the actual pool and validate the body with the live Intake checker; public checker paths returned HTTP 404.","treeHash":"03360a54180dcdc4ccd25c726c57eb29a96c4cc0","usage":{"cachedInputTokens":2730752,"inputTokens":136298,"model":"gpt-6-astra","outputTokens":45862,"runtime":"codex","turns":7,"wallClockMs":1018050}},{"artifacts":[],"attempt":1,"bundleHash":"90de7d059e75671b6a727a810acd37f375edad216a8e32330aa3e34df49de7e2","device":"382ebcfad965e0cd","findings":[],"hash":"59f11f0b7294d56aed393a13021ad844ddf5b01483aca865c7e078ba4e9dca7a","nodeId":"abf1591c-175a-4697-bce6-478d99c7213a","outcome":"completed","summary":"I left `launch.json` unchanged: it already matches the accepted code, and this round came with no findings to fix. `forge build` succeeded and `forge test` passed all 50 tests.\n\nWhat I checked against the code:\n- **One contract.** `WebsiteBuyer` is the only contract the manifest lists. Its constructor is `(owner_, intake_, imd_)` at `src/WebsiteBuyer.sol:153`, and the manifest fills it with `$owner`, the Intake v2 address `0xa43e6f75ee006411f79ac1c84120606c2330de82`, and the IMD token `0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7`. That token address is the same one the existing deployment test uses.\n- **`IntakeAccount` is not listed, which is correct.** `WebsiteBuyer` creates one for each user on their first purchase, so it isn't a separate deployment.\n- **Starting settings match the brief.** The price starts at 0.003 ETH and the job is `job.open@job-1`. The website body template is built in the constructor and adds the buyer's address as the site's owner.\n- **Notes are accurate.** They say purchases will fail until the owner sets up a Uniswap v4 ETH/IMD pool, since the contract starts with none. They also say the local tests haven't checked live pool liquidity or whether the Intake accepts the request body.","treeHash":"05f35655b8b74c63fe2c5ff1eefbdc999119e186","usage":{"cachedInputTokens":85914,"inputTokens":6,"model":"claude-opus-5-5","outputTokens":907,"runtime":"claude","turns":3,"wallClockMs":39560}},{"artifacts":[],"attempt":1,"bundleHash":"2c47972d3fdd5ae962bcfc1cf00e732a8570334007ee5934bb7c79bc2269c25a","device":"bf4c5b8707945096","findings":[],"hash":"5dece06b828104f6e4e135a93d1b6353fe34b76281cec128738e2a673692e5d9","nodeId":"37d220b4-576d-4933-8f68-d0f132490e25","outcome":"completed","summary":"I fixed the three code findings and answered all five in `.imd-responses.json`. `forge build`, `forge fmt --check` and `forge test` (with the verifier's fuzz seed) all pass: 50 tests, up from 44. Runtime size is 17,925 bytes, under the 24,576 limit.\n\nI reproduced every finding before changing anything, and the numbers matched the reviewer's exactly.\n\n**Fixed:**\n- **Body key set was fixed in the code (medium).** A template is now the owner's JSON pieces with gaps, as in the reference hub. Each gap is filled with either the caller's words or the caller's address.\n  - The default body still includes `\"owner\"`, because the brief asks for the owner to be passed.\n  - The owner can switch to the reference body without `owner` with one `setTemplate` call. A test confirms those exact bytes reach the Intake, still paid from the caller's own account.\n  - Templates must have exactly one words gap, at most 8 gaps and 8,192 bytes, which keeps every body under the Intake's 16 KiB limit. Words are still rejected if they contain a quote, a backslash or a control character, so they can't add keys.\n- **Sandwich on the retained IMD (medium).** I added an owner-set minimum IMD per purchase, `imdFloor`. It starts at 0.5 IMD and can't go lower, and changing it bumps the configuration version. Purchases now require at least the largest of the buyer's minimum, `imdFloor` and the live Intake price. A new test replays the reviewer's attack on a 0.05% pool with the floor at 2.36 IMD: the sandwiched purchase reverts, and the attacker ends with less ETH than they started with.\n- **Long result URIs left requests stuck (low).** The result callback now always marks the request done and stores the result hash. A URI longer than 192 bytes isn't stored but is emitted in full in the event; a 4,096-byte URI is tested within the Intake's 200,000-gas callback limit.\n\n**Disputed, with no code change:**\n- **Surplus IMD can never leave the contract (info).** The brief says \"extra stays in the contract\", so I didn't add a withdrawal. The README now states the IMD is locked permanently, and that adding an owner withdrawal before launch is the requester's call.\n- **Owner trust assumptions (info).** These are intended powers. The README now spells them out, including that `renounceOwnership` can't be undone.\n\n**Still open before sales:**\n- **`owner` key unverified.** I couldn't confirm the Intake accepts `owner` on `job.open`. The request checker still isn't reachable: `identity.md` is an unrelated site returning 404, two subdomains gave no answer, and `www` redirected. The operator should send a sample body to the real checker first; if it refuses `owner`, the README gives the exact call to remove it.\n- **Floor must be set after the pool.** Until the owner raises `imdFloor`, only the 0.5 IMD minimum applies, so the sandwich risk stays open. The README's \"After launch\" table explains how to set it from a price quote and when to update it.\n\nTests that used the old template format were updated, and one that asserted the old stuck-request behaviour was replaced. Changes are in `src/WebsiteBuyer.sol`, the two test files, `README.md` and `SECURITY.md`.","treeHash":"d2e52bd0169becfe82d603f444625818aa7c1fbb","usage":{"cachedInputTokens":3385360,"inputTokens":60,"model":"claude-opus-5-5","outputTokens":50394,"runtime":"claude","turns":57,"wallClockMs":548566}},{"artifacts":[],"attempt":1,"bundleHash":"03ba8c0b4a418d4730abcb0488893fa737d925efd11cf0bd3fa053b8865e16e2","device":"83b36551000748bb","findings":[],"hash":"77fdeacfb0e7ff2879881429747cc7ec8ba51aff456323057ee2aec5d84180ee","nodeId":"abf1591c-175a-4697-bce6-478d99c7213a","outcome":"completed","summary":"Created [launch.json](/root/.identitymd/work/a07b01eb-c2ad-4a47-9925-8e287ec6b0dc/abf1591c-175a-4697-bce6-478d99c7213a/launch.json) with `WebsiteBuyer` and matching constructor arguments. No other submitted files changed.\n\nManifest structure and compiled ABI checks passed. `forge build` succeeded; all 44 tests passed with the specified fuzz seed.\n\nNotes document that owner pool configuration is required before purchases.","treeHash":"0f901c7aa4932bbdddaa6022aa63c24794d185f0","usage":{"cachedInputTokens":422016,"inputTokens":35491,"model":"gpt-6-astra","outputTokens":3965,"runtime":"codex","turns":4,"wallClockMs":157223}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"759c614fdc84ff66","findings":[{"citation":"resolved","description":"Flow-gap seam periphery x first-principles. The brief's body template is {\"objective\":\"Build a one-page website: <words>\",\"skill\":\"build-website\",\"host\":true}, and templates must be owner-settable. bodyOf (src/WebsiteBuyer.sol:286-297) always appends ,\"owner\":\"<caller>\" after the template's fields. setTemplate only controls objectivePrefix, skill and the host flag, so no configuration can send the brief's body, or any job.open body without owner. The supplied protocol reference (which wins over the brief on protocol matters) shows job.open bodies without owner. It says to set owner only 'where the kind takes one' (univ4_hook, evm_project, evm_contracts launches). It says bodies are strict ('an unknown key ... refuses the whole ask (status 1)'), and that a kind deploying no app contracts (custom_token) refuses owner with unexpected_owner. A job.open website deploys no contracts. The brief's goal that 'the project stays theirs' is already met by the per-user IntakeAccount payer (README: 'An owner field alone would not isolate website projects'). Neither the repository nor the README reports a POST /requests/check result for this exact body, which the reference requires before baking a body. Economic trace: buyWebsite takes 0.003 ETH, swaps it, and pays 0.5 IMD to the Intake treasury (non-refundable). If the plane refuses owner on job.open, every purchase ends in onIntakeFailure(status 1) and the user's 0.003 ETH is gone. The owner has no setter that removes the key, so only a redeploy fixes it. I could not run the live checker because the plane's host is not in the supplied material. That is the evidence still needed: if the checker returns a blocker for owner on job.open, this is high (every sale loses its payment); if it does not, it is still a hard-coded deviation from the owner-settable template the brief requires. Minimal fix: make the owner slot part of the owner-settable template (e.g. a per-template bool includeOwner, or a suffix the owner sets). Default it to the brief's exact body unless the checker accepts owner.","line":294,"path":"src/WebsiteBuyer.sol","reproduction":"Owner calls setTemplate(0, \"Build a one-page website: \", \"build-website\", true, true), which is the brief's template exactly. Then bodyOf(0, alice, \"a bakery\") returns {\"objective\":\"Build a one-page website: a bakery\",\"skill\":\"build-website\",\"host\":true,\"owner\":\"0x328809bc894f92807417d2dad6b7c998c1afdac6\"}. Expected: {\"objective\":\"Build a one-page website: a bakery\",\"skill\":\"build-website\",\"host\":true}. Verified with a scratch Foundry test, whose assertEq on the expected bytes fails. On the live Intake, buyWebsite{value: 0.003 ether}(\"a bakery\", Purchase(0, 0, 0.5e18, now+300, version)) sends this body, and the 0.5 IMD is forwarded to the treasury before the plane evaluates the body.","severity":"medium","snippet":"            ',\"owner\":\"',\n            Strings.toHexString(user),\n            '\"}'","title":"bodyOf hard-codes an \"owner\" key into every job.open body; no owner-settable template can produce the brief's body, and a plane refusal would spend every buyer's payment"},{"citation":"resolved","description":"Conservation check. WebsiteBuyer has only two outflows. One is token.safeTransfer(address(account), price) at src/WebsiteBuyer.sol:194, which sends exactly the live price. The other sends the input currency to the pool manager at :272. Surplus is used for nothing either: buyWebsite needs fresh output >= max(0.5 IMD, price, minImdOut) and never spends existing balance. Each sale's excess (swap output minus Intake price) builds up with no withdraw, sweep or spend path. setIMD orphans the old token balance the same way. The brief says the extra stays in the contract, so keeping it is intended. Locking it permanently, with neither the owner nor any user able to recover it, is the defect. Economic consequence: since the surplus is worthless to the protocol, a MEV searcher who sandwiches buyWebsite down to the floor (callers passing minImdOut below the quote) takes value nobody would otherwise recover. If a sweep is added, that sandwich becomes a direct loss of owner revenue, so a sweep should come with guidance to pass a quoted minImdOut. Fix that keeps the design: an onlyOwner nonReentrant sweep(token, to, amount). Every purchase still pays exactly price from fresh output, so the surplus still stays until the owner chooses to take it.","line":27,"path":"src/WebsiteBuyer.sol","reproduction":"With the project's MockPoolManager (default amountOut 1 IMD) and Intake price 0.5 IMD, alice calls buyWebsite{value: 0.003 ether} three times. WebsiteBuyer then holds 1.5e18 IMD. Owner calls setIMD(newToken), and the 1.5e18 old IMD stays at WebsiteBuyer. No function in WebsiteBuyer's ABI (setters, buyWebsite, callbacks, unlockCallback, ownership) can move it. Expected: the owner can recover the retained extra. Actual: it is locked forever. Verified with a scratch Foundry test (balance 1500000000000000000 before and after setIMD).","severity":"low","snippet":"/// @notice Each caller owns a distinct payer account. Excess IMD remains here; no funds wait on callbacks.","title":"Surplus IMD (and any balance left after setIMD) can never leave WebsiteBuyer: the retained 'extra' has no use or recovery path"}],"hash":"7b7993ea982794b9e93dfdb42e3cb27334caa1be2523c95500e85cc92d99f571","nodeId":"016a5db4-fc03-4604-b097-3cf0ed2406ae","outcome":"completed","summary":"I found two defects in the economics review: one medium, one low. Both reproduce with scratch Foundry tests, which I deleted afterwards. The source tree is unchanged, and `.imd-findings.json` holds both findings plus 19 coverage rows: all 17 entry points and 2 invariants.\n\n**1. Medium: every request body gets an `\"owner\"` key the owner cannot remove** (`src/WebsiteBuyer.sol:294`)\n- **What happens:** `bodyOf` always adds `,\"owner\":\"<caller>\"`, and `setTemplate` only controls the objective prefix, the skill and the host flag. So the brief's body, `{\"objective\":\"Build a one-page website: <words>\",\"skill\":\"build-website\",\"host\":true}`, cannot be configured.\n- **Reproduction:** the owner sets template 0 to exactly the brief's values, and `bodyOf(0, alice, \"a bakery\")` still returns `…\"host\":true,\"owner\":\"0x3288…\"}`.\n- **Why it matters:** the intake reference adds `owner` only for launch kinds that deploy contracts, and treats bodies as strict. A website job deploys nothing. The per-user payer account already keeps each project with its user.\n- **Not confirmed:** I could not run the plane's `/requests/check` because its host isn't in the supplied material, and there is no record that the builder ran it either. If the plane refuses `owner` on a website job, every sale's 0.003 ETH is lost and only a redeploy fixes it, which would make this high.\n- **Fix:** make the owner key an owner-settable part of the template.\n\n**2. Low: surplus IMD is locked forever** (`src/WebsiteBuyer.sol:27`)\n- **What happens:** the only token transfers out are exactly the intake price to the user's account and the swap input to the pool. Nothing ever spends or withdraws the extra, and `setIMD` strands the old token's balance the same way.\n- **Reproduction:** after 3 purchases the contract holds 1.5 IMD, and it is still there after `setIMD`.\n- **Why it matters:** the brief says the extra stays in the contract, so the defect is that nobody can ever recover it. That also means anyone who sandwiches a purchase down to the 0.5 IMD floor takes value the owner could never have used.\n- **Fix:** an owner-only sweep, which keeps the current rule that each purchase pays from freshly bought IMD.\n\n**Checked and holding:**\n- **Swap settlement:** the native and wrapped-ETH paths match Uniswap v4-core's `sync`/`_settle`, partial fills revert, and no ETH is left behind.\n- **Payment flow:** the contract pays exactly the live intake price, the user's account ends at its starting balance, and its intake approval is cleared.\n- **Settings and callbacks:** every setting change invalidates purchases already in flight, and callbacks are tied to the intake that took the request.\n- **Callback gas:** a 192-byte URI fits the 200,000 gas callback limit, which an existing test covers.\n- **Static analysis leads:** the slither reentrancy-balance leads can't be reached, because the swap callback is gated and the intake is a contract the owner sets and trusts.\n\nI covered the Economic Security, Invariant and Flow Gap guides in full, and left no entry point unreached.","treeHash":null,"usage":{"cachedInputTokens":1124814,"inputTokens":32,"model":"claude-opus-5-5","outputTokens":21999,"runtime":"claude","turns":19,"wallClockMs":338668}},{"artifacts":[],"attempt":1,"bundleHash":"b78cc338cea9e4b85a138a1a3fe97a1da71c0cf411b7387419690c09e064729a","device":"94220c4a6ab72976","findings":[],"hash":"d56e1566dcd1e259a701876ab94f3475e8897dfe5b945283c28db0b0f249acab","nodeId":"9747bd13-ccc2-496d-bc07-a4b68d37fd8b","outcome":"completed","summary":"Added failure-path, fuzz, and stateful invariant coverage under `test/`.\n\n- `forge build` passed.\n- Full offline suite passed: **77 tests**, using the required seed.\n- Three invariants passed **16,384 randomized calls**.\n- No confirmed implementation defects found.\n\nIntake behavior is mocked; real Uniswap v4 integration is tested locally.","treeHash":"729f06c90934efa3beecf8c3b96fedc25d692731","usage":{"cachedInputTokens":3375616,"inputTokens":154064,"model":"gpt-6-astra","outputTokens":29494,"runtime":"codex","turns":8,"wallClockMs":781600}}],"verification":[{"checks":[{"durationMs":99652,"exitCode":0,"name":"build","output":"Compiling 95 files with Solc 0.8.26\nSolc 0.8.26 finished in 99.44s\nCompiler run successful!\n","passed":true},{"durationMs":21792,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/IntakeAccountAdversarial.t.sol:WebsiteBuyerPaymentProbeTest\n[PASS] testZeroRequestIdRollsBackSwapPaymentAndPayerCreation() (gas: 2581121)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.21ms (818.58µs CPU time)\n\nRan 2 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] testFactoryDeploymentHasExplicitLaunchOwnerAndNoExternalConstructorCalls() (gas: 12735797)\n[PASS] testZeroOwnerAndDependenciesRevert() (gas: 9091)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 32.36ms (32.68ms CPU time)\n\nRan 9 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testFuzzRealPoolConservesValueAcrossChangingETHPrices(uint96,uint96) (runs: 256, μ: 2479132, ~: 2479276)\nLogs:\n  Bound result 9000280531108246\n  Bound result 9001086311254381\n\n[PASS] testNativeSettlementResetsCurrencySyncedByPoolHook() (gas: 1787345)\n[PASS] testOwnerFloorStopsSandwichOfSurplusOnLowFeePool() (gas: 2712697)\n[PASS] testRealNativePoolSettlesAndPaysIntake() (gas: 1796029)\n[PASS] testRealPoolRollbackWhenIntakeReverts() (gas: 1313624)\n[PASS] testRealPoolSlippageRevertsPoolStateETHAndTokens() (gas: 698029)\n[PASS] testRealUninitializedAndEmptyPoolsRevert() (gas: 958475)\n[PASS] testRealWrappedNativePoolsInBothCurrencyOrders() (gas: 3576999)\n[PASS] testRealWrappedPoolIntakeFailureRollsBackDepositAndPriceAndCanRetry() (gas: 3409834)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 152.30ms (169.16ms CPU time)\n\nRan 6 tests for test/JsonTextProperties.t.sol:JsonTextPropertiesTest\n[PASS] testAllASCIIBytesHaveTheExpectedJSONSafety() (gas: 6000160)\n[PASS] testFuzzBreakingAnyContinuationByteIsRejected(uint32,uint8,uint8) (runs: 1000, μ: 21650, ~: 21589)\nLogs:\n  Bound result 3466\n  Bound result 1\n  Bound result 97\n\n[PASS] testFuzzUnicodeScalarsRoundTripThroughIndependentJSONParser(uint32,uint8) (runs: 1000, μ: 86734, ~: 77124)\nLogs:\n  Bound result 35\n  Bound result 31\n\n[PASS] testMaximumIsAByteLimitAndDoesNotPermitTruncatedScalars() (gas: 2248630)\n[PASS] testOverlongSurrogateOutOfRangeTruncatedAndStraySequencesRevert() (gas: 456862)\n[PASS] testUTF8BoundaryCodePointsRoundTripWithoutChangingWords() (gas: 663718)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 179.11ms (227.97ms CPU time)\n\nRan 13 tests for test/WebsiteBuyerEdges.t.sol:WebsiteBuyerEdgesTest\n[PASS] testAllConfigurationSettersInvalidateAnAlreadyReviewedQuote() (gas: 2027561)\n[PASS] testContractCallerOwnsPayerAndBodyRegardlessOfTransactionOrigin() (gas: 1638400)\n[PASS] testFuzzEveryDependencyMustHaveCode(uint8) (runs: 1000, μ: 174456, ~: 153690)\n[PASS] testFuzzFreshOutputMustSatisfyAllThreeFloors(uint96,uint96,uint96,uint96) (runs: 1000, μ: 1638343, ~: 1708516)\nLogs:\n  Bound result 21144\n  Bound result 1730344877\n  Bound result 16208\n  Bound result 828\n\n[PASS] testFuzzInvalidFailureStatusCannotSettle(uint8) (runs: 1000, μ: 1401880, ~: 1401889)\n[PASS] testMaximumSignedInputAndOutputDoNotTruncate() (gas: 1377849)\n[PASS] testMaximumTemplateAndWordLengthsProduceParseableBoundedJSON() (gas: 8705385)\n[PASS] testOneWeiETHPriceAndExactMinimumOutput() (gas: 1278658)\n[PASS] testPoolAndTemplateConfigurationBoundariesAreAtomic() (gas: 2709006)\n[PASS] testPurchaseEventContainsExactReceiptAndBodyCommitment() (gas: 1269961)\n[PASS] testShortWrapperMintCannotBeSubsidizedByOldBalance() (gas: 975160)\n[PASS] testWrappedRouteCannotSpendPreexistingWrappedTokensOrForcedETH() (gas: 2106061)\n[PASS] testWrongSettlementReceiptRevertsNativeAndWrappedPayments() (gas: 2844685)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 181.00ms (498.37ms CPU time)\n\nRan 9 tests for test/IntakeAccountAdversarial.t.sol:IntakeAccountAdversarialTest\n[PASS] testCannotCreateAnAccountWithNoUser() (gas: 145021)\n[PASS] testExecuteBubblesRevertAndDoesNotSpendETHOrChangeTarget() (gas: 306678)\n[PASS] testFreshPriceIsSpentWhileUserDonationsRemainRecoverable() (gas: 289113)\n[PASS] testFuzzUnderpullRevertsAllDependencyEffects(uint96) (runs: 1000, μ: 273263, ~: 273206)\nLogs:\n  Bound result 312608809383670469\n\n[PASS] testHubCannotWithdrawEvenThoughItCanAsk() (gas: 36744)\n[PASS] testOldUnlimitedAllowanceIsReducedBeforeAnIntakeCanOverpull() (gas: 440145)\n[PASS] testRefundAndNetBalanceIncreaseAreRejected() (gas: 590375)\n[PASS] testUserAndStrangerCannotInvokeHubPaymentPath() (gas: 98974)\n[PASS] testZeroFirstApprovalIsSupportedAndCleanupFailureIsAtomic() (gas: 1200566)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 181.03ms (182.98ms CPU time)\n\nRan 41 tests for test/WebsiteBuyer.t.sol:WebsiteBuyerTest\n[PASS] testAUserCanContinueOnlyTheirOwnProjectThroughAccount() (gas: 2452164)\n[PASS] testAccountCannotExecuteReentrantlyEvenWhenUserIsIntake() (gas: 1414619)\n[PASS] testActionVersionCanBeUpdated() (gas: 1334246)\n[PASS] testAtLeastHalfIMDIsBoughtEvenWhenLivePriceFalls() (gas: 1562587)\n[PASS] testCallbacksRejectWrongSenderUnknownAndDuplicateInBothDirections() (gas: 2713250)\n[PASS] testCallerSlippageAndLiveIntakePriceAreBothFloors() (gas: 536869)\n[PASS] testDeadlineBoundaryAndChangedETHPrice() (gas: 1233140)\n[PASS] testDefaultBodyIncludesCallerOwnerAndCannotNameSomeoneElse() (gas: 1295916)\n[PASS] testDifferentUsersHaveSeparateStablePayers() (gas: 3020175)\n[PASS] testDuplicateIdFromSameIntakeRevertsEntireSecondPurchase() (gas: 2232713)\n[PASS] testFeeOnTransferAndFalseReturningTokensRevert() (gas: 748157)\n[PASS] testFuzzConservationAcrossPurchases(uint96,uint8) (runs: 256, μ: 3459416, ~: 3014329)\nLogs:\n  Bound result 19500000000000000482\n  Bound result 5\n\n[PASS] testFuzzInjectedJSONSyntaxCannotReachSwap(uint8,uint16) (runs: 256, μ: 114922, ~: 113832)\nLogs:\n  Bound result 26\n  Bound result 20\n\n[PASS] testFuzzValidWordsHaveExactBody(bytes32,uint16) (runs: 256, μ: 407328, ~: 217538)\nLogs:\n  Bound result 279\n\n[PASS] testIntakeMigrationPreservesOldCallbacksAndNamespacesIdenticalIds() (gas: 4139262)\n[PASS] testIntakeRevertAndIncorrectPullRevertSwapAndAccountCreation() (gas: 2175023)\n[PASS] testInvalidAdminSettingsAndMissingCode() (gas: 404236)\n[PASS] testInvalidTemplatesRevertAndKeepThePreviousTemplate() (gas: 1681750)\n[PASS] testLivePriceIncreaseAndCallerMaximum() (gas: 1367913)\n[PASS] testMaximumURIStoresUnder200000Gas() (gas: 1529447)\n[PASS] testMissingCallbackNeverBlocksPurchases() (gas: 1880359)\n[PASS] testOnlyHubCanAskAndOnlyUserCanRecoverAccountFunds() (gas: 1404228)\n[PASS] testOnlyOwnerCanConfigureAndOwnershipUsesTwoSteps() (gas: 367576)\n[PASS] testOversizedURIRecordsHashAndEmitsFullURIUnderStipend() (gas: 2788741)\n[PASS] testOwnerCanDropTheOwnerKeyAndSendTheReferenceBody() (gas: 1375305)\n[PASS] testOwnerContractCannotRetargetConfigurationDuringSwap() (gas: 1484268)\n[PASS] testOwnerIMDFloorBindsWhenCallerSetsNoMinimum() (gas: 1642273)\n[PASS] testOwnerTemplatesHaveExactBodiesAndCanBeDisabled() (gas: 1639719)\n[PASS] testPurchasePaysExactLivePriceAndRetainsSurplus() (gas: 1350915)\n[PASS] testReentrancyDuringSwapAndIntakeCannotBuyAgain() (gas: 1859787)\n[PASS] testRefusalAndFailureSpendThePriceWithoutRefund() (gas: 2089901)\n[PASS] testResultStoresHashURIAndEmitsUnderStipend() (gas: 1445966)\n[PASS] testShortSwapCannotUseOldSurplusAndRollsBackEverything() (gas: 404522)\n[PASS] testSwapRejectsPartialInputOverspendAndInvalidCallbacks() (gas: 1772596)\n[PASS] testTemplateKeySetCanChangeWithANewActionVersion() (gas: 1503614)\n[PASS] testTokenMigrationRequiresNewPoolAndUnsupportedDecimalsFail() (gas: 2901278)\n[PASS] testTokenWithNoReturnValuesIsPaidWithNoResidualApproval() (gas: 1678625)\n[PASS] testUnpricedActionAndUnconfiguredPoolAndPause() (gas: 1685801)\n[PASS] testWordsRejectInjectionControlsEmptyOversizeAndMalformedUTF8() (gas: 283759)\n[PASS] testWrappedETHRouteSpendsFullPayment() (gas: 2072715)\n[PASS] testWrongPaymentsExpiredDeadlineAndStaleConfiguration() (gas: 283122)\nSuite result: ok. 41 passed; 0 failed; 0 skipped; finished in 181.16ms (376.69ms CPU time)\n\nRan 2 tests for test/WebsiteBuyerInvariant.t.sol:WebsiteBuyerInvariantTest\n[PASS]\nWebsiteBuyerInvariantTest invariants:\n[PASS] invariant_EveryWeiAndTokenIsAccountedFor\n[PASS] invariant_OnlyAuthorizedConfigurationAndPayersPersist\n[PASS] invariant_RequestIdentityAndTerminalResultsAreImmutable\n WebsiteBuyerInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------------+-----------------+-------+---------+----------╮\n| Contract            | Selector        | Calls | Reverts | Discards |\n+====================================================================+\n| WebsiteBuyerHandler | buy             | 3266  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| WebsiteBuyerHandler | configure       | 1560  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| WebsiteBuyerHandler | depositETH      | 1660  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| WebsiteBuyerHandler | donate          | 1531  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| WebsiteBuyerHandler | forgeCallback   | 1698  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| WebsiteBuyerHandler | rejectPurchase  | 1658  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| WebsiteBuyerHandler | settle          | 1739  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| WebsiteBuyerHandler | unauthorized    | 1604  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| WebsiteBuyerHandler | withdrawAccount | 1668  | 0       | 0        |\n╰---------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 2\n  Bound result 0\n  Bound result 3\n  Bound result 1000000000000000\n  Bound result 635\n  Bound result 92485398\n  Bound result 9625\n  Bound result 3381607679\n  Bound result 244\n  Bound result 10000000000000000000\n  Bound result 13388\n  Bound result 19041\n  Bound result 506953408300740336\n  Bound result 2996\n  Bound result 4021\n  Bound result 781678241\n  Bound result 7843\n  Bound result 2837\n  Bound result 0\n  Bound result 719206529\n  Bound result 240\n  Bound result 1998\n  Bound result 12873\n  Bound result 192\n  Bound result 3000\n  Bound result 4999999999999999997\n  Bound result 239\n  Bound result 1108\n  Bound result 10088\n  Bound result 10785\n  Bound result 64720898512492176\n  Bound result 69060\n  Bound result 1656118087511528162\n  Bound result 14667\n  Bound result 13126\n  Bound result 3697461681681343507\n  Bound result 9\n  Bound result 0\n  Bound result 0\n  Bound result 4232\n  Bound result 0\n  Bound result 1000000000000000000\n  Bound result 685\n  Bound result 4264337593543950334\n  Bound result 2659067023790259388\n  Bound result 300\n  Bound result 396031796755309390\n  Bound result 7252\n\n[PASS] testHandlerExercisesMigrationsFailuresAndRecovery() (gas: 5789993)\nLogs:\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 2\n  Bound result 0\n  Bound result 3\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000\n  Bound result 10000000000000000000\n  Bound result 3000000000000000000\n  Bound result 0\n  Bound result 0\n  Bound result 1000000000000000000\n  Bound result 2000000000000000000\n  Bound result 1000000000000000000\n  Bound result 3000000000000000000\n  Bound result 0\n  Bound result 0\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 21.61s (21.60s CPU time)\n\nRan 8 test suites in 21.61s (22.52s CPU time): 83 tests passed, 0 failed, 0 skipped (83 total tests)\n","passed":true},{"durationMs":119,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IntakeAccount.ask(address,bytes32,bytes,(address,bytes4),bytes4,address,uint256)\",\"IntakeAccount.execute(address,uint256,bytes)\",\"IntakeAccount.receive()\",\"WebsiteBuyer.acceptOwnership()\",\"WebsiteBuyer.buyWebsite(string,(uint256,uint256,uint256,uint256,uint256))\",\"WebsiteBuyer.onIntakeFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"WebsiteBuyer.onIntakeResult(bytes32,bytes32,string)\",\"WebsiteBuyer.renounceOwnership()\",\"WebsiteBuyer.setAction(bytes32)\",\"WebsiteBuyer.setETHPrice(uint256)\",\"WebsiteBuyer.setIMD(address)\",\"WebsiteBuyer.setIMDFloor(uint256)\",\"WebsiteBuyer.setIntake(address)\",\"WebsiteBuyer.setPaused(bool)\",\"WebsiteBuyer.setPool(address,address,uint24,int24,address,bytes)\",\"WebsiteBuyer.setTemplate(uint256,bytes[],uint8[],bool)\",\"WebsiteBuyer.transferOwnership(address)\",\"WebsiteBuyer.unlockCallback(bytes)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":12,\"LICENSE\":23,\"README.md\":113,\"SECURITY.md\":32,\"foundry.toml\":17,\"remappings.txt\":4,\"src/IntakeAccount.sol\":65,\"src/WebsiteBuyer.sol\":464,\"src/interfaces/IIntake.sol\":20,\"src/libraries/JsonText.sol\":37,\"test/Deployment.t.sol\":61,\"test/IntakeAccountAdversarial.t.sol\":255,\"test/JsonTextProperties.t.sol\":139,\"test/README.md\":32,\"test/UniswapV4Integration.t.sol\":353,\"test/WebsiteBuyer.t.sol\":845,\"test/WebsiteBuyerEdges.t.sol\":331,\"test/WebsiteBuyerInvariant.t.sol\":76,\"test/helpers/BuyerTestBase.sol\":61,\"test/helpers/SiteTemplate.sol\":20,\"test/invariant/WebsiteBuyerHandler.sol\":451,\"test/mocks/MockIntake.sol\":125,\"test/mocks/MockPoolManager.sol\":101,\"test/mocks/MockToken.sol\":78},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"4eb2502fb52e5bfed15e6fd392d393a4ab9a972531cd0695c0b62195a0af10aa","verifiedTreeHash":"d99d760534c1d92aaebe69f85d88d22dbc56ec74","verifierVersion":"0.1.0+c3fba9eb"},{"checks":[{"durationMs":21547,"exitCode":0,"name":"build","output":"Compiling 88 files with Solc 0.8.26\nSolc 0.8.26 finished in 21.44s\nCompiler run successful!\n","passed":true},{"durationMs":181,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 6 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testNativeSettlementResetsCurrencySyncedByPoolHook() (gas: 1765246)\n[PASS] testRealNativePoolSettlesAndPaysIntake() (gas: 1773632)\n[PASS] testRealPoolRollbackWhenIntakeReverts() (gas: 1291618)\n[PASS] testRealPoolSlippageRevertsPoolStateETHAndTokens() (gas: 676046)\n[PASS] testRealUninitializedAndEmptyPoolsRevert() (gas: 914598)\n[PASS] testRealWrappedNativePoolsInBothCurrencyOrders() (gas: 3532886)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 9.62ms (5.73ms CPU time)\n\nRan 2 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] testFactoryDeploymentHasExplicitLaunchOwnerAndNoExternalConstructorCalls() (gas: 11210849)\n[PASS] testZeroOwnerAndDependenciesRevert() (gas: 9091)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 19.62ms (20.15ms CPU time)\n\nRan 36 tests for test/WebsiteBuyer.t.sol:WebsiteBuyerTest\n[PASS] testAUserCanContinueOnlyTheirOwnProjectThroughAccount() (gas: 2408126)\n[PASS] testAccountCannotExecuteReentrantlyEvenWhenUserIsIntake() (gas: 1392539)\n[PASS] testActionVersionCanBeUpdated() (gas: 1312158)\n[PASS] testAtLeastHalfIMDIsBoughtEvenWhenLivePriceFalls() (gas: 1518776)\n[PASS] testCallbacksRejectWrongSenderUnknownAndDuplicateInBothDirections() (gas: 2668745)\n[PASS] testCallerSlippageAndLiveIntakePriceAreBothFloors() (gas: 493240)\n[PASS] testDeadlineBoundaryAndChangedETHPrice() (gas: 1211153)\n[PASS] testDefaultBodyIncludesCallerOwnerAndCannotNameSomeoneElse() (gas: 1254401)\n[PASS] testDifferentUsersHaveSeparateStablePayers() (gas: 2954334)\n[PASS] testDuplicateIdFromSameIntakeRevertsEntireSecondPurchase() (gas: 2188763)\n[PASS] testFeeOnTransferAndFalseReturningTokensRevert() (gas: 704461)\n[PASS] testFuzzConservationAcrossPurchases(uint96,uint8) (runs: 256, μ: 3218792, ~: 2948366)\nLogs:\n  Bound result 19500000000000000663\n  Bound result 1\n\n[PASS] testFuzzInjectedJSONSyntaxCannotReachSwap(uint8,uint16) (runs: 256, μ: 114801, ~: 113672)\nLogs:\n  Bound result 17\n  Bound result 31\n\n[PASS] testFuzzValidWordsHaveExactBody(bytes32,uint16) (runs: 256, μ: 357234, ~: 149024)\nLogs:\n  Bound result 946\n\n[PASS] testIntakeMigrationPreservesOldCallbacksAndNamespacesIdenticalIds() (gas: 4094911)\n[PASS] testIntakeRevertAndIncorrectPullRevertSwapAndAccountCreation() (gas: 2131259)\n[PASS] testInvalidAdminSettingsAndMissingCode() (gas: 407149)\n[PASS] testLivePriceIncreaseAndCallerMaximum() (gas: 1326499)\n[PASS] testMaximumURIStoresUnder200000Gas() (gas: 1507137)\n[PASS] testOnlyHubCanAskAndOnlyUserCanRecoverAccountFunds() (gas: 1382170)\n[PASS] testOnlyOwnerCanConfigureAndOwnershipUsesTwoSteps() (gas: 339295)\n[PASS] testOversizedURIIsNotStoredAndMissingCallbackNeverBlocksPurchases() (gas: 1870422)\n[PASS] testOwnerContractCannotRetargetConfigurationDuringSwap() (gas: 1461858)\n[PASS] testOwnerTemplatesHaveExactBodiesAndCanBeDisabled() (gas: 1402266)\n[PASS] testPurchasePaysExactLivePriceAndRetainsSurplus() (gas: 1328786)\n[PASS] testReentrancyDuringSwapAndIntakeCannotBuyAgain() (gas: 1837575)\n[PASS] testRefusalAndFailureSpendThePriceWithoutRefund() (gas: 2045844)\n[PASS] testResultStoresHashURIAndEmitsUnderStipend() (gas: 1423818)\n[PASS] testShortSwapCannotUseOldSurplusAndRollsBackEverything() (gas: 382514)\n[PASS] testSwapRejectsPartialInputOverspendAndInvalidCallbacks() (gas: 1641680)\n[PASS] testTokenMigrationRequiresNewPoolAndUnsupportedDecimalsFail() (gas: 2878756)\n[PASS] testTokenWithNoReturnValuesIsPaidWithNoResidualApproval() (gas: 1656303)\n[PASS] testUnpricedActionAndUnconfiguredPoolAndPause() (gas: 1643790)\n[PASS] testWordsRejectInjectionControlsEmptyOversizeAndMalformedUTF8() (gas: 263278)\n[PASS] testWrappedETHRouteSpendsFullPayment() (gas: 2050722)\n[PASS] testWrongPaymentsExpiredDeadlineAndStaleConfiguration() (gas: 256509)\nSuite result: ok. 36 passed; 0 failed; 0 skipped; finished in 68.64ms (167.43ms CPU time)\n\nRan 3 test suites in 69.55ms (97.87ms CPU time): 44 tests passed, 0 failed, 0 skipped (44 total tests)\n","passed":true},{"durationMs":75,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IntakeAccount.ask(address,bytes32,bytes,(address,bytes4),bytes4,address,uint256)\",\"IntakeAccount.execute(address,uint256,bytes)\",\"IntakeAccount.receive()\",\"WebsiteBuyer.acceptOwnership()\",\"WebsiteBuyer.buyWebsite(string,(uint256,uint256,uint256,uint256,uint256))\",\"WebsiteBuyer.onIntakeFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"WebsiteBuyer.onIntakeResult(bytes32,bytes32,string)\",\"WebsiteBuyer.renounceOwnership()\",\"WebsiteBuyer.setAction(bytes32)\",\"WebsiteBuyer.setETHPrice(uint256)\",\"WebsiteBuyer.setIMD(address)\",\"WebsiteBuyer.setIntake(address)\",\"WebsiteBuyer.setPaused(bool)\",\"WebsiteBuyer.setPool(address,address,uint24,int24,address,bytes)\",\"WebsiteBuyer.setTemplate(uint256,string,string,bool,bool)\",\"WebsiteBuyer.transferOwnership(address)\",\"WebsiteBuyer.unlockCallback(bytes)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":12,\"LICENSE\":23,\"README.md\":101,\"SECURITY.md\":32,\"foundry.toml\":17,\"remappings.txt\":4,\"src/IntakeAccount.sol\":65,\"src/WebsiteBuyer.sol\":417,\"src/interfaces/IIntake.sol\":20,\"src/libraries/JsonText.sol\":37,\"test/Deployment.t.sol\":61,\"test/UniswapV4Integration.t.sol\":224,\"test/WebsiteBuyer.t.sol\":692,\"test/mocks/MockIntake.sol\":125,\"test/mocks/MockPoolManager.sol\":101,\"test/mocks/MockToken.sol\":78},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":3660,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/WebsiteBuyer.sol:218: Reentrancy in WebsiteBuyer._buyIMD(uint256) (src/WebsiteBuyer.sol#218-240):\n[high/medium] reentrancy-balance at src/WebsiteBuyer.sol:218: Reentrancy in WebsiteBuyer._buyIMD(uint256) (src/WebsiteBuyer.sol#218-240):\n[high/medium] reentrancy-balance at src/WebsiteBuyer.sol:218: Reentrancy in WebsiteBuyer._buyIMD(uint256) (src/WebsiteBuyer.sol#218-240):\n[high/medium] reentrancy-balance at src/WebsiteBuyer.sol:181: Reentrancy in WebsiteBuyer._open(bytes,uint256,uint256,uint256) (src/WebsiteBuyer.sol#181-216):\n[high/medium] reentrancy-balance at src/WebsiteBuyer.sol:181: Reentrancy in WebsiteBuyer._open(bytes,uint256,uint256,uint256) (src/WebsiteBuyer.sol#181-216):\n[high/medium] reentrancy-balance at src/IntakeAccount.sol:29: Reentrancy in IntakeAccount.ask(IIntake,bytes32,bytes,IIntake.Callback,bytes4,address,uint256) (src/IntakeAccount.sol#29-46):\n[high/medium] reentrancy-balance at src/WebsiteBuyer.sol:181: Reentrancy in WebsiteBuyer._open(bytes,uint256,uint256,uint256) (src/WebsiteBuyer.sol#181-216):\n[high/medium] reentrancy-balance at src/IntakeAccount.sol:29: Reentrancy in IntakeAccount.ask(IIntake,bytes32,bytes,IIntake.Callback,bytes4,address,uint256) (src/IntakeAccount.sol#29-46):\n[high/medium] reentrancy-balance at src/IntakeAccount.sol:29: Reentrancy in IntakeAccount.ask(IIntake,bytes32,bytes,IIntake.Callback,bytes4,address,uint256) (src/IntakeAccount.sol#29-46):\n[medium/medium] uninitialized-local at src/libraries/JsonText.sol:18: JsonText.check(bytes,uint256,bool).n (src/libraries/JsonText.sol#18) is a local variable never initialized\n[low/medium] missing-zero-check at src/IntakeAccount.sol:49: IntakeAccount.execute(address,uint256,bytes).target (src/IntakeAccount.sol#49) lacks a zero-check on :\n[low/medium] reentrancy-benign at src/WebsiteBuyer.sol:181: Reentrancy in WebsiteBuyer._open(bytes,uint256,uint256,uint256) (src/WebsiteBuyer.sol#181-216):\n[low/medium] reentrancy-benign at src/WebsiteBuyer.sol:218: Reentrancy in WebsiteBuyer._buyIMD(uint256) (src/WebsiteBuyer.sol#218-240):\n[low/medium] reentrancy-benign at src/WebsiteBuyer.sol:218: Reentrancy in WebsiteBuyer._buyIMD(uint256) (src/WebsiteBuyer.sol#218-240):\n[low/medium] timestamp at src/WebsiteBuyer.sol:154: WebsiteBuyer.buyWebsite(string,WebsiteBuyer.Purchase) (src/WebsiteBuyer.sol#154-179) uses timestamp for comparisons","passed":true},{"durationMs":533,"exitCode":0,"name":"aderyn","output":"[high] abi-encode-packed-hash-collision at src/WebsiteBuyer.sol:286: `abi.encodePacked()` Hash Collision\n[low] centralization-risk at src/WebsiteBuyer.sol:28: Centralization Risk (8 places)\n[low] large-numeric-literal at src/WebsiteBuyer.sol:390: Large Numeric Literal\n[low] literal-instead-of-constant at src/WebsiteBuyer.sol:390: Literal Instead of Constant (14 places)\n[low] local-variable-shadowing at src/WebsiteBuyer.sol:54: Local Variable Shadows State Variable\n[low] non-reentrant-not-first at src/WebsiteBuyer.sol:335: `nonReentrant` is Not the First Modifier (7 places)\n[low] require-revert-in-loop at src/libraries/JsonText.sol:11: Loop Contains `require`/`revert` (2 places)\n[low] state-change-without-event at src/IntakeAccount.sol:29: State Change Without Event (3 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"560004ea420abed1525f30871f92e19de5367af8efa518ba6c7eba5b59e87939","verifiedTreeHash":"03360a54180dcdc4ccd25c726c57eb29a96c4cc0","verifierVersion":"0.1.0+c3fba9eb"},{"checks":[{"durationMs":45821,"exitCode":0,"name":"build","output":"Compiling 88 files with Solc 0.8.26\nSolc 0.8.26 finished in 45.63s\nCompiler run successful!\n","passed":true},{"durationMs":312,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] testFactoryDeploymentHasExplicitLaunchOwnerAndNoExternalConstructorCalls() (gas: 12735797)\n[PASS] testZeroOwnerAndDependenciesRevert() (gas: 9091)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 47.14ms (53.78ms CPU time)\n\nRan 7 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testNativeSettlementResetsCurrencySyncedByPoolHook() (gas: 1787323)\n[PASS] testOwnerFloorStopsSandwichOfSurplusOnLowFeePool() (gas: 2712675)\n[PASS] testRealNativePoolSettlesAndPaysIntake() (gas: 1795951)\n[PASS] testRealPoolRollbackWhenIntakeReverts() (gas: 1313602)\n[PASS] testRealPoolSlippageRevertsPoolStateETHAndTokens() (gas: 698029)\n[PASS] testRealUninitializedAndEmptyPoolsRevert() (gas: 958453)\n[PASS] testRealWrappedNativePoolsInBothCurrencyOrders() (gas: 3576999)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 105.03ms (25.57ms CPU time)\n\nRan 41 tests for test/WebsiteBuyer.t.sol:WebsiteBuyerTest\n[PASS] testAUserCanContinueOnlyTheirOwnProjectThroughAccount() (gas: 2452164)\n[PASS] testAccountCannotExecuteReentrantlyEvenWhenUserIsIntake() (gas: 1414619)\n[PASS] testActionVersionCanBeUpdated() (gas: 1334246)\n[PASS] testAtLeastHalfIMDIsBoughtEvenWhenLivePriceFalls() (gas: 1562587)\n[PASS] testCallbacksRejectWrongSenderUnknownAndDuplicateInBothDirections() (gas: 2713250)\n[PASS] testCallerSlippageAndLiveIntakePriceAreBothFloors() (gas: 536869)\n[PASS] testDeadlineBoundaryAndChangedETHPrice() (gas: 1233140)\n[PASS] testDefaultBodyIncludesCallerOwnerAndCannotNameSomeoneElse() (gas: 1295916)\n[PASS] testDifferentUsersHaveSeparateStablePayers() (gas: 3020175)\n[PASS] testDuplicateIdFromSameIntakeRevertsEntireSecondPurchase() (gas: 2232713)\n[PASS] testFeeOnTransferAndFalseReturningTokensRevert() (gas: 748157)\n[PASS] testFuzzConservationAcrossPurchases(uint96,uint8) (runs: 256, μ: 3364208, ~: 3014329)\nLogs:\n  Bound result 19500000280531108246\n  Bound result 4\n\n[PASS] testFuzzInjectedJSONSyntaxCannotReachSwap(uint8,uint16) (runs: 256, μ: 115100, ~: 114236)\nLogs:\n  Bound result 4\n  Bound result 24\n\n[PASS] testFuzzValidWordsHaveExactBody(bytes32,uint16) (runs: 256, μ: 386724, ~: 205969)\nLogs:\n  Bound result 946\n\n[PASS] testIntakeMigrationPreservesOldCallbacksAndNamespacesIdenticalIds() (gas: 4139262)\n[PASS] testIntakeRevertAndIncorrectPullRevertSwapAndAccountCreation() (gas: 2175023)\n[PASS] testInvalidAdminSettingsAndMissingCode() (gas: 404236)\n[PASS] testInvalidTemplatesRevertAndKeepThePreviousTemplate() (gas: 1681750)\n[PASS] testLivePriceIncreaseAndCallerMaximum() (gas: 1367913)\n[PASS] testMaximumURIStoresUnder200000Gas() (gas: 1529447)\n[PASS] testMissingCallbackNeverBlocksPurchases() (gas: 1880359)\n[PASS] testOnlyHubCanAskAndOnlyUserCanRecoverAccountFunds() (gas: 1404228)\n[PASS] testOnlyOwnerCanConfigureAndOwnershipUsesTwoSteps() (gas: 367576)\n[PASS] testOversizedURIRecordsHashAndEmitsFullURIUnderStipend() (gas: 2788741)\n[PASS] testOwnerCanDropTheOwnerKeyAndSendTheReferenceBody() (gas: 1375305)\n[PASS] testOwnerContractCannotRetargetConfigurationDuringSwap() (gas: 1484268)\n[PASS] testOwnerIMDFloorBindsWhenCallerSetsNoMinimum() (gas: 1642273)\n[PASS] testOwnerTemplatesHaveExactBodiesAndCanBeDisabled() (gas: 1639719)\n[PASS] testPurchasePaysExactLivePriceAndRetainsSurplus() (gas: 1350915)\n[PASS] testReentrancyDuringSwapAndIntakeCannotBuyAgain() (gas: 1859787)\n[PASS] testRefusalAndFailureSpendThePriceWithoutRefund() (gas: 2089901)\n[PASS] testResultStoresHashURIAndEmitsUnderStipend() (gas: 1445966)\n[PASS] testShortSwapCannotUseOldSurplusAndRollsBackEverything() (gas: 404522)\n[PASS] testSwapRejectsPartialInputOverspendAndInvalidCallbacks() (gas: 1772596)\n[PASS] testTemplateKeySetCanChangeWithANewActionVersion() (gas: 1503614)\n[PASS] testTokenMigrationRequiresNewPoolAndUnsupportedDecimalsFail() (gas: 2901278)\n[PASS] testTokenWithNoReturnValuesIsPaidWithNoResidualApproval() (gas: 1678625)\n[PASS] testUnpricedActionAndUnconfiguredPoolAndPause() (gas: 1685801)\n[PASS] testWordsRejectInjectionControlsEmptyOversizeAndMalformedUTF8() (gas: 283759)\n[PASS] testWrappedETHRouteSpendsFullPayment() (gas: 2072715)\n[PASS] testWrongPaymentsExpiredDeadlineAndStaleConfiguration() (gas: 283122)\nSuite result: ok. 41 passed; 0 failed; 0 skipped; finished in 105.14ms (274.27ms CPU time)\n\nRan 3 test suites in 106.60ms (257.32ms CPU time): 50 tests passed, 0 failed, 0 skipped (50 total tests)\n","passed":true},{"durationMs":130,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IntakeAccount.ask(address,bytes32,bytes,(address,bytes4),bytes4,address,uint256)\",\"IntakeAccount.execute(address,uint256,bytes)\",\"IntakeAccount.receive()\",\"WebsiteBuyer.acceptOwnership()\",\"WebsiteBuyer.buyWebsite(string,(uint256,uint256,uint256,uint256,uint256))\",\"WebsiteBuyer.onIntakeFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"WebsiteBuyer.onIntakeResult(bytes32,bytes32,string)\",\"WebsiteBuyer.renounceOwnership()\",\"WebsiteBuyer.setAction(bytes32)\",\"WebsiteBuyer.setETHPrice(uint256)\",\"WebsiteBuyer.setIMD(address)\",\"WebsiteBuyer.setIMDFloor(uint256)\",\"WebsiteBuyer.setIntake(address)\",\"WebsiteBuyer.setPaused(bool)\",\"WebsiteBuyer.setPool(address,address,uint24,int24,address,bytes)\",\"WebsiteBuyer.setTemplate(uint256,bytes[],uint8[],bool)\",\"WebsiteBuyer.transferOwnership(address)\",\"WebsiteBuyer.unlockCallback(bytes)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":12,\"LICENSE\":23,\"README.md\":113,\"SECURITY.md\":32,\"foundry.toml\":17,\"launch.json\":14,\"remappings.txt\":4,\"src/IntakeAccount.sol\":65,\"src/WebsiteBuyer.sol\":464,\"src/interfaces/IIntake.sol\":20,\"src/libraries/JsonText.sol\":37,\"test/Deployment.t.sol\":61,\"test/UniswapV4Integration.t.sol\":297,\"test/WebsiteBuyer.t.sol\":845,\"test/mocks/MockIntake.sol\":125,\"test/mocks/MockPoolManager.sol\":101,\"test/mocks/MockToken.sol\":78},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"59f11f0b7294d56aed393a13021ad844ddf5b01483aca865c7e078ba4e9dca7a","verifiedTreeHash":"05f35655b8b74c63fe2c5ff1eefbdc999119e186","verifierVersion":"0.1.0+c3fba9eb"},{"checks":[{"durationMs":21341,"exitCode":0,"name":"build","output":"Compiling 88 files with Solc 0.8.26\nSolc 0.8.26 finished in 21.25s\nCompiler run successful!\n","passed":true},{"durationMs":170,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] testFactoryDeploymentHasExplicitLaunchOwnerAndNoExternalConstructorCalls() (gas: 12735797)\n[PASS] testZeroOwnerAndDependenciesRevert() (gas: 9091)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 21.36ms (21.89ms CPU time)\n\nRan 7 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testNativeSettlementResetsCurrencySyncedByPoolHook() (gas: 1787323)\n[PASS] testOwnerFloorStopsSandwichOfSurplusOnLowFeePool() (gas: 2712675)\n[PASS] testRealNativePoolSettlesAndPaysIntake() (gas: 1795951)\n[PASS] testRealPoolRollbackWhenIntakeReverts() (gas: 1313602)\n[PASS] testRealPoolSlippageRevertsPoolStateETHAndTokens() (gas: 698029)\n[PASS] testRealUninitializedAndEmptyPoolsRevert() (gas: 958453)\n[PASS] testRealWrappedNativePoolsInBothCurrencyOrders() (gas: 3576999)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 65.34ms (7.24ms CPU time)\n\nRan 41 tests for test/WebsiteBuyer.t.sol:WebsiteBuyerTest\n[PASS] testAUserCanContinueOnlyTheirOwnProjectThroughAccount() (gas: 2452164)\n[PASS] testAccountCannotExecuteReentrantlyEvenWhenUserIsIntake() (gas: 1414619)\n[PASS] testActionVersionCanBeUpdated() (gas: 1334246)\n[PASS] testAtLeastHalfIMDIsBoughtEvenWhenLivePriceFalls() (gas: 1562587)\n[PASS] testCallbacksRejectWrongSenderUnknownAndDuplicateInBothDirections() (gas: 2713250)\n[PASS] testCallerSlippageAndLiveIntakePriceAreBothFloors() (gas: 536869)\n[PASS] testDeadlineBoundaryAndChangedETHPrice() (gas: 1233140)\n[PASS] testDefaultBodyIncludesCallerOwnerAndCannotNameSomeoneElse() (gas: 1295916)\n[PASS] testDifferentUsersHaveSeparateStablePayers() (gas: 3020175)\n[PASS] testDuplicateIdFromSameIntakeRevertsEntireSecondPurchase() (gas: 2232713)\n[PASS] testFeeOnTransferAndFalseReturningTokensRevert() (gas: 748157)\n[PASS] testFuzzConservationAcrossPurchases(uint96,uint8) (runs: 256, μ: 3364208, ~: 3014329)\nLogs:\n  Bound result 19500000280531108246\n  Bound result 4\n\n[PASS] testFuzzInjectedJSONSyntaxCannotReachSwap(uint8,uint16) (runs: 256, μ: 115100, ~: 114236)\nLogs:\n  Bound result 4\n  Bound result 24\n\n[PASS] testFuzzValidWordsHaveExactBody(bytes32,uint16) (runs: 256, μ: 386724, ~: 205969)\nLogs:\n  Bound result 946\n\n[PASS] testIntakeMigrationPreservesOldCallbacksAndNamespacesIdenticalIds() (gas: 4139262)\n[PASS] testIntakeRevertAndIncorrectPullRevertSwapAndAccountCreation() (gas: 2175023)\n[PASS] testInvalidAdminSettingsAndMissingCode() (gas: 404236)\n[PASS] testInvalidTemplatesRevertAndKeepThePreviousTemplate() (gas: 1681750)\n[PASS] testLivePriceIncreaseAndCallerMaximum() (gas: 1367913)\n[PASS] testMaximumURIStoresUnder200000Gas() (gas: 1529447)\n[PASS] testMissingCallbackNeverBlocksPurchases() (gas: 1880359)\n[PASS] testOnlyHubCanAskAndOnlyUserCanRecoverAccountFunds() (gas: 1404228)\n[PASS] testOnlyOwnerCanConfigureAndOwnershipUsesTwoSteps() (gas: 367576)\n[PASS] testOversizedURIRecordsHashAndEmitsFullURIUnderStipend() (gas: 2788741)\n[PASS] testOwnerCanDropTheOwnerKeyAndSendTheReferenceBody() (gas: 1375305)\n[PASS] testOwnerContractCannotRetargetConfigurationDuringSwap() (gas: 1484268)\n[PASS] testOwnerIMDFloorBindsWhenCallerSetsNoMinimum() (gas: 1642273)\n[PASS] testOwnerTemplatesHaveExactBodiesAndCanBeDisabled() (gas: 1639719)\n[PASS] testPurchasePaysExactLivePriceAndRetainsSurplus() (gas: 1350915)\n[PASS] testReentrancyDuringSwapAndIntakeCannotBuyAgain() (gas: 1859787)\n[PASS] testRefusalAndFailureSpendThePriceWithoutRefund() (gas: 2089901)\n[PASS] testResultStoresHashURIAndEmitsUnderStipend() (gas: 1445966)\n[PASS] testShortSwapCannotUseOldSurplusAndRollsBackEverything() (gas: 404522)\n[PASS] testSwapRejectsPartialInputOverspendAndInvalidCallbacks() (gas: 1772596)\n[PASS] testTemplateKeySetCanChangeWithANewActionVersion() (gas: 1503614)\n[PASS] testTokenMigrationRequiresNewPoolAndUnsupportedDecimalsFail() (gas: 2901278)\n[PASS] testTokenWithNoReturnValuesIsPaidWithNoResidualApproval() (gas: 1678625)\n[PASS] testUnpricedActionAndUnconfiguredPoolAndPause() (gas: 1685801)\n[PASS] testWordsRejectInjectionControlsEmptyOversizeAndMalformedUTF8() (gas: 283759)\n[PASS] testWrappedETHRouteSpendsFullPayment() (gas: 2072715)\n[PASS] testWrongPaymentsExpiredDeadlineAndStaleConfiguration() (gas: 283122)\nSuite result: ok. 41 passed; 0 failed; 0 skipped; finished in 70.51ms (159.87ms CPU time)\n\nRan 3 test suites in 71.46ms (157.22ms CPU time): 50 tests passed, 0 failed, 0 skipped (50 total tests)\n","passed":true},{"durationMs":62,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IntakeAccount.ask(address,bytes32,bytes,(address,bytes4),bytes4,address,uint256)\",\"IntakeAccount.execute(address,uint256,bytes)\",\"IntakeAccount.receive()\",\"WebsiteBuyer.acceptOwnership()\",\"WebsiteBuyer.buyWebsite(string,(uint256,uint256,uint256,uint256,uint256))\",\"WebsiteBuyer.onIntakeFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"WebsiteBuyer.onIntakeResult(bytes32,bytes32,string)\",\"WebsiteBuyer.renounceOwnership()\",\"WebsiteBuyer.setAction(bytes32)\",\"WebsiteBuyer.setETHPrice(uint256)\",\"WebsiteBuyer.setIMD(address)\",\"WebsiteBuyer.setIMDFloor(uint256)\",\"WebsiteBuyer.setIntake(address)\",\"WebsiteBuyer.setPaused(bool)\",\"WebsiteBuyer.setPool(address,address,uint24,int24,address,bytes)\",\"WebsiteBuyer.setTemplate(uint256,bytes[],uint8[],bool)\",\"WebsiteBuyer.transferOwnership(address)\",\"WebsiteBuyer.unlockCallback(bytes)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":12,\"LICENSE\":23,\"README.md\":113,\"SECURITY.md\":32,\"foundry.toml\":17,\"remappings.txt\":4,\"src/IntakeAccount.sol\":65,\"src/WebsiteBuyer.sol\":464,\"src/interfaces/IIntake.sol\":20,\"src/libraries/JsonText.sol\":37,\"test/Deployment.t.sol\":61,\"test/UniswapV4Integration.t.sol\":297,\"test/WebsiteBuyer.t.sol\":845,\"test/mocks/MockIntake.sol\":125,\"test/mocks/MockPoolManager.sol\":101,\"test/mocks/MockToken.sol\":78},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":4026,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/WebsiteBuyer.sol:203: Reentrancy in WebsiteBuyer._open(bytes,uint256,uint256,uint256) (src/WebsiteBuyer.sol#203-238):\n[high/medium] reentrancy-balance at src/IntakeAccount.sol:29: Reentrancy in IntakeAccount.ask(IIntake,bytes32,bytes,IIntake.Callback,bytes4,address,uint256) (src/IntakeAccount.sol#29-46):\n[high/medium] reentrancy-balance at src/WebsiteBuyer.sol:240: Reentrancy in WebsiteBuyer._buyIMD(uint256) (src/WebsiteBuyer.sol#240-262):\n[high/medium] reentrancy-balance at src/WebsiteBuyer.sol:240: Reentrancy in WebsiteBuyer._buyIMD(uint256) (src/WebsiteBuyer.sol#240-262):\n[high/medium] reentrancy-balance at src/WebsiteBuyer.sol:240: Reentrancy in WebsiteBuyer._buyIMD(uint256) (src/WebsiteBuyer.sol#240-262):\n[high/medium] reentrancy-balance at src/IntakeAccount.sol:29: Reentrancy in IntakeAccount.ask(IIntake,bytes32,bytes,IIntake.Callback,bytes4,address,uint256) (src/IntakeAccount.sol#29-46):\n[high/medium] reentrancy-balance at src/WebsiteBuyer.sol:203: Reentrancy in WebsiteBuyer._open(bytes,uint256,uint256,uint256) (src/WebsiteBuyer.sol#203-238):\n[high/medium] reentrancy-balance at src/WebsiteBuyer.sol:203: Reentrancy in WebsiteBuyer._open(bytes,uint256,uint256,uint256) (src/WebsiteBuyer.sol#203-238):\n[high/medium] reentrancy-balance at src/IntakeAccount.sol:29: Reentrancy in IntakeAccount.ask(IIntake,bytes32,bytes,IIntake.Callback,bytes4,address,uint256) (src/IntakeAccount.sol#29-46):\n[high/high] uninitialized-state at src/WebsiteBuyer.sol:303: WebsiteBuyer._templates (src/WebsiteBuyer.sol#145) is never initialized. It is used in:\n[medium/medium] uninitialized-local at src/WebsiteBuyer.sol:404: WebsiteBuyer._storeTemplate(uint256,bytes[],WebsiteBuyer.Slot[],bool).words (src/WebsiteBuyer.sol#404) is a local variable never initialized\n[medium/medium] uninitialized-local at src/libraries/JsonText.sol:18: JsonText.check(bytes,uint256,bool).n (src/libraries/JsonText.sol#18) is a local variable never initialized\n[medium/medium] uninitialized-local at src/WebsiteBuyer.sol:408: WebsiteBuyer._storeTemplate(uint256,bytes[],WebsiteBuyer.Slot[],bool).size (src/WebsiteBuyer.sol#408) is a local variable never initialized\n[low/medium] missing-zero-check at src/IntakeAccount.sol:49: IntakeAccount.execute(address,uint256,bytes).target (src/IntakeAccount.sol#49) lacks a zero-check on :\n[low/medium] reentrancy-benign at src/WebsiteBuyer.sol:240: Reentrancy in WebsiteBuyer._buyIMD(uint256) (src/WebsiteBuyer.sol#240-262):\n[low/medium] reentrancy-benign at src/WebsiteBuyer.sol:240: Reentrancy in WebsiteBuyer._buyIMD(uint256) (src/WebsiteBuyer.sol#240-262):\n[low/medium] reentrancy-benign at src/WebsiteBuyer.sol:203: Reentrancy in WebsiteBuyer._open(bytes,uint256,uint256,uint256) (src/WebsiteBuyer.sol#203-238):\n[low/medium] timestamp at src/WebsiteBuyer.sol:175: WebsiteBuyer.buyWebsite(string,WebsiteBuyer.Purchase) (src/WebsiteBuyer.sol#175-201) uses timestamp for comparisons","passed":true},{"durationMs":455,"exitCode":0,"name":"aderyn","output":"[low] centralization-risk at src/WebsiteBuyer.sol:28: Centralization Risk (9 places)\n[low] costly-loop at src/WebsiteBuyer.sol:416: Costly operations inside loop (2 places)\n[low] large-numeric-literal at src/WebsiteBuyer.sol:437: Large Numeric Literal\n[low] literal-instead-of-constant at src/WebsiteBuyer.sol:437: Literal Instead of Constant (14 places)\n[low] local-variable-shadowing at src/WebsiteBuyer.sol:63: Local Variable Shadows State Variable\n[low] non-reentrant-not-first at src/WebsiteBuyer.sol:350: `nonReentrant` is Not the First Modifier (8 places)\n[low] require-revert-in-loop at src/WebsiteBuyer.sol:309: Loop Contains `require`/`revert` (3 places)\n[low] state-change-without-event at src/IntakeAccount.sol:29: State Change Without Event (3 places)\n[low] uninitialized-local-variable at src/WebsiteBuyer.sol:309: Uninitialized Local Variable (6 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"5dece06b828104f6e4e135a93d1b6353fe34b76281cec128738e2a673692e5d9","verifiedTreeHash":"d2e52bd0169becfe82d603f444625818aa7c1fbb","verifierVersion":"0.1.0+c3fba9eb"},{"checks":[{"durationMs":51615,"exitCode":0,"name":"build","output":"Compiling 88 files with Solc 0.8.26\nSolc 0.8.26 finished in 51.42s\nCompiler run successful!\n","passed":true},{"durationMs":434,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 6 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testNativeSettlementResetsCurrencySyncedByPoolHook() (gas: 1765246)\n[PASS] testRealNativePoolSettlesAndPaysIntake() (gas: 1773632)\n[PASS] testRealPoolRollbackWhenIntakeReverts() (gas: 1291618)\n[PASS] testRealPoolSlippageRevertsPoolStateETHAndTokens() (gas: 676046)\n[PASS] testRealUninitializedAndEmptyPoolsRevert() (gas: 914598)\n[PASS] testRealWrappedNativePoolsInBothCurrencyOrders() (gas: 3532886)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 8.46ms (12.97ms CPU time)\n\nRan 2 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] testFactoryDeploymentHasExplicitLaunchOwnerAndNoExternalConstructorCalls() (gas: 11210849)\n[PASS] testZeroOwnerAndDependenciesRevert() (gas: 9091)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 42.16ms (44.24ms CPU time)\n\nRan 36 tests for test/WebsiteBuyer.t.sol:WebsiteBuyerTest\n[PASS] testAUserCanContinueOnlyTheirOwnProjectThroughAccount() (gas: 2408126)\n[PASS] testAccountCannotExecuteReentrantlyEvenWhenUserIsIntake() (gas: 1392539)\n[PASS] testActionVersionCanBeUpdated() (gas: 1312158)\n[PASS] testAtLeastHalfIMDIsBoughtEvenWhenLivePriceFalls() (gas: 1518776)\n[PASS] testCallbacksRejectWrongSenderUnknownAndDuplicateInBothDirections() (gas: 2668745)\n[PASS] testCallerSlippageAndLiveIntakePriceAreBothFloors() (gas: 493240)\n[PASS] testDeadlineBoundaryAndChangedETHPrice() (gas: 1211153)\n[PASS] testDefaultBodyIncludesCallerOwnerAndCannotNameSomeoneElse() (gas: 1254401)\n[PASS] testDifferentUsersHaveSeparateStablePayers() (gas: 2954334)\n[PASS] testDuplicateIdFromSameIntakeRevertsEntireSecondPurchase() (gas: 2188763)\n[PASS] testFeeOnTransferAndFalseReturningTokensRevert() (gas: 704461)\n[PASS] testFuzzConservationAcrossPurchases(uint96,uint8) (runs: 256, μ: 3218792, ~: 2948366)\nLogs:\n  Bound result 19500000001383378921\n  Bound result 3\n\n[PASS] testFuzzInjectedJSONSyntaxCannotReachSwap(uint8,uint16) (runs: 256, μ: 114801, ~: 113672)\nLogs:\n  Bound result 8\n  Bound result 24\n\n[PASS] testFuzzValidWordsHaveExactBody(bytes32,uint16) (runs: 256, μ: 357234, ~: 149024)\nLogs:\n  Bound result 279\n\n[PASS] testIntakeMigrationPreservesOldCallbacksAndNamespacesIdenticalIds() (gas: 4094911)\n[PASS] testIntakeRevertAndIncorrectPullRevertSwapAndAccountCreation() (gas: 2131259)\n[PASS] testInvalidAdminSettingsAndMissingCode() (gas: 407149)\n[PASS] testLivePriceIncreaseAndCallerMaximum() (gas: 1326499)\n[PASS] testMaximumURIStoresUnder200000Gas() (gas: 1507137)\n[PASS] testOnlyHubCanAskAndOnlyUserCanRecoverAccountFunds() (gas: 1382170)\n[PASS] testOnlyOwnerCanConfigureAndOwnershipUsesTwoSteps() (gas: 339295)\n[PASS] testOversizedURIIsNotStoredAndMissingCallbackNeverBlocksPurchases() (gas: 1870422)\n[PASS] testOwnerContractCannotRetargetConfigurationDuringSwap() (gas: 1461858)\n[PASS] testOwnerTemplatesHaveExactBodiesAndCanBeDisabled() (gas: 1402266)\n[PASS] testPurchasePaysExactLivePriceAndRetainsSurplus() (gas: 1328786)\n[PASS] testReentrancyDuringSwapAndIntakeCannotBuyAgain() (gas: 1837575)\n[PASS] testRefusalAndFailureSpendThePriceWithoutRefund() (gas: 2045844)\n[PASS] testResultStoresHashURIAndEmitsUnderStipend() (gas: 1423818)\n[PASS] testShortSwapCannotUseOldSurplusAndRollsBackEverything() (gas: 382514)\n[PASS] testSwapRejectsPartialInputOverspendAndInvalidCallbacks() (gas: 1641680)\n[PASS] testTokenMigrationRequiresNewPoolAndUnsupportedDecimalsFail() (gas: 2878756)\n[PASS] testTokenWithNoReturnValuesIsPaidWithNoResidualApproval() (gas: 1656303)\n[PASS] testUnpricedActionAndUnconfiguredPoolAndPause() (gas: 1643790)\n[PASS] testWordsRejectInjectionControlsEmptyOversizeAndMalformedUTF8() (gas: 263278)\n[PASS] testWrappedETHRouteSpendsFullPayment() (gas: 2050722)\n[PASS] testWrongPaymentsExpiredDeadlineAndStaleConfiguration() (gas: 256509)\nSuite result: ok. 36 passed; 0 failed; 0 skipped; finished in 179.19ms (359.40ms CPU time)\n\nRan 3 test suites in 181.13ms (229.81ms CPU time): 44 tests passed, 0 failed, 0 skipped (44 total tests)\n","passed":true},{"durationMs":160,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IntakeAccount.ask(address,bytes32,bytes,(address,bytes4),bytes4,address,uint256)\",\"IntakeAccount.execute(address,uint256,bytes)\",\"IntakeAccount.receive()\",\"WebsiteBuyer.acceptOwnership()\",\"WebsiteBuyer.buyWebsite(string,(uint256,uint256,uint256,uint256,uint256))\",\"WebsiteBuyer.onIntakeFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"WebsiteBuyer.onIntakeResult(bytes32,bytes32,string)\",\"WebsiteBuyer.renounceOwnership()\",\"WebsiteBuyer.setAction(bytes32)\",\"WebsiteBuyer.setETHPrice(uint256)\",\"WebsiteBuyer.setIMD(address)\",\"WebsiteBuyer.setIntake(address)\",\"WebsiteBuyer.setPaused(bool)\",\"WebsiteBuyer.setPool(address,address,uint24,int24,address,bytes)\",\"WebsiteBuyer.setTemplate(uint256,string,string,bool,bool)\",\"WebsiteBuyer.transferOwnership(address)\",\"WebsiteBuyer.unlockCallback(bytes)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":12,\"LICENSE\":23,\"README.md\":101,\"SECURITY.md\":32,\"foundry.toml\":17,\"launch.json\":14,\"remappings.txt\":4,\"src/IntakeAccount.sol\":65,\"src/WebsiteBuyer.sol\":417,\"src/interfaces/IIntake.sol\":20,\"src/libraries/JsonText.sol\":37,\"test/Deployment.t.sol\":61,\"test/UniswapV4Integration.t.sol\":224,\"test/WebsiteBuyer.t.sol\":692,\"test/mocks/MockIntake.sol\":125,\"test/mocks/MockPoolManager.sol\":101,\"test/mocks/MockToken.sol\":78},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"77fdeacfb0e7ff2879881429747cc7ec8ba51aff456323057ee2aec5d84180ee","verifiedTreeHash":"0f901c7aa4932bbdddaa6022aa63c24794d185f0","verifierVersion":"0.1.0+c3fba9eb"},{"checks":[{"durationMs":40503,"exitCode":0,"name":"build","output":"Compiling 94 files with Solc 0.8.26\nSolc 0.8.26 finished in 40.41s\nCompiler run successful!\n","passed":true},{"durationMs":12012,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/IntakeAccountAdversarial.t.sol:WebsiteBuyerPaymentProbeTest\n[PASS] testZeroRequestIdRollsBackSwapPaymentAndPayerCreation() (gas: 2537256)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.07ms (1.03ms CPU time)\n\nRan 2 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] testFactoryDeploymentHasExplicitLaunchOwnerAndNoExternalConstructorCalls() (gas: 11210849)\n[PASS] testZeroOwnerAndDependenciesRevert() (gas: 9091)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 39.48ms (39.63ms CPU time)\n\nRan 6 tests for test/JsonTextProperties.t.sol:JsonTextPropertiesTest\n[PASS] testAllASCIIBytesHaveTheExpectedJSONSafety() (gas: 4164434)\n[PASS] testFuzzBreakingAnyContinuationByteIsRejected(uint32,uint8,uint8) (runs: 1000, μ: 21663, ~: 21589)\nLogs:\n  Bound result 1502\n  Bound result 1\n  Bound result 99\n\n[PASS] testFuzzUnicodeScalarsRoundTripThroughIndependentJSONParser(uint32,uint8) (runs: 1000, μ: 67043, ~: 58790)\nLogs:\n  Bound result 674014\n  Bound result 1\n\n[PASS] testMaximumIsAByteLimitAndDoesNotPermitTruncatedScalars() (gas: 2228063)\n[PASS] testOverlongSurrogateOutOfRangeTruncatedAndStraySequencesRevert() (gas: 455694)\n[PASS] testUTF8BoundaryCodePointsRoundTripWithoutChangingWords() (gas: 429370)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 100.04ms (158.89ms CPU time)\n\nRan 8 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testFuzzRealPoolConservesValueAcrossChangingETHPrices(uint96,uint96) (runs: 256, μ: 2434906, ~: 2435042)\nLogs:\n  Bound result 9000000000007472\n  Bound result 9000000000013218\n\n[PASS] testNativeSettlementResetsCurrencySyncedByPoolHook() (gas: 1765268)\n[PASS] testRealNativePoolSettlesAndPaysIntake() (gas: 1773710)\n[PASS] testRealPoolRollbackWhenIntakeReverts() (gas: 1291640)\n[PASS] testRealPoolSlippageRevertsPoolStateETHAndTokens() (gas: 676046)\n[PASS] testRealUninitializedAndEmptyPoolsRevert() (gas: 914620)\n[PASS] testRealWrappedNativePoolsInBothCurrencyOrders() (gas: 3532886)\n[PASS] testRealWrappedPoolIntakeFailureRollsBackDepositAndPriceAndCanRetry() (gas: 3365869)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 113.31ms (122.53ms CPU time)\n\nRan 9 tests for test/IntakeAccountAdversarial.t.sol:IntakeAccountAdversarialTest\n[PASS] testCannotCreateAnAccountWithNoUser() (gas: 145021)\n[PASS] testExecuteBubblesRevertAndDoesNotSpendETHOrChangeTarget() (gas: 306678)\n[PASS] testFreshPriceIsSpentWhileUserDonationsRemainRecoverable() (gas: 289113)\n[PASS] testFuzzUnderpullRevertsAllDependencyEffects(uint96) (runs: 1000, μ: 273263, ~: 273206)\nLogs:\n  Bound result 8147039366\n\n[PASS] testHubCannotWithdrawEvenThoughItCanAsk() (gas: 36744)\n[PASS] testOldUnlimitedAllowanceIsReducedBeforeAnIntakeCanOverpull() (gas: 440145)\n[PASS] testRefundAndNetBalanceIncreaseAreRejected() (gas: 590375)\n[PASS] testUserAndStrangerCannotInvokeHubPaymentPath() (gas: 98974)\n[PASS] testZeroFirstApprovalIsSupportedAndCleanupFailureIsAtomic() (gas: 1200566)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 125.41ms (125.79ms CPU time)\n\nRan 36 tests for test/WebsiteBuyer.t.sol:WebsiteBuyerTest\n[PASS] testAUserCanContinueOnlyTheirOwnProjectThroughAccount() (gas: 2408126)\n[PASS] testAccountCannotExecuteReentrantlyEvenWhenUserIsIntake() (gas: 1392539)\n[PASS] testActionVersionCanBeUpdated() (gas: 1312158)\n[PASS] testAtLeastHalfIMDIsBoughtEvenWhenLivePriceFalls() (gas: 1518776)\n[PASS] testCallbacksRejectWrongSenderUnknownAndDuplicateInBothDirections() (gas: 2668745)\n[PASS] testCallerSlippageAndLiveIntakePriceAreBothFloors() (gas: 493240)\n[PASS] testDeadlineBoundaryAndChangedETHPrice() (gas: 1211153)\n[PASS] testDefaultBodyIncludesCallerOwnerAndCannotNameSomeoneElse() (gas: 1254401)\n[PASS] testDifferentUsersHaveSeparateStablePayers() (gas: 2954334)\n[PASS] testDuplicateIdFromSameIntakeRevertsEntireSecondPurchase() (gas: 2188763)\n[PASS] testFeeOnTransferAndFalseReturningTokensRevert() (gas: 704461)\n[PASS] testFuzzConservationAcrossPurchases(uint96,uint8) (runs: 256, μ: 3278086, ~: 2948400)\nLogs:\n  Bound result 19500000000000000663\n  Bound result 1\n\n[PASS] testFuzzInjectedJSONSyntaxCannotReachSwap(uint8,uint16) (runs: 256, μ: 115048, ~: 114269)\nLogs:\n  Bound result 7\n  Bound result 20\n\n[PASS] testFuzzValidWordsHaveExactBody(bytes32,uint16) (runs: 256, μ: 369263, ~: 160222)\nLogs:\n  Bound result 946\n\n[PASS] testIntakeMigrationPreservesOldCallbacksAndNamespacesIdenticalIds() (gas: 4094911)\n[PASS] testIntakeRevertAndIncorrectPullRevertSwapAndAccountCreation() (gas: 2131259)\n[PASS] testInvalidAdminSettingsAndMissingCode() (gas: 407149)\n[PASS] testLivePriceIncreaseAndCallerMaximum() (gas: 1326499)\n[PASS] testMaximumURIStoresUnder200000Gas() (gas: 1507137)\n[PASS] testOnlyHubCanAskAndOnlyUserCanRecoverAccountFunds() (gas: 1382170)\n[PASS] testOnlyOwnerCanConfigureAndOwnershipUsesTwoSteps() (gas: 339295)\n[PASS] testOversizedURIIsNotStoredAndMissingCallbackNeverBlocksPurchases() (gas: 1870422)\n[PASS] testOwnerContractCannotRetargetConfigurationDuringSwap() (gas: 1461858)\n[PASS] testOwnerTemplatesHaveExactBodiesAndCanBeDisabled() (gas: 1402266)\n[PASS] testPurchasePaysExactLivePriceAndRetainsSurplus() (gas: 1328786)\n[PASS] testReentrancyDuringSwapAndIntakeCannotBuyAgain() (gas: 1837575)\n[PASS] testRefusalAndFailureSpendThePriceWithoutRefund() (gas: 2045844)\n[PASS] testResultStoresHashURIAndEmitsUnderStipend() (gas: 1423818)\n[PASS] testShortSwapCannotUseOldSurplusAndRollsBackEverything() (gas: 382514)\n[PASS] testSwapRejectsPartialInputOverspendAndInvalidCallbacks() (gas: 1641680)\n[PASS] testTokenMigrationRequiresNewPoolAndUnsupportedDecimalsFail() (gas: 2878756)\n[PASS] testTokenWithNoReturnValuesIsPaidWithNoResidualApproval() (gas: 1656303)\n[PASS] testUnpricedActionAndUnconfiguredPoolAndPause() (gas: 1643790)\n[PASS] testWordsRejectInjectionControlsEmptyOversizeAndMalformedUTF8() (gas: 263278)\n[PASS] testWrappedETHRouteSpendsFullPayment() (gas: 2050722)\n[PASS] testWrongPaymentsExpiredDeadlineAndStaleConfiguration() (gas: 256509)\nSuite result: ok. 36 passed; 0 failed; 0 skipped; finished in 125.41ms (248.78ms CPU time)\n\nRan 13 tests for test/WebsiteBuyerEdges.t.sol:WebsiteBuyerEdgesTest\n[PASS] testAllConfigurationSettersInvalidateAnAlreadyReviewedQuote() (gas: 1973294)\n[PASS] testContractCallerOwnsPayerAndBodyRegardlessOfTransactionOrigin() (gas: 1616283)\n[PASS] testFuzzEveryDependencyMustHaveCode(uint8) (runs: 1000, μ: 171547, ~: 153492)\n[PASS] testFuzzFreshOutputMustSatisfyAllThreeFloors(uint96,uint96,uint96,uint96) (runs: 1000, μ: 1601446, ~: 1664832)\nLogs:\n  Bound result 20141\n  Bound result 19466\n  Bound result 159\n  Bound result 21153\n\n[PASS] testFuzzInvalidFailureStatusCannotSettle(uint8) (runs: 1000, μ: 1379741, ~: 1379750)\n[PASS] testMaximumSignedInputAndOutputDoNotTruncate() (gas: 1355693)\n[PASS] testMaximumTemplateAndWordLengthsProduceParseableBoundedJSON() (gas: 3802470)\n[PASS] testOneWeiETHPriceAndExactMinimumOutput() (gas: 1256678)\n[PASS] testPoolAndTemplateConfigurationBoundariesAreAtomic() (gas: 1799657)\n[PASS] testPurchaseEventContainsExactReceiptAndBodyCommitment() (gas: 1247882)\n[PASS] testShortWrapperMintCannotBeSubsidizedByOldBalance() (gas: 953417)\n[PASS] testWrappedRouteCannotSpendPreexistingWrappedTokensOrForcedETH() (gas: 2084102)\n[PASS] testWrongSettlementReceiptRevertsNativeAndWrappedPayments() (gas: 2779068)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 125.41ms (339.26ms CPU time)\n\nRan 2 tests for test/WebsiteBuyerInvariant.t.sol:WebsiteBuyerInvariantTest\n[PASS]\nWebsiteBuyerInvariantTest invariants:\n[PASS] invariant_EveryWeiAndTokenIsAccountedFor\n[PASS] invariant_OnlyAuthorizedConfigurationAndPayersPersist\n[PASS] invariant_RequestIdentityAndTerminalResultsAreImmutable\n WebsiteBuyerInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------------+-----------------+-------+---------+----------╮\n| Contract            | Selector        | Calls | Reverts | Discards |\n+====================================================================+\n| WebsiteBuyerHandler | buy             | 3266  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| WebsiteBuyerHandler | configure       | 1560  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| WebsiteBuyerHandler | depositETH      | 1660  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| WebsiteBuyerHandler | donate          | 1531  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| WebsiteBuyerHandler | forgeCallback   | 1698  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| WebsiteBuyerHandler | rejectPurchase  | 1658  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| WebsiteBuyerHandler | settle          | 1739  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| WebsiteBuyerHandler | unauthorized    | 1604  | 0       | 0        |\n|---------------------+-----------------+-------+---------+----------|\n| WebsiteBuyerHandler | withdrawAccount | 1668  | 0       | 0        |\n╰---------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 2\n  Bound result 0\n  Bound result 3\n  Bound result 999999999999999998\n  Bound result 1313373042\n  Bound result 8747\n  Bound result 1791\n  Bound result 244\n  Bound result 60\n  Bound result 4044\n  Bound result 19961\n  Bound result 506953408300740336\n  Bound result 3433\n  Bound result 4596\n  Bound result 20\n  Bound result 23878\n  Bound result 3335\n  Bound result 0\n  Bound result 777\n  Bound result 3\n  Bound result 1112063\n  Bound result 11839\n  Bound result 2000\n  Bound result 9999999999999900001\n  Bound result 26\n  Bound result 224\n  Bound result 889\n  Bound result 12764\n  Bound result 3333550953188870533\n  Bound result 64720898512492176\n  Bound result 887220\n  Bound result 1865\n  Bound result 22032\n  Bound result 6006\n  Bound result 14586\n  Bound result 3697461681681343507\n  Bound result 9\n  Bound result 573\n  Bound result 18128\n  Bound result 7349\n  Bound result 0\n  Bound result 23031\n  Bound result 705\n  Bound result 4264337593543950334\n  Bound result 2659067023790259388\n  Bound result 193\n  Bound result 952892140609068328\n  Bound result 2162\n\n[PASS] testHandlerExercisesMigrationsFailuresAndRecovery() (gas: 5637627)\nLogs:\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 2\n  Bound result 0\n  Bound result 3\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000\n  Bound result 10000000000000000000\n  Bound result 3000000000000000000\n  Bound result 0\n  Bound result 0\n  Bound result 1000000000000000000\n  Bound result 2000000000000000000\n  Bound result 1000000000000000000\n  Bound result 3000000000000000000\n  Bound result 0\n  Bound result 0\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 11.92s (11.92s CPU time)\n\nRan 8 test suites in 11.92s (12.55s CPU time): 77 tests passed, 0 failed, 0 skipped (77 total tests)\n","passed":true},{"durationMs":55,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IntakeAccount.ask(address,bytes32,bytes,(address,bytes4),bytes4,address,uint256)\",\"IntakeAccount.execute(address,uint256,bytes)\",\"IntakeAccount.receive()\",\"WebsiteBuyer.acceptOwnership()\",\"WebsiteBuyer.buyWebsite(string,(uint256,uint256,uint256,uint256,uint256))\",\"WebsiteBuyer.onIntakeFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"WebsiteBuyer.onIntakeResult(bytes32,bytes32,string)\",\"WebsiteBuyer.renounceOwnership()\",\"WebsiteBuyer.setAction(bytes32)\",\"WebsiteBuyer.setETHPrice(uint256)\",\"WebsiteBuyer.setIMD(address)\",\"WebsiteBuyer.setIntake(address)\",\"WebsiteBuyer.setPaused(bool)\",\"WebsiteBuyer.setPool(address,address,uint24,int24,address,bytes)\",\"WebsiteBuyer.setTemplate(uint256,string,string,bool,bool)\",\"WebsiteBuyer.transferOwnership(address)\",\"WebsiteBuyer.unlockCallback(bytes)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":12,\"LICENSE\":23,\"README.md\":101,\"SECURITY.md\":32,\"foundry.toml\":17,\"remappings.txt\":4,\"src/IntakeAccount.sol\":65,\"src/WebsiteBuyer.sol\":417,\"src/interfaces/IIntake.sol\":20,\"src/libraries/JsonText.sol\":37,\"test/Deployment.t.sol\":61,\"test/IntakeAccountAdversarial.t.sol\":255,\"test/JsonTextProperties.t.sol\":139,\"test/README.md\":32,\"test/UniswapV4Integration.t.sol\":280,\"test/WebsiteBuyer.t.sol\":692,\"test/WebsiteBuyerEdges.t.sol\":312,\"test/WebsiteBuyerInvariant.t.sol\":76,\"test/helpers/BuyerTestBase.sol\":61,\"test/invariant/WebsiteBuyerHandler.sol\":441,\"test/mocks/MockIntake.sol\":125,\"test/mocks/MockPoolManager.sol\":101,\"test/mocks/MockToken.sol\":78},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"d56e1566dcd1e259a701876ab94f3475e8897dfe5b945283c28db0b0f249acab","verifiedTreeHash":"729f06c90934efa3beecf8c3b96fedc25d692731","verifierVersion":"0.1.0+c3fba9eb"}]}