{"assessments":[],"deployments":[],"fuzz":[],"identity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"interpretation":"Records acceptance and evidence. Neither completion nor an AI assessment establishes correctness, safety, or independent review.","jobId":"e854ea48-ee65-40eb-b029-f441e6b3b1a2","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"3ceaf9349a7d594a1637d41f4fa77fdbc58eb2a3147aea34982eeb2ac6530782","dependsOn":["audit_imported_code"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","tools":[]},"key":"adapt_contract_project","kind":"code","role":"implement","skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","state":"accepted"},{"acceptedSubmissionHash":"8ee33e6580c5429ae41cc85e1d7321a5f06d98489bf05147c72746c8c9543c29","dependsOn":["adapt_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":"58752bcf933a2de7f167d41a0a82a526b84c7992a805719af6d591b257a9c7fb","dependsOn":["adapt_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":"fbd6d8dca3b3e36da4988aed6b51fb9731a3ae982323849f11d862170deb993c","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","tools":[]},"key":"audit_imported_code","kind":"code","role":"review","skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","state":"accepted"},{"acceptedSubmissionHash":"74bfb6ab79cd86df654b238de244542138b12e79cdb2fa3b00928a62867987fa","dependsOn":["adapt_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":"9f454609f217145e2f3fe475621dcaba2f49b4a54d9afbaf48d89aa9eadcf3ca","dependsOn":["adapt_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":"77e81b91628cc68ccdf690c58729928058f730ca70503143cec615c5e441a1ff","dependsOn":["adapt_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":"71a290b6fab631c16498495281b0b447b37e09dd2bd631a2270e54b39e182063","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"4c4be87d9c5f52419b087764f993b87215d390c42f29454275e00b753c0e3c0f","dependsOn":["adapt_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":"Launch the Conditionals contract in this repository, src/Conditionals.sol:Conditionals, on Robinhood Chain without changing its logic. It is a conditional prediction market in IMD settled by the IdentityMD oracle. Constructor (address owner = $owner, IERC20 imd = 0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127, IIntake intake = 0xa43e6f75ee006411f79ac1c84120606c2330de82, address oracleSigner = 0x5598aa9146215bc13eb26f2c692ad1461fd32982, address feeRecipient = 0x63bcf2d1483c7628d7d53704ff9c8199dc28d907); it makes no external calls. A market asks 'if CONDITION, will OUTCOME?' and has two worlds (the condition happens / does not), each with a YES and a NO pool of IMD. The creator deposits 1 IMD (two 0.5 IMD oracle questions). After resolveTime anyone calls requestResolution: the contract pays the Intake with requestWithFailure for oracle.request@oracle-1 questions (panel 5, quorum 4, bool, panel evidence, window pinned to one Ethereum block, block.number - 10, since block.number on Robinhood Chain is the Ethereum block number) and stores keccak256 of each canonical question document. onOracleResult (selector 0x510379c7) accepts an answer only from the Intake for its own pending request, with the stored questionHash, answerType bool, panelSize and quorum at least the market's, agreed >= quorum, not expired, and an EIP-712 signature (domain IdentityMD Oracle, version 2) from the market's oracle signer. onFailure (0x5a0df71e) only clears the ask. Settlement: the world that did not happen is refunded; in the world that happened the winning side splits the pot minus 3% (2% burned to 0x000000000000000000000000000000000000dEaD because IMD reverts on transfers to zero, 1% to feeRecipient); a one-sided world is refunded with no fee; with no usable answer 30 days after resolveTime anyone can void and every stake is refunded. The owner can only propose an oracle configuration for future markets behind a 3 day delay and set the fee recipient; it cannot touch stakes, answers or open markets. No token, no pool, no proxy, no pause. The test suite (unit, fuzz, invariant, and a live-API question-hash vector) must keep passing.","parentJobId":null,"planHash":"d05b1e65ac13b70b9f9e10333a33ceffbc247d08da0b7b7d20b74a32fcb37bcb","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"e854ea48-ee65-40eb-b029-f441e6b3b1a2","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2278-launch-conditionals-contract-repository"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51432","feedbackHash":"00aeed8981651df037be920adb0242ce60018ea34468c5a1d7950a05d89d83f5","nodeKey":"adapt_contract_project","submissionHash":"36b28f99cb08768d8c81ac5c7d14194892516e33048ed9aa25e0e91eafcb0f4c","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52174","feedbackHash":"ebc6d796327451f9d528b17df7c640ae1ceafaed5429df799035cd487ca0768f","nodeKey":"adapt_contract_project","submissionHash":"3ceaf9349a7d594a1637d41f4fa77fdbc58eb2a3147aea34982eeb2ac6530782","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51885","feedbackHash":"eae2e8f5d7371b5059e6916aa7cb719a92cdfbf32694d2232c9af3ad55313367","nodeKey":"audit_economics","submissionHash":"8ee33e6580c5429ae41cc85e1d7321a5f06d98489bf05147c72746c8c9543c29","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51886","feedbackHash":"f611f5d7ba8e837206a7925c5685cf97d6f6b698a45963556f9c23fd28abe738","nodeKey":"audit_flow","submissionHash":"58752bcf933a2de7f167d41a0a82a526b84c7992a805719af6d591b257a9c7fb","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51239","feedbackHash":"e450188dddf4b329418669204410a8b962a01993b3a4b8e135b98e5745f5d3ce","nodeKey":"audit_imported_code","submissionHash":"fbd6d8dca3b3e36da4988aed6b51fb9731a3ae982323849f11d862170deb993c","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51493","feedbackHash":"719d782f393456d8da022b93b8b84671e200a41a9422310260208e06abf02930","nodeKey":"audit_judge","submissionHash":"74bfb6ab79cd86df654b238de244542138b12e79cdb2fa3b00928a62867987fa","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51144","feedbackHash":"2fcd5ac1a138f66dd44091008337f6d778ecfa7021be2d5e49be70bb3b3b7054","nodeKey":"audit_judge","submissionHash":"fe3b1670d415104693333f562f5e8bdd8d0c9aa993fb971eb98fea22cce92c76","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51214","feedbackHash":"c76a8c5f118fbd31faeca5749a391db5544ef7f863cd96b7b496fdf0154c445d","nodeKey":"audit_math","submissionHash":"9f454609f217145e2f3fe475621dcaba2f49b4a54d9afbaf48d89aa9eadcf3ca","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52686","feedbackHash":"43a50c6ed970dfec6b54dbfd9966188a0d506665b8034f7939b09337f32420e8","nodeKey":"audit_permissions","submissionHash":"77e81b91628cc68ccdf690c58729928058f730ca70503143cec615c5e441a1ff","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51305","feedbackHash":"240f7a0014f06122881a0e28080081022f4bade3f88531f2b2f8430104245e31","nodeKey":"manifest","submissionHash":"71a290b6fab631c16498495281b0b447b37e09dd2bd631a2270e54b39e182063","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51496","feedbackHash":"0f4029d60e6339d1e64a9d25f62c19c9897280986ad9eb01f752f91ec854f150","nodeKey":"manifest","submissionHash":"7b5adfb2a99404ae5a78cab54fdc19f921e41106626c06edd391b422e3dc5d5b","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51285","feedbackHash":"f9c274c9addcb37692aefa96977340b899d089af6ec4c5edb0d1cebcd4663f37","nodeKey":"write_foundry_tests","submissionHash":"f9f3cc8db45a7335433a83194101476a7667448049a97ee57e076796ae87d040","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52480","feedbackHash":"755762ab5d88565de0909403a3d4aba171eb8bfe4d791483c67dc2abcebf238b","nodeKey":"write_foundry_tests","submissionHash":"4c4be87d9c5f52419b087764f993b87215d390c42f29454275e00b753c0e3c0f","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"c4d1b01af85bba8410095432535a26ec0ba55cd50a4901c113600e12ea2c2750","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"d5f2ae7596e0af154dda50a09e0e24befa46e8e25fd7e3f2b43b35b29a1a8cb4","device":"03f15d1296244279","findings":[],"hash":"36b28f99cb08768d8c81ac5c7d14194892516e33048ed9aa25e0e91eafcb0f4c","nodeId":"58c4e798-e54f-4df0-9f31-38d48876dd08","outcome":"completed","summary":"All checks pass. Final state:\n\n**Outcome**: the contract is launch-ready as it was (constructor with explicit owner, no external calls, no proxy or pause, runtime 18.6 KB), so the only source changes are fixes for the five audit findings, each of which reproduced.\n\n**Fixes in `src/Conditionals.sol`**\n- **World 1 settled to NO (high).** Both questions are now always asked and both answers are required. The condition picks the world and the outcome picks the winning side in either world. The audit's proof file fails on the old code and passes now.\n- **Void race (low).** `voidMarket` refuses while an unanswered question has a live ask younger than one day, so an answer on its way cannot be voided away. New `voidable` view.\n- **Hard-coded action id (low).** `OracleConfig` carries a non-zero `action`, snapshotted per market and rotatable on the 3 day path.\n- **ECDSA-only signer (low).** Verification uses `SignatureChecker`, so an ERC-1271 signer registry works. Each market still keeps its own signer, because accepting the current signer would let the owner settle open markets, which the brief forbids. This is noted in ADAPTATION.md.\n- **Uncapped deposit (low).** `createMarket` takes a `maxBudget` and reverts `BudgetTooHigh` when a newly activated price charges more.\n\n**Tests**: 56 pass with the verifier's fuzz seed, including new tests per fix, an ERC-1271 registry mock, and a new `test/OracleConformance.t.sol` that reproduces the protocol's digest vector and accepts the vector key's signature as delivered. The live-API question-hash vector is unchanged.\n\n**Docs and frontend**: README and ADAPTATION.md describe each change and why. The frontend's mirror logic, create call and generated ABI were updated to match, but the frontend was not built here since it has no installed dependencies. The fork test needs an RPC and was not run.","treeHash":"b61ddcda8379efd634988ddedbfd9879761847cf","usage":{"cachedInputTokens":2594651,"inputTokens":642,"model":"claude-fable-5-1","outputTokens":43236,"runtime":"claude","turns":36,"wallClockMs":764694}},{"artifacts":[],"attempt":1,"bundleHash":"311dee5c42499efa6526d535376a2c3aec208e2400a28bebebdde89a0d6bdb2a","device":"b4f6137e7c93a2b3","findings":[],"hash":"3ceaf9349a7d594a1637d41f4fa77fdbc58eb2a3147aea34982eeb2ac6530782","nodeId":"58c4e798-e54f-4df0-9f31-38d48876dd08","outcome":"completed","summary":"Done. The reopened finding reproduced and is fixed, the five advisory findings I judged real are fixed, and the full suite passes.\n\n**Reopened finding (fee on a dusted one-sided world).** The proof failed on the starting tree exactly as reported. `_settle` in `src/Conditionals.sol` now caps the fee at the losing pool, split 2:1 between the burn and the fee recipient as before. In the reviewer's scenario the whale gets back exactly 1000 IMD and the fee is the 0.001 IMD dust. One caveat you should know about: the proof file is self-contradictory. Its line 104 asserts the defective payout of 970.00097 IMD and its line 109 asserts at least 1000 IMD, so no fix can pass it. On the fixed tree it fails at line 104 with the corrected amount. I answered that finding `disputed` for that reason only, with the fix in place and three tests of my own covering the scenario, including a fuzz that a winner never nets less than staked.\n\n**Advisory findings fixed, each with tests:**\n- Third-party pushes no longer block stakers. The burn, fee and budget return try the transfer and, if IMD refuses it, credit `owed` for later collection through `withdrawOwed`.\n- Asks read the Intake's live price and pay it when lower than the snapshot. A higher price still reverts and voids, as the README documented, so a caller never pays above the snapshot. The price view lives in a separate interface so the proofs' mocks still compile.\n- Attestations issued more than 5 minutes ahead of the chain clock are refused.\n- A pending oracle configuration lapses 7 days after its delay. The ABI pin was not added, and the reason is in ADAPTATION.md.\n- The `onFailure` comment now matches the immediate-retry behaviour the README and tests encode.\n\n**Left alone, answered as disputed:** rounding dust, `claim` reverting for a non-staker after settling, and the fee recipient being read at settlement. Each reproduced, and the reasons are in the responses file and ADAPTATION.md.\n\n**Checks:** `forge build` succeeds and `forge test` with the verifier's fuzz seed passes 66 tests, up from 55. Runtime size is 19,654 bytes. The frontend payout estimate, demo settlement and fee copy mirror the cap, and the ABI was regenerated. The audit's older proof no longer compiles because it predates the accepted `maxBudget` argument. It is not held against this round and is noted in the responses. The fork test still needs the network and was not run.","treeHash":"0c1b5f5e90ed97c376d659c9acb2406e1205d6d0","usage":{"cachedInputTokens":2477347,"inputTokens":610,"model":"claude-fable-5-1","outputTokens":45809,"runtime":"claude","turns":53,"wallClockMs":665701}},{"artifacts":[],"attempt":1,"bundleHash":"382c843c12dfe9ba3bf644250fcf6d68fcfbdd3e0b112544713301e6ada2754c","device":"b0b4e7bbc84f9d80","findings":[],"hash":"4c4be87d9c5f52419b087764f993b87215d390c42f29454275e00b753c0e3c0f","nodeId":"02a4edbc-3522-4a09-9c9f-476983bd4863","outcome":"completed","summary":"I fixed my two tests that the latest implementation broke, and extended the invariant suite to cover what the implementer added. `forge build` and `forge test` pass with the verifier's seed (`0x454e…2db6`): 101 tests, none failing or skipped. Only `test/ConditionalsAdversarial.t.sol` and `test/ConditionalsLifecycleInvariant.t.sol` changed.\n\n**Why two tests failed.** After my last round, the implementer changed `src/Conditionals.sol` in response to a review finding. The 3% fee is now capped at what the losing side staked, split 2:1 between the burn address and the fee recipient. So a winner never gets back less than they staked. Two of my tests still asserted the old rule, 3% on the whole pot:\n- **`test_lopsidedWinnersPayTheFeeOnTheirOwnStake`** is now `test_lopsidedWinnersAreMadeWholeByTheFeeCap`. With 100 IMD against 1 IMD, the winner gets exactly 100 IMD back. The burn address gets ⅔ of the 1 IMD and the fee recipient the rest, the contract ends empty, and the loser cannot claim.\n- **`testFuzz_winnersSplitTheNetPotWithBoundedDust`** now works out the capped fee itself. It also checks that the fee is never more than the losing pool or 3% of the pot, and that each winner gets back at least their stake.\n\n**New test, `test_feeCapBoundary`.** When the losing side staked exactly 3% of the pot, the normal 2%/1% split applies. When it staked less (2 IMD of 99), the cap applies and the whole 2 IMD goes to fees. While writing it I found that 1 wei under 3% doesn't trigger the cap, because the 2% and 1% are each rounded down. That's harmless, so the test uses a clearly smaller losing pool.\n\n**Invariant suite** (random call sequences):\n- **New actions:** the handler can now block the burn address or either fee recipient on the mock IMD. It can also call `withdrawOwed`, the new function that pays out fees a blocked recipient couldn't receive at settlement.\n- **Solvency:** the check that the contract holds at least what it owes now counts that withheld IMD too.\n- **Fee accounting:** every burned or fee amount is either delivered or still held for its recipient, never lost or paid twice. The burn-to-fee 2:1 check now allows for the capped split's rounding, up to 2 wei per market.\n- **New per-trader checks:** a settled market never pays a trader less than their winning or refunded stake, and `withdrawOwed` pays exactly what was owed.\n\nI found no new defect with a concrete failing input, so I wrote no `.imd-findings.json`. One small point I didn't report: if IMD ever blocked the burn address, the withheld 2% would stay in the contract, since nobody can call `withdrawOwed` as that address. It still can't be spent, so it is effectively burned. The implementer's own tests for the other changes (the `issuedAt` check, the config activation window, the live price, blocked parties) are in `test/Conditionals.t.sol`. I didn't modify them.","treeHash":"9f5e907d69c7d39afa56f1d34e106f7bfc2e9c71","usage":{"cachedInputTokens":1050954,"inputTokens":32,"model":"claude-opus-5-5","outputTokens":12926,"runtime":"claude","turns":17,"wallClockMs":472634}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"dd3018ab6b18e7bc","findings":[{"citation":"resolved","description":"Area: execution trace / first principles (value flow from settle to claim). The fee base in _settle is the full pot of the world that happened, i.e. the winners' own stakes plus the losers'. When the losing pool is smaller than 3/97 of the winning pool (about 3.1%), netPot is below the winners' total stake and every winner receives less IMD than they put in while the oracle confirmed their prediction. The brief does say 'the winning side splits the pot minus 3%', so this matches the written specification; it is reported because the code header and README present the fee as taken from winnings, and a market with lopsided pools silently makes winning a loss. It is not an attack vector (a staker on the thin side cannot profit from it) and no funds are misrouted; it is a design property the author should confirm or change (e.g. fee on the losers' pool only, or a cap so winners never receive less than their stake).","line":603,"path":"src/Conditionals.sol","reproduction":"Fixture: createMarket; alice stake(id, 0, 0, 100e18) (world 0 YES); bob stake(id, 0, 1, 1e18) (world 0 NO); oracle answers condition=true, outcome=true; alice claim(id). Expected by a reader of 'winners split the pot': alice receives at least 100e18. Actual: pot=101e18, burned=2.02e18, builderFee=1.01e18, netPot=97.97e18, alice receives 97.97e18 (a loss of 2.03e18 on a correct YES). Verified with test/scratch/Probe.t.sol::test_probe_winnerLosesAgainstThinOpposition on this tree.","severity":"info","snippet":"            burned = pot * BURN_BPS / BPS;\n            builderFee = pot * BUILDER_BPS / BPS;\n            m.netPot = pot - burned - builderFee;","title":"3% fee is taken on the whole pot, so a correct side facing a thin opposing pool is paid less than it staked"},{"citation":"resolved","description":"Area: execution trace across transactions (request -> failure -> retry). onFailure deletes the ask and decrements openAsks[q] to zero. _askable (line 531: `return m.openAsks[q] == 0 || block.timestamp >= uint256(m.lastAskAt[q]) + RETRY_DELAY;`) then returns true immediately because openAsks[q] == 0, regardless of lastAskAt. The NatSpec on onFailure and on the retry comment of requestResolution ('a retry, allowed RETRY_DELAY after the last ask') describe a one-day wait that the code does not enforce after a failure. Consequence: a question the wording screen deterministically refuses (status 1, body_refused) can be re-asked by any caller as often as the Intake fails it, each costing the caller 0.5 IMD with no chance of a different result; the existing test test_failureThenRetryPaidByCaller actually asserts the immediate re-ask, so the tests encode the behaviour and the comments encode the opposite. No third party loses funds (the caller pays for their own retries), so this is a documentation/behaviour mismatch to resolve one way or the other, not a loss.","line":415,"path":"src/Conditionals.sol","reproduction":"Fixture: createMarket; warp to resolveTime; requestResolution(id) (both asks sent, budget spent); intake.fail(ask0, 1, 'body_refused'); in the same block askable(id, 0) returns true and requestResolution(id) succeeds, pulling 0.5 IMD from the caller; fail it again and askable(id, 0) is true again at once. Expected per the comment at line 415: askable(id,0) false until lastAskAt[0] + 1 day. Verified with test/scratch/Probe.t.sol::test_probe_failureRetryImmediate.","severity":"info","snippet":"    ///         Unsigned, so it only clears the ask: the question can be asked again after RETRY_DELAY.","title":"onFailure documents a RETRY_DELAY before re-asking, but a failed question is re-askable in the same block"},{"citation":"resolved","description":"Area: periphery (the Intake boundary) x execution trace. _ask passes config.price, frozen at createMarket, as the amount of requestWithFailure (lines 567-575). The Intake refuses an amount below its current priceOf(action, imd) (the mock does the same: `require(amount >= PRICE, 'PriceNotMet')`). The snapshot is deliberate (the brief wants the owner unable to touch open markets), but the consequence is not handled: once the Intake's price for oracle.request@oracle-1 moves above a market's snapshot, requestResolution(id) reverts on the first _ask, for the first ask and every retry, until voidAfter. No answer can ever arrive, so the only exit is voidMarket after resolveTime + 30 days, refunding every stake and returning the 1 IMD budget. Nobody loses principal, but every open market created before the change (up to 730 days of horizon) can no longer settle, and the frontend/README promise of resolution 'from resolveTime' is broken for all of them. The owner cannot repair it (config changes apply to future markets only). A mitigation that keeps the owner away from stakes and answers is to let _ask top up to the live price from the caller (requestResolution already lets the caller pay retries), or to read priceOf at ask time and have the market budget cover it, with the shortfall paid by the caller.","line":553,"path":"src/Conditionals.sol","reproduction":"State: market created with the default config (price 0.5 IMD), stakes placed, block.timestamp >= resolveTime. Intake.priceOf(oracle.request@oracle-1, IMD) is now 0.6 IMD (the Intake owner repriced). Call requestResolution(id). Expected: two asks sent (the caller or budget covering the difference). Actual: _ask calls requestWithFailure(action, body, cb, onFailure, imd, 0.5e18); the Intake reverts for the short payment, the whole call reverts, and this repeats for every caller and every block until block.timestamp > voidAfter; askable(id, q) keeps returning true while nothing can be asked. The market is then voided by anyone and never settled.","severity":"low","snippet":"        uint256 price = config.price;","title":"A market pays the Intake its snapshotted price; if the live oracle.request price rises above it, requestResolution reverts for every such market and they can only be voided"}],"hash":"58752bcf933a2de7f167d41a0a82a526b84c7992a805719af6d591b257a9c7fb","nodeId":"17223cba-36da-44b2-b579-1e4a66ad725e","outcome":"completed","summary":"The review is complete and `.imd-findings.json` is written at the repository root. The working tree has no other changes.\n\n**Outcome.** The control flow of `src/Conditionals.sol` holds up under the Execution Trace, Periphery and First Principles checks. I found no loss-of-funds, misrouting or permanent-lock defect. The existing suite passes on this tree.\n\n**What I traced.** Every one of the fourteen state-changing entry points has a coverage row, plus three invariant rows.\n\n- **Ask path**: the budget covers exactly the first two asks, retries are paid by the caller, the approval is zeroed after the Intake call, and asks are keyed by Intake address and request id so a rotated Intake cannot answer an older market.\n- **Callback path**: the question hash, bool type, panel bounds, expiry and the EIP-712 digest all gate the answer before any state is kept. A late or duplicate answer is ignored rather than reverted. The live signer on Robinhood Chain is an EOA, so the ECDSA branch is what runs.\n- **Settle and void**: a market can always leave the Open state. Settlement needs both answers, and void becomes possible at most one day after `voidAfter`. The live IMD bytecode exposes no pause or blacklist selectors, so the settlement transfers cannot be blocked.\n\n**Findings reported.**\n\n1. **Info.** The 3% fee is taken on the whole pot. When the losing pool is under about 3.1% of the winning pool, a correct side is paid less than it staked. This matches the brief's wording but contradicts the \"fee from winnings\" reading of the header and README. Reproduced with concrete numbers.\n2. **Info.** The `onFailure` comment says a failed question waits `RETRY_DELAY` before it can be re-asked, but a cleared ask is re-askable in the same block. The tests encode the immediate behaviour, the comments the opposite.\n3. **Low.** Each market pays the Intake its snapshotted price. If the live `oracle.request` price rises above it, `requestResolution` reverts for that market for its whole life, so every such market can only be voided and never settled. No principal is lost.\n\n**Not reached.** I did not run the live fork test or the `POST /requests/check` wording screen against the baked question text. The on-chain question document format matches the protocol reference's canonical member order, and a live-API hash vector in the suite pins it.","treeHash":null,"usage":{"cachedInputTokens":1761031,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":35380,"runtime":"claude","turns":36,"wallClockMs":599425}},{"artifacts":[],"attempt":1,"bundleHash":"4bd926bf071e6d090ac9ca9f7877da28358d7be1f3cda343a08413bb471bc76c","device":"4576f670afa0131d","findings":[],"hash":"71a290b6fab631c16498495281b0b447b37e09dd2bd631a2270e54b39e182063","nodeId":"b906e6d7-c2b8-42b1-86f8-1bdcdaffb755","outcome":"completed","summary":"Updated `launch.json` for the revised Conditionals contract. Only that file changed among tracked files.\n\n**What I found.** The contract was revised after the manifest was last written. The constructor is unchanged, so the contract entry and its five arguments stay as they were: `$owner`, the Robinhood Chain IMD, Intake version 2, the oracle signer, and the brief's fee recipient. The notes, however, described the earlier implementation and missed behaviour the revision added.\n\n**What changed in the notes.** They now describe the accepted tree:\n- The fee is capped at the losing side's stake, so a winner never nets less than they staked.\n- A proposed oracle configuration must be activated between 3 and 10 days after the proposal, or it lapses.\n- Attestations are refused when issuedAt is more than 5 minutes ahead of the chain clock.\n- Asks charge the Intake's live price when it is below the market's snapshot.\n- After a failure callback the question can be asked again at once, paid by the caller.\n- Burn, fee or budget transfers IMD refuses are credited to the recipient, who collects them with the permissionless withdrawOwed call.\n\n**Checks run.**\n- The manifest has exactly kind, contracts and notes, kind is `evm_contracts`, every hex value has even length, and the notes are 2874 characters.\n- Every function and constant named in the notes exists in the source with the stated value.\n- `forge build --offline` compiled, and `forge test --offline` with the verifier's fuzz seed passed all 66 tests across 4 suites.","treeHash":"d10ba8582faab098f316e87ed2c432019f810b6a","usage":{"cachedInputTokens":670151,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":5635,"runtime":"claude","turns":10,"wallClockMs":142447}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"74979c69cdd583ad","findings":[],"hash":"74bfb6ab79cd86df654b238de244542138b12e79cdb2fa3b00928a62867987fa","nodeId":"f25d6cb4-098b-46ac-897e-69901b922e62","outcome":"completed","summary":"I've written `.imd-findings.json` with no findings and a `holds` row for all 15 entry points. Every finding from the last round is either fixed or settled. The full suite passes: `forge test` ran 101 tests with 0 failures. I checked each fix by reading the revised code. I did not run my old proofs as copies under `test/scratch/`; the checks below come from the code and the suite.\n\n**The medium finding, b57733eb (dust counter-stake defeats the fee-free refund): fixed.**\n- `_settle` now caps the fee at the losing pool, split 2:1 between burn and fee recipient.\n- In the proof's scenario the whale gets back exactly 1000 IMD. Nobody can make money by dusting a one-sided world any more.\n- The author marked it disputed because of the proof itself, and they're right. The proof asserted the defective amounts and also `paid >= 1000 IMD`, which can't both hold, so no fix could pass it. I'm not raising it again.\n\n**The low findings: all fixed.**\n- **3425133b (blocked receiver bricks settle and void):** the burn, the fee and the budget return now go through `_push`. If IMD refuses a transfer, the amount is recorded as owed and the recipient collects it later with `withdrawOwed()`. A blocked third party can no longer block stakers.\n- **d2cd6ea6 (asks always pay the snapshotted price):** each ask now pays the lower of the Intake's live price and the market's snapshot. If the live price is higher, the market still voids at `voidAfter`, which the README documents.\n- **931492ce (future `issuedAt` accepted):** `onOracleResult` now refuses an attestation issued more than 5 minutes ahead.\n- **17efe772 (creator races `activateOracleConfig`):** a proposed configuration now expires 7 days after its 3-day delay ends. So any configuration a creator can meet was announced at most 10 days earlier. The author didn't add a configuration pin to `createMarket`; that only exposes the creator's own 1 IMD budget, so I accept it.\n\n**The info notes:**\n- **427f0607 (retry-delay comment):** fixed; the comment now matches the code.\n- **f2b2f269 (rounding dust):** accepted as documented. At most a few wei per market stay in the contract.\n- **1b5229b4 (`claim` with nothing to claim rolls back the settlement):** accepted. It costs only gas, and `settle()` is the call for that case.\n- **167b3d1e (fee recipient read at settlement):** accepted. The fee amount is fixed and stakes are untouched, and after the fee cap the recipient has no profitable move.\n\n**New issues:** none. The fixes add no regression I could find. Slither's reentrancy warnings on `_settle` don't hold: every caller is `nonReentrant` and the market's state is written before any transfer.","treeHash":null,"usage":{"cachedInputTokens":233206,"inputTokens":8,"model":"claude-opus-5-5","outputTokens":2319,"runtime":"claude","turns":4,"wallClockMs":129276}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"8a05ec1ea94411ea","findings":[{"citation":"resolved","description":"Seam: access x economics x asymmetry (Trust Gap). The brief guarantees that 'a one-sided world is refunded with no fee'. The fee branch is selected by a strict yes == 0 || no == 0 test and then charges FEE_BPS of the entire pot, so the guarantee flips from 'no fee' to 'full 3% fee' on the first wei of counter-stake. Anyone can stake MIN_STAKE (1e15 = 0.001 IMD) on the empty side before closeTime. The sole-side stakers then lose 3% of their own pot; 2% is burned and 1% goes to feeRecipient. The privileged beneficiary of the fee (feeRecipient, set by the owner) has a direct incentive to dust every one-sided world: it pays 0.001 IMD and receives 1% of the opposing pool, and the dust stake still wins the whole net pot if the outcome goes its way. The asymmetry is that the fee is a function of the pot, not of what the losing side actually risked, so a counterparty of 0.001 IMD is treated exactly like one of 100 IMD. Against the code's own promise this is a broken guarantee under a cheap, permissionless condition; the loss is bounded to 3% of the affected pool.","line":599,"path":"src/Conditionals.sol","reproduction":"State: fixture (test/utils/Fixture.sol), market created by creator. alice stakes 100 IMD on world 0 / YES. Without any other stake: condition=true, outcome=true -> alice claims exactly 100 IMD, totalBurned 0 (verified). Now the attack: bob (or feeRecipient itself) stakes 0.001 IMD (1e15) on world 0 / NO before closeTime. Same answers condition=true, outcome=true. settle: pot = 100.001 IMD, burned = 2.00002 IMD, builderFee = 1.00001 IMD, netPot = 97.00097 IMD. alice.claim() returns 97.00097 IMD -> alice loses 2.99903 IMD; feeRecipient receives 1.00001 IMD for 0.001 IMD spent; 2.00002 IMD is burned. Expected per the brief: a world whose only real counterparty is dust is refunded without fee, or the fee is bounded by what the losing side risked. Actual: full 3% of the pot is taken. Measured with a scratch Foundry test (logs: alice got 97000970000000000000, builder got 1000010000000000000, burned 2000020000000000000).","severity":"medium","snippet":"        if (yes == 0 || no == 0) {\n            m.worldRefunded = true;\n        } else {\n            uint256 pot = yes + no;\n            burned = pot * BURN_BPS / BPS;\n            builderFee = pot * BUILDER_BPS / BPS;\n            m.netPot = pot - burned - builderFee;","title":"A MIN_STAKE counter-stake defeats the fee-free one-sided refund: 0.001 IMD imposes a 3% fee on the whole pot and pays the fee recipient 1% of it"},{"citation":"resolved","description":"Seam: access x economics (Trust Gap). Each market snapshots OracleConfig at creation and _ask always pays exactly config.price (approve price, amount = price) with no reading of the Intake's live price and no way for the retry payer to top up. The owner's only remedy, proposeOracleConfig/activateOracleConfig, is deliberately scoped to future markets, so no actor can repair an open market. Markets may have resolveTime up to 730 days out while the oracle-consumer reference states the price is owner-settable at the Intake ('a moved price must not need a redeploy'). If Intake.priceOf(oracle.request, IMD) rises above 0.5 IMD before a market resolves, either requestWithFailure reverts on the short amount (then requestResolution reverts forever for that market), or the Intake accepts the pull and the plane refuses the request with short_payment (status 1), spending the budget and every caller-paid retry. In both cases the market cannot resolve: it voids 30 days after resolveTime, the stakes are refunded, and the creator's 1 IMD budget is spent for nothing (in the refuse case). Liveness of resolution depends on an external parameter the contract never reads.","line":553,"path":"src/Conditionals.sol","reproduction":"State: market id created at price 0.5 IMD (budget 1 IMD), alice 10 IMD on world0/YES, bob 10 IMD on world0/NO. Warp to resolveTime. Replace the intake's code with one that requires amount >= 0.6 IMD (vm.etch, modelling a price change at the Intake). c.requestResolution(id) reverts with the intake's price error (verified: revert 'PriceNotMet' in the scratch test). No call sequence by owner, creator or trader can make the market ask again: askable stays true but every requestResolution reverts; proposeOracleConfig cannot touch m.oracle. Warp to resolveTime + 30 days + 1: voidable(id) == true (verified) and voidMarket refunds all stakes; the market the traders paid for never resolves. If the live Intake instead accepts the short pull, the plane returns status 1 short_payment through onFailure and the 1 IMD budget is gone before the void.","severity":"low","snippet":"        uint256 price = config.price;\n        uint256 windowBlock = block.number - WINDOW_DEPTH;\n\n        if (m.budget >= price) {\n            m.budget -= price;\n        } else {\n            imd.safeTransferFrom(msg.sender, address(this), price);\n        }","title":"Intake price rising above a market's snapshotted price strands every open market: requestResolution reverts or the ask is refused, the budget is lost and the market can only void"},{"citation":"resolved","description":"Seam: access x asymmetry (Trust Gap, race amplifier). The author recognised the race between an owner's activateOracleConfig and an in-flight createMarket and added maxBudget, but it covers only one of six configuration fields. The signer (who decides every answer), the intake (who is paid and who may call back), the action id and the panel/quorum (how strong the answer must be) can all change under a pending createMarket. A pending config also has no expiry: it may sit past its 3-day delay for months and be activated in the same block a market is created. The creator deposits 1 IMD and commits traders to a market whose oracle they never saw; stakers can read getMarket(id).oracle before staking, so the loss is the creator's budget and the market's usefulness rather than stakes.","line":332,"path":"src/Conditionals.sol","reproduction":"Owner proposes _config() with signer = 0xBEEF and quorum = 2 (price unchanged, 0.5 IMD). Warp 3 days; in one block the owner calls activateOracleConfig() and then creator calls createMarket(CONDITION, OUTCOME, RULES, close, resolve, maxBudget = 1 ether), having read signer == original and quorum == 4 before sending. maxBudget check passes because the price is unchanged. getMarket(id).oracle.signer == 0xBEEF and .quorum == 2 (verified in a scratch test). Expected: the creator can pin the configuration it agreed to (for example a config hash or fields beyond price in createMarket, or a pending-config expiry). Actual: only the price is pinned.","severity":"low","snippet":"        m.oracle = oracleConfig;\n        m.budget = uint256(m.oracle.price) * 2;\n        if (m.budget > maxBudget) revert BudgetTooHigh(m.budget, maxBudget);","title":"createMarket binds a market to whatever oracle config is active at mining time; maxBudget guards only the price, so a creator racing activateOracleConfig gets an unseen signer, intake or panel"},{"citation":"resolved","description":"Asymmetry between settle() and claim(): claim() runs _settle (which pays burn, fee and returns the budget to the creator) and then reverts when the caller personally has nothing to claim, so the settlement and the budget return are rolled back. A creator with no stake who follows the natural path 'claim what the market owes me' gets a revert and keeps paying gas until it discovers settle(). No funds are at risk; settle() and any staker's claim() complete the same settlement.","line":450,"path":"src/Conditionals.sol","reproduction":"Market with alice 10 IMD world0/YES and bob 10 IMD world0/NO; both answers delivered (settleable(id) == true). vm.prank(creator); c.claim(id) reverts NothingToClaim and getMarket(id).status is still Open afterwards (verified). Expected: either the settlement persists (return 0 instead of reverting after settling) or the function refuses before settling. Actual: settlement executes and is reverted.","severity":"info","snippet":"        amount = _payout(marketId, m, msg.sender);\n        if (amount == 0) revert NothingToClaim();","title":"claim() auto-settles and then reverts NothingToClaim for a caller without stakes, undoing the settlement: the creator cannot recover the budget through claim"}],"hash":"77e81b91628cc68ccdf690c58729928058f730ca70503143cec615c5e441a1ff","nodeId":"c45c7efa-bd49-4882-b368-8d7b6682da88","outcome":"completed","summary":"The review is complete. `.imd-findings.json` holds four findings and a coverage row for all 14 entry points plus three invariants; every snippet and line was verified against the tree, the scratch tests were removed, and the working tree is otherwise untouched.\n\n**Outcome:** the permission model holds. The owner is confined to proposing a delayed oracle config for future markets and setting the fee recipient. Oracle callbacks are bound to the market's own intake and live ask, pinned by question hash, gated on panel, quorum, expiry and the contract-domain EIP-712 signature, and the approval to the Intake never exceeds one price. Forged or replayed deliveries revert or are ignored without moving value. No critical or high defects were found, so no proof files were attached.\n\n**Findings reported**\n\n- **Medium, settle fee branch.** A 0.001 IMD counter-stake on an empty side converts the promised fee-free one-sided refund into a 3% fee on the whole pot. With 100 IMD alone on YES the sole staker loses 2.99903 IMD, 2.00002 IMD is burned and the fee recipient collects 1.00001 IMD for a 0.001 IMD outlay. The dust bettor also keeps the full net pot if the outcome goes its way.\n- **Low, requestResolution.** Each market pays a snapshotted price and nobody can repair an open market. If the Intake's live price rises above it, every ask reverts or is refused as a short payment, the budget is spent, and the market can only void after 30 days.\n- **Low, createMarket and activateOracleConfig.** The creator's only pin on the active config is maxBudget. A createMarket mined after an activation in the same block binds to a new signer, intake, action or quorum the creator never saw, and a pending config has no expiry.\n- **Info, claim.** A caller with nothing to claim triggers settlement and then reverts, rolling the settlement back. The creator cannot recover the budget through claim and must call settle.\n\n**Coverage.** Every listed entry point has a verdict. Ten hold, four carry findings, none were left unreached. Static-analysis leads (uninitialised locals, calls in loop, timestamp use) were checked and not promoted: the locals default to zero by design, the loop has two fixed iterations, and the timestamp comparisons implement the documented windows.","treeHash":null,"usage":{"cachedInputTokens":1238292,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":36934,"runtime":"claude","turns":26,"wallClockMs":592101}},{"artifacts":[],"attempt":1,"bundleHash":"43d232eb7f72e930164087b1c7d7269011d454930ed30ecc051577a4eaafc03c","device":"b3e0a28d5c3d1a63","findings":[],"hash":"7b5adfb2a99404ae5a78cab54fdc19f921e41106626c06edd391b422e3dc5d5b","nodeId":"b906e6d7-c2b8-42b1-86f8-1bdcdaffb755","outcome":"completed","summary":"Created [launch.json](/home/imd2/.identitymd/work/e854ea48-ee65-40eb-b029-f441e6b3b1a2/b906e6d7-c2b8-42b1-86f8-1bdcdaffb755/launch.json) with `Conditionals`, `$owner`, and the exact approved addresses.\n\nManifest structure and compiled ABI checks passed. `forge build` and `forge test` passed with the supplied fuzz seed; the optional live fork was disabled.\n\nOnly `launch.json` is changed for submission.","treeHash":"af79b9ae3f81ef90d1b26037a9a60fd106aed304","usage":{"cachedInputTokens":893824,"inputTokens":81722,"model":"gpt-6-astra","outputTokens":4369,"runtime":"codex","turns":4,"wallClockMs":123892}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"aca5d7170d77c721","findings":[{"citation":"resolved","description":"Economic Security / Invariant: the fee is charged on `pot = yes + no`, i.e. on the winners' own principal, not on what they won from the losers. The only protection is the one-sided check `yes == 0 || no == 0`, which any address bypasses with a single MIN_STAKE (0.001 IMD) stake on the empty side. With YES = 1000 IMD and NO = 0.001 IMD in the world that happened, YES wins and `_payout` returns 1000 * 970.00097 / 1000 = 970.00097 IMD: the correct side loses 29.999 IMD, 20 IMD is burned and 10 IMD goes to feeRecipient. Whoever holds feeRecipient has a direct incentive to dust every one-sided world (spend 0.001 IMD, receive 1% of the pot); anyone else can burn 2% of others' principal for 0.001 IMD. More generally every market whose odds are more lopsided than ~32:1 pays its winners less than they staked (YES 100 / NO 2: payout 98.94 < 100). The stated guarantee 'a world with stakes on only one side has no counterparty and is refunded with no fee' is therefore trivially defeatable, and the invariant 'the winning side does not lose money' does not hold. Fix: charge the fee on the losing pool only (netPot = winners + losers * 0.97), or exempt stakes up to the counter-pool from the fee.","line":599,"path":"src/Conditionals.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {Conditionals} from \"src/Conditionals.sol\";\nimport {IIntake} from \"src/interfaces/IIntake.sol\";\nimport {IOracleAttestation} from \"src/interfaces/IOracleAttestation.sol\";\n\ncontract ScratchIMD is ERC20 {\n    constructor() ERC20(\"Identity.md\", \"IMD\") {}\n    function mint(address to, uint256 amount) external { _mint(to, amount); }\n}\n\ncontract ScratchIntake is IIntake {\n    struct Req { IIntake.Callback cb; bytes4 onFailure; }\n    uint256 public nonce;\n    mapping(bytes32 => Req) public reqs;\n    bytes32[] public ids;\n    function requestWithFailure(bytes32, bytes calldata, Callback calldata cb, bytes4 onFailure, address asset, uint256 amount)\n        external returns (bytes32 id)\n    {\n        IERC20(asset).transferFrom(msg.sender, address(0xBEEF), amount);\n        id = keccak256(abi.encode(address(this), ++nonce));\n        reqs[id] = Req(cb, onFailure);\n        ids.push(id);\n    }\n    function complete(bytes32 id, IOracleAttestation.Attestation memory a, bytes memory sig) external returns (bool ok) {\n        (ok,) = reqs[id].cb.target.call{gas: 200_000}(abi.encodeWithSelector(reqs[id].cb.selector, id, a, sig));\n    }\n}\n\n/// @dev A world whose empty side receives one MIN_STAKE dust stake is no longer \"one-sided\", so the 3% fee is\n///      charged on the whole pot, including the winners' own principal. The winners net-lose by winning.\ncontract FeeOnPrincipalTest is Test {\n    ScratchIMD imd;\n    ScratchIntake intake;\n    Conditionals c;\n    uint256 signerKey = 0xA11CE;\n    address signer;\n    address owner = makeAddr(\"owner\");\n    address builder = makeAddr(\"builder\");\n    address creator = makeAddr(\"creator\");\n    address alice = makeAddr(\"alice\");\n    address griefer = makeAddr(\"griefer\");\n    uint64 closeTime;\n    uint64 resolveTime;\n\n    function setUp() public {\n        vm.warp(1_790_000_000);\n        vm.roll(26_000_000);\n        signer = vm.addr(signerKey);\n        imd = new ScratchIMD();\n        intake = new ScratchIntake();\n        c = new Conditionals(owner, IERC20(address(imd)), IIntake(address(intake)), signer, builder);\n        address[3] memory users = [creator, alice, griefer];\n        for (uint256 i; i < 3; ++i) {\n            imd.mint(users[i], 10_000 ether);\n            vm.prank(users[i]);\n            imd.approve(address(c), type(uint256).max);\n        }\n        closeTime = uint64(block.timestamp + 7 days);\n        resolveTime = uint64(block.timestamp + 14 days);\n    }\n\n    function _digest(IOracleAttestation.Attestation memory a) internal view returns (bytes32) {\n        bytes32 domain = keccak256(abi.encode(\n            keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n            keccak256(\"IdentityMD Oracle\"), keccak256(\"2\"), block.chainid, address(c)));\n        bytes32 structHash = keccak256(bytes.concat(\n            abi.encode(c.ATTESTATION_TYPEHASH(), a.requestId, a.chainId, a.questionHash, a.answerType, keccak256(a.answer), a.figure, a.fromBlock),\n            abi.encode(a.toBlock, a.blockHash, a.panelJobId, a.panelSize, a.quorum, a.agreed, a.issuedAt, a.expiresAt)));\n        return keccak256(abi.encodePacked(\"\\x19\\x01\", domain, structHash));\n    }\n\n    function _answer(uint256 id, bytes32 rid, uint8 q, uint256 windowBlock, bool value) internal {\n        IOracleAttestation.Attestation memory a;\n        a.requestId = bytes32(bytes16(rid));\n        a.chainId = 1;\n        a.questionHash = keccak256(bytes(c.questionDocument(id, q, windowBlock)));\n        a.answer = abi.encode(value);\n        a.fromBlock = uint64(windowBlock);\n        a.toBlock = uint64(windowBlock);\n        a.panelSize = 5; a.quorum = 4; a.agreed = 5;\n        a.issuedAt = uint64(block.timestamp);\n        a.expiresAt = uint64(block.timestamp + 1 days);\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerKey, _digest(a));\n        assertTrue(intake.complete(rid, a, abi.encodePacked(r, s, v)), \"not delivered\");\n    }\n\n    function test_dustStakeOnEmptySideMakesWinnersLosePrincipal() public {\n        vm.prank(creator);\n        uint256 id = c.createMarket(\"Condition happens\", \"Outcome happens\", \"Use public sources.\", closeTime, resolveTime, 1 ether);\n\n        // Alice is alone on YES in world 0 with 1000 IMD. If nobody else stakes she is refunded in full.\n        vm.prank(alice);\n        c.stake(id, 0, 0, 1_000 ether);\n        // A griefer (or the fee recipient itself) puts the minimum stake on the empty NO side.\n        uint256 dust = c.MIN_STAKE();\n        vm.prank(griefer);\n        c.stake(id, 0, 1, dust);\n\n        vm.warp(resolveTime);\n        uint256 windowBlock = block.number - c.WINDOW_DEPTH();\n        c.requestResolution(id);\n        _answer(id, intake.ids(0), 0, windowBlock, true); // condition happened -> world 0\n        _answer(id, intake.ids(1), 1, windowBlock, true); // outcome YES -> Alice is right\n\n        uint256 before = imd.balanceOf(alice);\n        vm.prank(alice);\n        uint256 paid = c.claim(id);\n        assertEq(imd.balanceOf(alice) - before, paid);\n\n        emit log_named_uint(\"alice staked      \", 1_000 ether);\n        emit log_named_uint(\"alice paid out    \", paid);\n        emit log_named_uint(\"builder fee       \", imd.balanceOf(builder));\n        emit log_named_uint(\"burned            \", imd.balanceOf(0x000000000000000000000000000000000000dEaD));\n\n        // Alice was right, had 0.001 IMD of counterparty, and still gets back less than she staked.\n        assertGe(paid, 1_000 ether, \"winner must not lose principal\");\n    }\n}","reproduction":"1. creator: createMarket(cond, outcome, rules, close, resolve, 1e18). 2. alice: stake(id, 0, YES, 1000e18). 3. griefer (or feeRecipient): stake(id, 0, NO, 1e15). 4. warp to resolveTime, requestResolution(id); the Intake delivers condition=true and outcome=true (alice is right). 5. alice: claim(id). Expected: alice receives at least her 1000e18 back (she had 0.001 IMD of counterparty). Actual: claim returns 970000970000000000000 (970.00097 IMD); 20.00002 IMD is sent to 0xdEaD and 10.00001 IMD to feeRecipient. The proof test test/scratch/FeeOnPrincipal.t.sol fails with 'winner must not lose principal: 970000970000000000000 < 1000000000000000000000'.","severity":"medium","snippet":"        if (yes == 0 || no == 0) {\n            m.worldRefunded = true;\n        } else {\n            uint256 pot = yes + no;\n            burned = pot * BURN_BPS / BPS;\n            builderFee = pot * BUILDER_BPS / BPS;","title":"3% fee is taken from the whole pot, so winners net-lose when the counter-pool is small; one MIN_STAKE dust stake defeats the no-counterparty refund"},{"citation":"resolved","description":"Economic Security 'break dependencies' / Flow Gap (periphery x first principles): `_settle` transfers to BURN_ADDRESS and `feeRecipient`, and `_returnBudget` (called from `_settle` and `voidMarket`) transfers the unspent budget to `creator`, all inside the same transaction that every staker depends on. The live IMD on Robinhood Chain (0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127) is not a plain ERC-20: its bytecode is a 'BridgedFP' LayerZero OFT exposing `blocked(address)`, `transfersEnabled()`/`enableTransfers()` and a Uniswap v4 transfer gate (`setV4Config`, revert strings 'BridgedFP: transfers not enabled' and 'BridgedFP: v4 transfer not appro[ved]'). If the token refuses the fee recipient, every settlement with a fee reverts and, because the market is settleable, `voidMarket` reverts with NotVoidable: the stakers' pot is stuck until the owner rotates feeRecipient (and permanently if 0xdEaD were refused, since BURN_ADDRESS is a constant). If the token refuses the creator while the 1 IMD budget is unspent (nobody called requestResolution), `voidMarket` reverts on the budget push and no staker can ever be refunded; nobody can fix that. Fix: make the budget return and the fee pull-based (credit `feeOwed[feeRecipient]` / `budgetOwed[creator]` and let them withdraw), or wrap the third-party pushes so a failed push does not block stakers' claims.","line":612,"path":"src/Conditionals.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {Conditionals} from \"src/Conditionals.sol\";\nimport {IIntake} from \"src/interfaces/IIntake.sol\";\nimport {IOracleAttestation} from \"src/interfaces/IOracleAttestation.sol\";\n\n/// @dev Like the live IMD (a \"BridgedFP\" LayerZero OFT exposing `blocked(address)`, `transfersEnabled()` and a\n///      v4 transfer gate): the token owner can make transfers to or from one address revert.\ncontract BlocklistIMD is ERC20 {\n    mapping(address => bool) public blocked;\n    constructor() ERC20(\"Identity.md\", \"IMD\") {}\n    function mint(address to, uint256 amount) external { _mint(to, amount); }\n    function block_(address a, bool b) external { blocked[a] = b; }\n    function _update(address from, address to, uint256 value) internal override {\n        require(!blocked[from] && !blocked[to], \"blocked\");\n        super._update(from, to, value);\n    }\n}\n\ncontract ScratchIntake2 is IIntake {\n    struct Req { IIntake.Callback cb; bytes4 onFailure; }\n    uint256 public nonce;\n    mapping(bytes32 => Req) public reqs;\n    bytes32[] public ids;\n    function requestWithFailure(bytes32, bytes calldata, Callback calldata cb, bytes4 onFailure, address asset, uint256 amount)\n        external returns (bytes32 id)\n    {\n        IERC20(asset).transferFrom(msg.sender, address(0xBEEF), amount);\n        id = keccak256(abi.encode(address(this), ++nonce));\n        reqs[id] = Req(cb, onFailure);\n        ids.push(id);\n    }\n    function complete(bytes32 id, IOracleAttestation.Attestation memory a, bytes memory sig) external returns (bool ok) {\n        (ok,) = reqs[id].cb.target.call{gas: 200_000}(abi.encodeWithSelector(reqs[id].cb.selector, id, a, sig));\n    }\n}\n\n/// @dev Settlement and void push IMD to third parties (fee recipient, creator). A receiver the token refuses\n///      makes settle, claim and void revert for every staker. Passes once those payments are pull-based (or\n///      tolerate a failed push), so stakers can always claim their own money.\ncontract PushTransferBricksTest is Test {\n    BlocklistIMD imd;\n    ScratchIntake2 intake;\n    Conditionals c;\n    uint256 signerKey = 0xA11CE;\n    address signer;\n    address owner = makeAddr(\"owner\");\n    address builder = makeAddr(\"builder\");\n    address creator = makeAddr(\"creator\");\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n    uint64 closeTime;\n    uint64 resolveTime;\n\n    function setUp() public {\n        vm.warp(1_790_000_000);\n        vm.roll(26_000_000);\n        signer = vm.addr(signerKey);\n        imd = new BlocklistIMD();\n        intake = new ScratchIntake2();\n        c = new Conditionals(owner, IERC20(address(imd)), IIntake(address(intake)), signer, builder);\n        address[3] memory users = [creator, alice, bob];\n        for (uint256 i; i < 3; ++i) {\n            imd.mint(users[i], 10_000 ether);\n            vm.prank(users[i]);\n            imd.approve(address(c), type(uint256).max);\n        }\n        closeTime = uint64(block.timestamp + 7 days);\n        resolveTime = uint64(block.timestamp + 14 days);\n    }\n\n    function _digest(IOracleAttestation.Attestation memory a) internal view returns (bytes32) {\n        bytes32 domain = keccak256(abi.encode(\n            keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n            keccak256(\"IdentityMD Oracle\"), keccak256(\"2\"), block.chainid, address(c)));\n        bytes32 structHash = keccak256(bytes.concat(\n            abi.encode(c.ATTESTATION_TYPEHASH(), a.requestId, a.chainId, a.questionHash, a.answerType, keccak256(a.answer), a.figure, a.fromBlock),\n            abi.encode(a.toBlock, a.blockHash, a.panelJobId, a.panelSize, a.quorum, a.agreed, a.issuedAt, a.expiresAt)));\n        return keccak256(abi.encodePacked(\"\\x19\\x01\", domain, structHash));\n    }\n\n    function _answer(uint256 id, bytes32 rid, uint8 q, uint256 windowBlock, bool value) internal {\n        IOracleAttestation.Attestation memory a;\n        a.requestId = bytes32(bytes16(rid));\n        a.chainId = 1;\n        a.questionHash = keccak256(bytes(c.questionDocument(id, q, windowBlock)));\n        a.answer = abi.encode(value);\n        a.fromBlock = uint64(windowBlock);\n        a.toBlock = uint64(windowBlock);\n        a.panelSize = 5; a.quorum = 4; a.agreed = 5;\n        a.issuedAt = uint64(block.timestamp);\n        a.expiresAt = uint64(block.timestamp + 1 days);\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerKey, _digest(a));\n        assertTrue(intake.complete(rid, a, abi.encodePacked(r, s, v)), \"not delivered\");\n    }\n\n    function _market() internal returns (uint256 id) {\n        vm.prank(creator);\n        id = c.createMarket(\"Condition happens\", \"Outcome happens\", \"Use public sources.\", closeTime, resolveTime, 1 ether);\n        vm.prank(alice);\n        c.stake(id, 0, 0, 100 ether);\n        vm.prank(bob);\n        c.stake(id, 0, 1, 100 ether);\n    }\n\n    /// Both answers are in, then the token refuses the fee recipient. Alice (the winner) must still be able to\n    /// collect her payout; today settle/claim revert on the fee push and void is refused (the market is settleable).\n    function test_winnerCanClaimWhenTheFeeRecipientIsBlocked() public {\n        uint256 id = _market();\n        vm.warp(resolveTime);\n        uint256 wb = block.number - c.WINDOW_DEPTH();\n        c.requestResolution(id);\n        _answer(id, intake.ids(0), 0, wb, true);\n        _answer(id, intake.ids(1), 1, wb, true);\n        imd.block_(builder, true);\n\n        uint256 before = imd.balanceOf(alice);\n        vm.prank(alice);\n        c.claim(id);\n        assertGt(imd.balanceOf(alice), before, \"winner could not claim\");\n    }\n\n    /// No answer ever came and the creator (who still has the 1 IMD budget to receive) is blocked by the token.\n    /// Stakers must still get their refunds after voidAfter; today voidMarket reverts on the budget push.\n    function test_stakersCanBeRefundedWhenTheCreatorIsBlocked() public {\n        uint256 id = _market();\n        imd.block_(creator, true);\n        vm.warp(resolveTime + 31 days);\n        c.voidMarket(id);\n        vm.prank(alice);\n        assertEq(c.claim(id), 100 ether);\n    }\n}","reproduction":"Token: an ERC-20 whose `_update` reverts for a blocked address (as the live IMD's owner can configure). Case A: alice stake(id,0,YES,100e18), bob stake(id,0,NO,100e18); both oracle answers delivered (true,true); the token blocks feeRecipient. settle(id) and alice's claim(id) revert with the token's error; voidMarket(id) at resolveTime+31 days reverts NotVoidable. Expected: alice can collect her payout; actual: 200 IMD stuck until the owner changes feeRecipient. Case B: same stakes, nobody asks the oracle, the token blocks creator; at resolveTime+31 days voidMarket(id) reverts on the 1 IMD budget push and claim(id) reverts NotSettleable. Expected: every stake refunded; actual: 200 IMD stuck permanently. Proof: test/scratch/PushTransferBricks.t.sol, both tests fail with 'blocked'.","severity":"low","snippet":"        if (burned != 0) imd.safeTransfer(BURN_ADDRESS, burned);\n        if (builderFee != 0) imd.safeTransfer(feeRecipient, builderFee);\n        _returnBudget(marketId, m);","title":"Settlement and void push IMD to the fee recipient and creator; a receiver the token refuses bricks settle, claim and void for every staker"},{"citation":"resolved","description":"Economic Security (value leak at an external boundary): `_ask` passes `price = config.price` (0.5 IMD, frozen at market creation) as the Intake `amount`, and the Intake pulls and forwards exactly `amount` (nothing is escrowed or refunded; the repository's own MockIntake models this with `require(amount >= PRICE)` and a pull of `amount`). The contract never reads `priceOf(action, imd)` (IIntake.sol does not even declare it). If the oracle price moves down (for example to 0.25 IMD) every ask of every existing market overpays the difference: 0.5 IMD per market lost from the creator's budget that `_returnBudget` would otherwise refund, plus 0.25 IMD per caller-paid retry; the owner cannot repair existing markets because each keeps its config. (If the price moves up the ask reverts and the market voids after 30 days, which the README documents; the downward case is not documented and silently leaks.) Fix: read `priceOf` at ask time and pay `min(livePrice, config.price)`, refunding the rest of the budget.","line":567,"path":"src/Conditionals.sol","reproduction":"State: market created while oracleConfig.price = 0.5e18 (budget 1e18). The Intake's price for the action drops to 0.25e18 (Intake.priceOf returns 250000000000000000). Call requestResolution(id). Expected: 0.25e18 paid per question, 0.5e18 of the budget returned to the creator at settlement/void. Actual: `_ask` pays `amount = 0.5e18` twice, `m.budget` becomes 0 and `totalOracleSpend` records 1e18; the creator gets nothing back. With MockIntake: set PRICE to 0.25e18 and assert the payee received 1e18 rather than 0.5e18.","severity":"low","snippet":"        bytes32 requestId = config.intake\n            .requestWithFailure(\n                config.action,\n                body,\n                IIntake.Callback({target: address(this), selector: this.onOracleResult.selector}),\n                this.onFailure.selector,\n                address(imd),\n                price\n            );","title":"Asks always pay the market's snapshotted price as `amount`, so a lower live Intake price is overpaid from the creator's budget and by retriers"}],"hash":"8ee33e6580c5429ae41cc85e1d7321a5f06d98489bf05147c72746c8c9543c29","nodeId":"e1b2eec6-88bc-481c-ba8d-12d7cc43a37a","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds three substantiated findings plus a 17-row coverage record. Git shows no tracked files changed. Only the findings file and `test/scratch/` are new.\n\n**What I found**\n\n- **Medium, fee on principal.** The 3% fee at `src/Conditionals.sol:599` is taken from the whole pot, not from the winnings. The one-sided refund only triggers when a pool is exactly zero, so a single 0.001 IMD stake on the empty side converts a full refund into a 3% haircut on everyone's principal. With 1000 IMD on YES and 0.001 on NO, the correct side gets back 970 IMD. The fee recipient has a direct incentive to do this on every lopsided world. Proof test fails on current code.\n- **Low, push transfers can brick settlement and void.** Settlement pushes IMD to the fee recipient and the creator inside the transaction every staker depends on. The live IMD on Robinhood Chain is a \"BridgedFP\" LayerZero OFT with a `blocked(address)` list, a transfers switch and a v4 transfer gate, so a receiver-specific revert is possible. A blocked creator with an unspent budget makes void revert permanently, stranding all stakes. Proof test fails on current code with a blocklisting mock token.\n- **Low, overpayment on a lower oracle price.** Asks always pass the market's frozen 0.5 IMD as the Intake amount and never read the live price, so a price cut leaks the difference from the creator's budget and from every retrier.\n\n**Leads I closed as holding**\n\n- Robinhood Chain's `block.number` really is the Ethereum head. The RPC height is 93M, but Multicall3's `getBlockNumber()` on chain 4663 returned the same value as Ethereum's head, so the window-block pinning is sound.\n- The live oracle signer and fee recipient are EOAs, so the 200k callback stipend is not at risk from an ERC-1271 call.\n- Solvency and conservation hold across every writer, open-ask counts cannot wrap, and void and settle are mutually exclusive.\n\n**Coverage.** All 14 entry points have a row. Four are marked `finding`, the rest `holds`. Nothing was left unreached. The existing suite of 54 tests still passes.","treeHash":null,"usage":{"cachedInputTokens":1825560,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":52309,"runtime":"claude","turns":34,"wallClockMs":829284}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"dd2ee4882a1be950","findings":[{"citation":"resolved","description":"Boundary x invariant seam (Pashov Math Precision: round at sole-occupant boundary; Numerical Gap seam 3). The one-sided test is a strict zero check, so the smallest allowed stake (MIN_STAKE = 1e15, 0.001 IMD) on the empty side flips the world from the no-fee refund branch into the fee branch. The fee is then taken on the whole pot, which is almost entirely the winners own stakes, so the winning side ends with 97% of what it put in (the losing 0.001 IMD does not come close to covering the 3%). The README promise that a world with stakes on only one side is refunded with no fee is defeated by anyone for 0.001 IMD, and the designated feeRecipient has a repeatable, profitable move: stake 0.001 IMD on the empty side of every one-sided world and collect 1% of the pot (a 10,000x return on 1000 IMD pots). The owner can set feeRecipient to itself at any time, so this is also an owner-value-extraction path the brief says should not exist (the owner cannot touch stakes). Invariant broken: a staker who wins should never receive less than their stake when the counterparty is negligible. Fix options that keep the design: cap the fee at the losing pool (fee = min(3% of pot, losing pool)), or take the fee from the losing side only, or require a minimum counter-stake ratio before a world is treated as two-sided.","line":599,"path":"src/Conditionals.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {Conditionals} from \"src/Conditionals.sol\";\nimport {IIntake} from \"src/interfaces/IIntake.sol\";\nimport {IOracleAttestation} from \"src/interfaces/IOracleAttestation.sol\";\n\ncontract ScratchIMD is ERC20 {\n    constructor() ERC20(\"Identity.md\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\ncontract ScratchIntake is IIntake {\n    struct Request {\n        address target;\n        bytes4 selector;\n    }\n\n    uint256 public nonce;\n    mapping(bytes32 => Request) public requests;\n    bytes32[] public ids;\n\n    function requestWithFailure(bytes32, bytes calldata, Callback calldata callback, bytes4, address asset, uint256 amount)\n        external\n        returns (bytes32 requestId)\n    {\n        IERC20(asset).transferFrom(msg.sender, address(0xBEEF), amount);\n        requestId = keccak256(abi.encode(address(this), ++nonce));\n        requests[requestId] = Request(callback.target, callback.selector);\n        ids.push(requestId);\n    }\n\n    function complete(bytes32 requestId, IOracleAttestation.Attestation memory a, bytes memory sig)\n        external\n        returns (bool ok)\n    {\n        Request memory r = requests[requestId];\n        (ok,) = r.target.call{gas: 200_000}(abi.encodeWithSelector(r.selector, requestId, a, sig));\n    }\n}\n\n/// @dev A 0.001 IMD stake on the empty side of an otherwise one-sided world turns the fee-free refund into a 3% fee\n///      on the whole pot: the sole winner gets back less than they staked, and the fee recipient is paid 1% of the pot.\ncontract MinStakeFeeBoundaryTest is Test {\n    ScratchIMD imd;\n    ScratchIntake intake;\n    Conditionals c;\n\n    uint256 signerKey = 0xA11CE;\n    address signer;\n    address owner = makeAddr(\"owner\");\n    address feeRecipient = makeAddr(\"feeRecipient\");\n    address creator = makeAddr(\"creator\");\n    address whale = makeAddr(\"whale\");\n    address griefer = makeAddr(\"griefer\");\n\n    uint64 closeTime;\n    uint64 resolveTime;\n\n    function setUp() public {\n        vm.warp(1_790_000_000);\n        vm.roll(26_000_000);\n        signer = vm.addr(signerKey);\n        imd = new ScratchIMD();\n        intake = new ScratchIntake();\n        c = new Conditionals(owner, IERC20(address(imd)), IIntake(address(intake)), signer, feeRecipient);\n        address[3] memory users = [creator, whale, griefer];\n        for (uint256 i; i < 3; ++i) {\n            imd.mint(users[i], 10_000 ether);\n            vm.prank(users[i]);\n            imd.approve(address(c), type(uint256).max);\n        }\n        closeTime = uint64(block.timestamp + 7 days);\n        resolveTime = uint64(block.timestamp + 14 days);\n    }\n\n    function test_minimumCounterStakeMakesTheWinnerLoseAndPaysTheFeeRecipient() public {\n        vm.prank(creator);\n        uint256 id = c.createMarket(\"Condition happens\", \"Outcome happens\", \"Use public sources.\", closeTime, resolveTime, 1 ether);\n\n        // World 0 is one-sided: 1000 IMD on YES. By the README this world is \"refunded with no fee\" if it happens.\n        vm.prank(whale);\n        c.stake(id, 0, 0, 1_000 ether);\n        // The griefer (or the fee recipient itself) adds the minimum stake on NO.\n        uint256 minStake = c.MIN_STAKE();\n        vm.prank(griefer);\n        c.stake(id, 0, 1, minStake);\n\n        vm.warp(resolveTime);\n        c.requestResolution(id);\n        _answer(id, intake.ids(0), 0, true); // condition true -> world 0 happened\n        _answer(id, intake.ids(1), 1, true); // outcome true -> YES wins\n\n        vm.prank(whale);\n        uint256 paid = c.claim(id);\n\n        // Pot = 1000.001 IMD; burned = 20.00002, fee = 10.00001, netPot = 970.00097 IMD.\n        assertEq(paid, 970_000_970_000_000_000_000);\n        assertEq(imd.balanceOf(feeRecipient), 10_000_010_000_000_000_000);\n        assertEq(imd.balanceOf(0x000000000000000000000000000000000000dEaD), 20_000_020_000_000_000_000);\n\n        // The expected property: a winner whose only counterparty is 0.001 IMD must not get back less than they staked.\n        assertGe(paid, 1_000 ether, \"sole winner ends with less than their stake\");\n    }\n\n    function _answer(uint256 id, bytes32 requestId, uint8 q, bool value) internal {\n        IOracleAttestation.Attestation memory a;\n        a.requestId = bytes32(bytes16(keccak256(abi.encode(\"oracle\", requestId))));\n        a.chainId = 1;\n        a.questionHash = keccak256(bytes(c.questionDocument(id, q, block.number - c.WINDOW_DEPTH())));\n        a.answerType = 0;\n        a.answer = abi.encode(value);\n        a.fromBlock = uint64(block.number - 10);\n        a.toBlock = a.fromBlock;\n        a.panelSize = 5;\n        a.quorum = 4;\n        a.agreed = 5;\n        a.issuedAt = uint64(block.timestamp);\n        a.expiresAt = uint64(block.timestamp + 1 days);\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerKey, c.attestationDigest(a));\n        assertTrue(intake.complete(requestId, a, abi.encodePacked(r, s, v)), \"not delivered\");\n    }\n}","reproduction":"Market id 1, world 0: whale stakes 1000 IMD on YES (stake(1,0,0,1000e18)); griefer stakes 1e15 wei on NO (stake(1,0,1,1e15)). After resolveTime: requestResolution(1); oracle answers condition=true and outcome=true. settle/claim by whale. Expected: world 0 had effectively one side, whale gets back >= 1000 IMD (README: no fee for a one-sided world). Actual: pot = 1000.001e18; burned = pot*200/10000 = 20.00002e18; builderFee = 10.00001e18; netPot = 970.00097e18; whale claim() returns 970000970000000000000 (970.00097 IMD), a loss of 29.99903 IMD; feeRecipient receives 10000010000000000000 (10.00001 IMD) for a 0.001 IMD outlay by itself or anyone acting with it. Scratch test test/scratch/MinStakeFeeBoundary.t.sol fails with: sole winner ends with less than their stake: 970000970000000000000 < 1000000000000000000000.","severity":"medium","snippet":"        if (yes == 0 || no == 0) {\n            m.worldRefunded = true;\n        } else {\n            uint256 pot = yes + no;\n            burned = pot * BURN_BPS / BPS;\n            builderFee = pot * BUILDER_BPS / BPS;\n            m.netPot = pot - burned - builderFee;","title":"A 0.001 IMD counter-stake turns a fee-free one-sided world into a 3% fee on the whole pot: the sole winner nets less than staked and the fee recipient profits"},{"citation":"resolved","description":"Precision x invariant seam. Each winner is paid floor(mine * netPot / pool). The floors do not sum to netPot, and nothing (no sweep, no last-claimant remainder, no owner rescue) can ever move the remainder: the contract has no function that transfers IMD other than claim/settle/void paths keyed to stakes. The invariant that money in equals money out plus what is owed (invariant_conservation) holds only because the test treats the locked dust as held; the shortfall is up to (numberOfWinners - 1) wei per settled market, accumulating across markets. Impact is negligible per market but permanent. Minimal fix: pay the remainder to the last claimant of the winning side, or track and let feeRecipient sweep unclaimable dust once all winners have claimed.","line":636,"path":"src/Conditionals.sol","reproduction":"Market world 0: three traders A, B, C each stake 1e15 on YES, D stakes 1e15 on NO; condition true, outcome true. pot = 4e15, burned = 8e13, builderFee = 4e13, netPot = 3.88e15. Each winner: 1e15 * 3.88e15 / 3e15 = 1293333333333333 (floored). Sum of payouts = 3879999999999999 < netPot = 3880000000000000. Expected: all of netPot is distributable. Actual: 1 wei remains in the contract with no path to withdraw it; claimable() of every address is 0 and claim() reverts NothingToClaim.","severity":"low","snippet":"            if (mine != 0) amount += mine * m.netPot / m.pools[w][m.winningSide];","title":"Pro-rata payout rounds down per claimant and the dust is never sweepable, so IMD is locked in the contract forever"}],"hash":"9f454609f217145e2f3fe475621dcaba2f49b4a54d9afbaf48d89aa9eadcf3ca","nodeId":"dd8905e1-3a2a-4a98-8067-81bd5513cb33","outcome":"completed","summary":"Review complete. The findings file is written at `.imd-findings.json` with two findings and sixteen coverage rows, and no tracked file was changed.\n\n**What I checked** (Math Precision, Boundary, Numerical Gap guides): every division and multiplication in `_settle` and `_payout`, all downcasts, the `uint8` ask counters, the time-window edges of staking, asking, settling and voiding, the budget-versus-caller payment split, the attestation bounds in the callback, and the text canonicalisation rules against the protocol's own `OracleQuestion` library. The existing suite passes (54 tests including fuzz and invariants).\n\n**Findings**\n\n- **Medium, `_settle` one-sided check.** A 0.001 IMD stake on the empty side of an otherwise one-sided world flips it from the fee-free refund branch into the 3% fee on the whole pot. With 1000 IMD on YES and 0.001 IMD on NO, the sole YES winner receives 970.00097 IMD and loses about 30 IMD. The fee recipient collects 10.00001 IMD for that 0.001 IMD outlay, so the owner-chosen recipient has a repeatable extraction move, and the README's no-fee promise for one-sided worlds is defeated. A scratch Foundry proof fails on the current code and is embedded in the finding.\n- **Low, `_payout` rounding.** Per-claimant floor division leaves up to one wei per winner in the contract with no sweep path. Example with three equal winners: payouts sum to 3879999999999999 against a netPot of 3880000000000000.\n\n**Coverage.** All 14 entry points have a row. Twelve hold; `claim` and `settle` point at finding 1. One lead is recorded as unreached: whether the oracle plane trims leading, trailing or doubled spaces in market text before hashing could not be checked, because the quote endpoint requires a token and the free check endpoint returns no hash.","treeHash":null,"usage":{"cachedInputTokens":1347680,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":26021,"runtime":"claude","turns":26,"wallClockMs":531280}},{"artifacts":[],"attempt":1,"bundleHash":"eb35416fe49c80dc2df4abdd4cb3922aaf6d50448641fef548c162573166e96e","device":"7e929507773df661","findings":[{"description":"onOracleResult reimplements OracleAttestationConsumer._verifyAttestation instead of inheriting it, and drops one of its two window checks: the reference reverts AttestationNotYetValid when a.issuedAt > block.timestamp + ISSUED_AT_TOLERANCE (5 minutes). Conditionals checks only expiresAt. The signer is trusted, so the impact is limited to accepting a pre-dated (pre-signed) answer that the protocol's own consumer would refuse; the oracle-consumer reference says it wins over local reimplementations.","line":397,"path":"src/Conditionals.sol","reproduction":"Market created, requestResolution called, then deliver for the condition ask an attestation from the market's signer with every field correct except issuedAt = block.timestamp + 365 days. Expected (reference behaviour): callback reverts, ask stays pending. Actual: the answer is stored (getMarket(id).answers[0] == 1). Checked with a scratch test using the Fixture helpers: _attestation(...), a.issuedAt = now + 365 days, _deliver(...) returns true.","severity":"low","title":"An attestation with issuedAt in the future is accepted; the protocol's _verifyAttestation refuses it"},{"description":"As the brief specifies, the 3% fee is taken from the whole pot of the world that happened, winners' own stakes included. When the losing side is below about 3.09% of the pot, every winner receives less than their stake although they were right. This follows the brief; it is reported so the frontend can show it, not as a logic error. Pinned by test_lopsidedWinnersPayTheFeeOnTheirOwnStake.","line":603,"path":"src/Conditionals.sol","reproduction":"World 0: alice stakes 100 IMD YES, bob 1 IMD NO; condition true, outcome true. alice claims 101 - 3.03 = 97.97 IMD (< 100 staked).","severity":"info","title":"Winners on a heavily favoured side get back less than they staked"},{"description":"The comment on onFailure says the question can be asked again after RETRY_DELAY, but _askable (line 531) returns true as soon as openAsks[q] == 0, so after a failure anyone may re-ask immediately (paying the price, since the budget is spent). No value is at risk; the documentation and the behaviour differ. The existing test test_failureThenRetryPaidByCaller asserts the immediate re-ask.","line":415,"path":"src/Conditionals.sol","reproduction":"requestResolution; intake.fail(conditionRequestId, 2, 'disagreed'); askable(id, 0) is true in the same block, and requestResolution re-asks it without waiting a day.","severity":"info","title":"onFailure NatSpec says a question can be asked again after RETRY_DELAY; the code allows it at once"},{"description":"Markets snapshot their oracle configuration but not the fee recipient, so setFeeRecipient applies immediately to every open market's fee. The brief lets the owner set the fee recipient and the amount is fixed, so stakes are not touched; reported because the brief also says the owner cannot touch open markets. Pinned by test_feeRecipientIsReadAtSettlement.","line":613,"path":"src/Conditionals.sol","reproduction":"Market open with 10/10 IMD on world 0; owner setFeeRecipient(next); resolve and settle: next receives 0.2 IMD, the recipient at creation receives 0.","severity":"info","title":"The fee recipient is read at settlement, so changing it redirects the 1% of markets already open"},{"description":"createMarket snapshots oracleConfig as it is when the transaction executes. A creator whose transaction lands just after activateOracleConfig gets the new signer, intake and panel minimums with only the price bounded by maxBudget. The 3 day delay and the OracleConfigProposed event make this visible in advance, and traders can read getMarket(id).oracle before staking; whoever controls the owner key controls the outcome of every market created after an activation of its own signer.","line":332,"path":"src/Conditionals.sol","reproduction":"Owner proposes {signer: ownerKey, quorum: 2, same price}; after 3 days activates; a createMarket in the next block gets oracle.signer == ownerKey without any parameter of createMarket rejecting it.","severity":"info","title":"maxBudget protects a creator against a price change but not against a signer, intake or panel rotation"},{"description":"Each winner's share mine * netPot / winningPool rounds down, leaving at most one wei per winner per settled market in the contract permanently (there is no sweep, by design). Bounded and harmless; testFuzz_winnersSplitTheNetPotWithBoundedDust pins the bound.","line":636,"path":"src/Conditionals.sol","reproduction":"Three winners with stakes that do not divide netPot evenly: sum of claims = netPot - k with k < 3; the k wei remain in the contract.","severity":"info","title":"Rounding dust from winner payouts stays in the contract with no way out"}],"hash":"f9f3cc8db45a7335433a83194101476a7667448049a97ee57e076796ae87d040","nodeId":"02a4edbc-3522-4a09-9c9f-476983bd4863","outcome":"completed","summary":"`forge test` passes with the verifier's fuzz seed: 89 tests in 7 suites, 0 failures. I added 34 tests and found no critical, high or medium defects, so no failing proof was needed. I changed nothing outside `test/`, installed nothing, and haven't committed anything.\n\n**What I added** (the earlier contributors' tests are untouched):\n\n- **`test/utils/OracleQuestion.sol`**: the protocol's `OracleQuestion` library, copied from the oracle reference. A test confirms it reproduces the protocol's vector hash (`0x2117…46fb`).\n- **`test/ConditionalsQuestion.t.sol`** (6 tests): checks the question text the contract builds by hand against that library.\n  - For random valid inputs, the contract's question text matches the library's byte for byte.\n  - The live-API vector (`0xf025…da7e`) comes out the same from both.\n  - The contract accepts text only when it needs no escaping in the JSON.\n  - The request body parses to the right fields and the panel is 5 with quorum 4.\n  - Each ask's stored hash is for exactly the block window it asked about, `block.number - 10`.\n- **`test/ConditionalsAdversarial.t.sol`** (27 tests), covering the failure paths:\n  - **Deployment:** the contract deploys from a factory in one call with the real Robinhood Chain addresses while none of them has code, and the owner is the named owner, not the factory. A zero owner, intake or signer is refused.\n  - **Time limits:** tested at closeTime, resolveTime and the void deadline, including an ask sent in the last second, which delays a void by one day at most.\n  - **Bad answers are refused:** expired answers, weak panels, signatures made for another deployment or another chain, an answer to the other question or to an older window, unreadable callbacks, and calls from a second intake.\n  - **Failure callbacks** move no money and clear only their own ask; a retry by someone who hasn't approved the payment reverts and changes nothing.\n  - **Finished markets stay finished.**\n  - **The owner's reach:** switching to its own signer and intake doesn't affect an open market, and nobody else can call the owner functions.\n  - **Settlement maths:** a fuzz test of the 2% burn and 1% fee and of the leftover rounding.\n- **`test/ConditionalsLifecycleInvariant.t.sol`**: a second invariant suite (128 runs × 80 calls). The random sequences include forged answers and the owner changing the signer and the fee recipient. After every sequence:\n  - forged answers never count;\n  - settled or voided markets and stored answers never change;\n  - a market's signer never changes after it is created;\n  - a voided market pays back exactly what each trader staked;\n  - the contract holds at least what it owes, and leaves no token approval to the intake.\n\n**Findings**, written to `.imd-findings.json` (one low, five info):\n\n- **Low:** an answer stamped with a time in the future (`issuedAt`) is accepted. The protocol's own check refuses anything more than 5 minutes ahead (`Conditionals.sol:397`). A scratch test confirmed an answer stamped one year ahead is stored. I didn't write a test that treats this as correct.\n- **Info:**\n  - The fee is taken from the whole pot, as the brief says, so winners can get back less than they staked when the losing side is under about 3% (stake 100 against 1, and the winner gets 97.97).\n  - The comment on `onFailure` says a retry waits a day, but the code allows one immediately.\n  - The fee recipient is read at settlement, so changing it redirects the 1% fee of markets already open.\n  - `maxBudget` protects a market creator against a price change, but not against a new signer taking effect just before their transaction.\n  - Rounding leaves at most 1 wei per winner stuck in the contract.","treeHash":"6fb173825bf1b08b462487c69178404074cd9949","usage":{"cachedInputTokens":2635330,"inputTokens":50,"model":"claude-opus-5-5","outputTokens":49256,"runtime":"claude","turns":27,"wallClockMs":656727}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"20e8e883cb88740a","findings":[{"citation":"resolved","description":"A market has two worlds; world 1 is 'the condition does not happen' and, like world 0, has a YES and a NO pool on the OUTCOME (the README table, the frontend's 'YES chance' per world and `impact()` = YES in world 0 minus YES in world 1 all present world-1 YES as 'the outcome happens even though the condition does not'). When the oracle answers the condition FALSE, world 1 is the world that happened and its outcome must decide the winner. Instead `_settle` sets `side = NO` for WORLD_DOES_NOT unconditionally, `_settleable` is true on the condition answer alone (line 514) and `_askable` refuses to ask the outcome once the condition is false (line 507). An outcome answer that does arrive is stored (onOracleResult does not look at the condition) and then ignored by `_settle`. Every world-1 YES staker therefore forfeits their stake to world-1 NO stakers whenever the condition does not happen, regardless of whether the outcome happened, and 3% of that pot is burned/paid as fee on a result the oracle never gave. The author's own `test_conditionFalseSettlesWithoutOutcome` bakes this in: it delivers an outcome=TRUE answer after settlement and asserts the world-1 NO stakers were paid the YES pool. Fix options that keep the design: (a) ask and wait for the outcome in both worlds and use `answers[OUTCOME]` to pick the side for world 1 too; or (b) if world 1 is meant to be unconditional, refund it (`worldRefunded = true`) instead of paying NO. Either way the frontend copy and the README must say which.","line":554,"path":"src/Conditionals.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {Conditionals} from \"src/Conditionals.sol\";\nimport {IIntake} from \"src/interfaces/IIntake.sol\";\nimport {IOracleAttestation} from \"src/interfaces/IOracleAttestation.sol\";\n\ncontract ProofIMD is ERC20 {\n    constructor() ERC20(\"Identity.md\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @dev Minimal Intake: pulls the price, records the callback, and later delivers from its own address.\ncontract ProofIntake is IIntake {\n    struct Req {\n        Callback callback;\n        bytes4 onFailure;\n    }\n\n    uint256 public nonce;\n    mapping(bytes32 => Req) public reqs;\n    bytes32[] public ids;\n\n    function requestWithFailure(bytes32, bytes calldata, Callback calldata callback, bytes4 onFailure, address asset, uint256 amount)\n        external\n        returns (bytes32 requestId)\n    {\n        IERC20(asset).transferFrom(msg.sender, address(0xBEEF), amount);\n        requestId = keccak256(abi.encode(address(this), ++nonce));\n        reqs[requestId] = Req(callback, onFailure);\n        ids.push(requestId);\n    }\n\n    function complete(bytes32 requestId, IOracleAttestation.Attestation memory a, bytes memory sig) external returns (bool ok) {\n        Req memory r = reqs[requestId];\n        (ok,) = r.callback.target.call{gas: 200_000}(abi.encodeWithSelector(r.callback.selector, requestId, a, sig));\n    }\n}\n\n/// @title World 1 (\"the condition does not happen\") is settled to NO without the outcome ever being judged\n/// @notice Alice stakes YES and Bob NO in world 1. The oracle answers: condition FALSE (so world 1 is the world that\n///         happened) and outcome TRUE. YES should win world 1 (or, at the very least, world 1 should be refunded).\n///         The contract instead hands the whole world-1 pot to Bob and pays Alice nothing.\ncontract World1OutcomeProof is Test {\n    ProofIMD imd;\n    ProofIntake intake;\n    Conditionals c;\n\n    uint256 signerKey = 0xA11CE;\n    address creator = makeAddr(\"creator\");\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n    address feeRecipient = makeAddr(\"fee\");\n\n    function setUp() public {\n        vm.warp(1_790_000_000);\n        vm.roll(26_000_000);\n        imd = new ProofIMD();\n        intake = new ProofIntake();\n        c = new Conditionals(address(this), IERC20(address(imd)), IIntake(address(intake)), vm.addr(signerKey), feeRecipient);\n        address[3] memory users = [creator, alice, bob];\n        for (uint256 i; i < users.length; ++i) {\n            imd.mint(users[i], 100 ether);\n            vm.prank(users[i]);\n            imd.approve(address(c), type(uint256).max);\n        }\n    }\n\n    function test_yesStakeInWorldThatHappenedIsForfeitedThoughOutcomeWasTrue() public {\n        uint64 closeTime = uint64(block.timestamp + 1 days);\n        uint64 resolveTime = uint64(block.timestamp + 2 days);\n        vm.prank(creator);\n        uint256 id = c.createMarket(\n            \"The Clarity Act is signed into US law\",\n            \"ETH closes above 5000 USD on Coinbase on 2026-12-01\",\n            \"Use congress.gov for the law and the Coinbase ETH-USD daily close for the price.\",\n            closeTime,\n            resolveTime\n        );\n        // World 1 = the condition does not happen. Alice: the outcome happens anyway. Bob: it does not.\n        vm.prank(alice);\n        c.stake(id, 1, 0, 10 ether);\n        vm.prank(bob);\n        c.stake(id, 1, 1, 20 ether);\n\n        vm.warp(resolveTime);\n        uint256 windowBlock = block.number - c.WINDOW_DEPTH();\n        assertEq(c.requestResolution(id), 2, \"both questions asked from the budget\");\n        bytes32 conditionAsk = intake.ids(0);\n        bytes32 outcomeAsk = intake.ids(1);\n\n        // The oracle: the outcome is TRUE, the condition is FALSE. Both answers are accepted by the contract.\n        assertTrue(_answer(id, outcomeAsk, 1, windowBlock, true), \"outcome answer delivered\");\n        assertTrue(_answer(id, conditionAsk, 0, windowBlock, false), \"condition answer delivered\");\n        assertEq(c.getMarket(id).answers[1], 1, \"outcome recorded as TRUE\");\n\n        c.settle(id);\n        Conditionals.Market memory m = c.getMarket(id);\n        assertEq(m.world, 1, \"world 1 happened\");\n\n        // Expected: Alice staked YES in the world that happened and the outcome was TRUE, so she does not lose her\n        // stake (YES wins, or at minimum world 1 is refunded). Actual: NO is declared the winner without the outcome\n        // being considered; Alice gets nothing and Bob is paid Alice's stake.\n        uint256 aliceGets = c.claimable(id, alice);\n        uint256 bobGets = c.claimable(id, bob);\n        assertGe(aliceGets, 10 ether, \"a YES stake in the happened world with a TRUE outcome must not be forfeited\");\n        assertLe(bobGets, 20 ether, \"a NO stake must not be paid the YES pot when the outcome was TRUE\");\n    }\n\n    function _answer(uint256 id, bytes32 requestId, uint8 question, uint256 windowBlock, bool value) internal returns (bool) {\n        IOracleAttestation.Attestation memory a;\n        a.requestId = keccak256(abi.encode(\"oracle\", requestId));\n        a.chainId = 1;\n        a.questionHash = keccak256(bytes(c.questionDocument(id, question, windowBlock)));\n        a.answerType = 0;\n        a.answer = abi.encode(value);\n        a.fromBlock = uint64(windowBlock);\n        a.toBlock = uint64(windowBlock);\n        a.blockHash = keccak256(\"block\");\n        a.panelJobId = keccak256(\"panel\");\n        a.panelSize = 5;\n        a.quorum = 4;\n        a.agreed = 5;\n        uint64 now_ = uint64(block.timestamp);\n        a.issuedAt = now_;\n        a.expiresAt = now_ + 86_400;\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerKey, _digest(a));\n        return intake.complete(requestId, a, abi.encodePacked(r, s, v));\n    }\n\n    function _digest(IOracleAttestation.Attestation memory a) internal view returns (bytes32) {\n        bytes32 domain = keccak256(\n            abi.encode(\n                keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n                keccak256(\"IdentityMD Oracle\"),\n                keccak256(\"2\"),\n                block.chainid,\n                address(c)\n            )\n        );\n        bytes32 structHash = keccak256(\n            bytes.concat(\n                abi.encode(\n                    c.ATTESTATION_TYPEHASH(),\n                    a.requestId,\n                    a.chainId,\n                    a.questionHash,\n                    a.answerType,\n                    keccak256(a.answer),\n                    a.figure,\n                    a.fromBlock\n                ),\n                abi.encode(a.toBlock, a.blockHash, a.panelJobId, a.panelSize, a.quorum, a.agreed, a.issuedAt, a.expiresAt)\n            )\n        );\n        return keccak256(abi.encodePacked(\"\\x19\\x01\", domain, structHash));\n    }\n}","reproduction":"State: market id 1, alice stakes 10 IMD on (world 1, YES), bob stakes 20 IMD on (world 1, NO). After resolveTime anyone calls requestResolution(1): both questions are asked. The Intake delivers outcome=TRUE then condition=FALSE, both accepted (getMarket(1).answers == [2, 1]). settle(1). Expected: world 1 happened and the outcome was TRUE, so YES wins world 1 (alice claimable ~29.1 IMD, bob 0) or at minimum world 1 is refunded (alice 10, bob 20). Actual: world=1, winningSide=NO; claimable(1, alice) == 0 and claimable(1, bob) == 29.1 IMD (30 IMD pot minus 0.6 burned and 0.3 fee). Run: forge test --match-path test/scratch/World1Outcome.t.sol","severity":"high","snippet":"        uint8 world = m.answers[CONDITION] == ANSWER_TRUE ? WORLD_HAPPENS : WORLD_DOES_NOT;\n        uint8 side = NO;\n        if (world == WORLD_HAPPENS) side = m.answers[OUTCOME] == ANSWER_TRUE ? YES : NO;","title":"In the 'condition does not happen' world the pot is always paid to NO; the outcome is never judged"},{"citation":"resolved","description":"`voidMarket` only requires `block.timestamp > voidAfter` and `!_settleable(m)`. It does not look at `openAsks` (a question asked and not yet called back) or at a partial answer. `requestResolution` is allowed up to and including `voidAfter` (line 357), and an answer always arrives minutes after an ask, so there is always a window in which a market has a live ask, or already has the condition answered, and anyone may void it. A trader on the losing side who can see the answer coming (the condition is public by then) voids one second after `voidAfter`; the answer that lands next is `AnswerIgnored` and the winners are refunded instead of paid. The same boundary lets a caller ask at exactly `voidAfter`, spending 0.5 IMD of the budget (or their own) on a question whose answer can never count. A fix that keeps the 30 day void: refuse void while an ask is live and younger than RETRY_DELAY, or while the condition is already answered and the outcome ask is live.","line":422,"path":"src/Conditionals.sol","reproduction":"alice stakes 10 IMD (world 0, YES), bob 20 IMD (world 0, NO). Nobody asks until resolveTime + 29 days; then requestResolution asks both. The Intake delivers condition=TRUE; the outcome ask stays live. At resolveTime + 30 days + 1 s bob calls voidMarket(id): it succeeds (status Voided). The outcome=TRUE answer delivered 60 s later is AnswerIgnored. Expected: the market settles YES in world 0 (alice ~29.1 IMD, bob 0). Actual: claimable(alice) == 10 IMD, claimable(bob) == 20 IMD. Reproduced with test/scratch/VoidRace.t.sol (the test passes, i.e. the sequence is accepted by the contract as it stands).","severity":"low","snippet":"        if (m.status != Status.Open || block.timestamp <= m.voidAfter || _settleable(m)) revert NotVoidable();","title":"voidMarket can be called while an oracle answer is in flight, letting a losing staker escape a settled-in-principle market"},{"citation":"resolved","description":"The oracle-consumer reference requires the action id to be constructor input and owner-settable alongside the intake, asset/price and signer, because a new action version (`oracle-2`) must not need a redeploy. `OracleConfig` lets the owner rotate intake, signer, price, panel and quorum for future markets, but `ORACLE_ACTION` is hard-coded to `oracle.request@oracle-1`, and the body shape (`v`:1 and its keys) is fixed too. Markets may have a resolveTime up to 730 days out and keep their configuration. If the Intake stops pricing oracle-1 (`priceOf` returns 0) or refuses the v1 body, `requestWithFailure` reverts `ActionNotSold` (or the ask is refused with the price spent) for every market, old and new, and nothing the owner can do restores resolution: every open market voids at voidAfter. No stake is lost, but the product stops working and budgets spent on refused asks are gone. Fix: carry the action id in `OracleConfig` (validated non-zero) so the 3 day rotation path covers it, as it covers the intake.","line":83,"path":"src/Conditionals.sol","reproduction":"State: Intake.priceOf(bytes32('oracle.request@oracle-1'), IMD) becomes 0 (version retired) while market 1 is open with resolveTime in the future. At resolveTime anyone calls requestResolution(1). Expected (per the reference's upgrade path): the owner has moved the configuration to the new action and the ask goes through. Actual: the Intake reverts ActionNotSold inside requestWithFailure, requestResolution reverts, and this repeats until voidAfter; the only exit is voidMarket. With a mock intake that reverts for the constant action id, requestResolution(1) reverts and askable(1, 0) stays true for 30 days.","severity":"low","snippet":"    bytes32 public constant ORACLE_ACTION = \"oracle.request@oracle-1\";","title":"The oracle action id is a constant: when oracle-1 stops being sold every market can only void"},{"citation":"resolved","description":"The protocol's `OracleAttestationConsumer` verifies with `SignatureChecker`, so the signer may be an `OracleSignerRegistry` (ERC-1271) and keys can rotate without touching consumers. `onOracleResult` uses `ECDSA.tryRecover` against the single EOA stored in the market's snapshot `config.signer`. A market can be open for up to 760 days with that snapshot. If IdentityMD rotates the attester (new EOA or a registry contract), every answer for a market created before the owner's 3 day rotation lands reverts `BadAttestation`; the writer's single delivery attempt is lost, each retry costs the caller 0.5 IMD and fails the same way, and the market voids. Stakes are refunded but the creator's 1 IMD budget and any retries are gone. Verify with `SignatureChecker` (supports both an EOA and a registry) and consider letting an answer verify against the current signer as well as the market's snapshot.","line":390,"path":"src/Conditionals.sol","reproduction":"Market 1 created with oracleSigner S1. Owner proposes and activates S2 (3 days). The oracle now signs with S2. At resolveTime requestResolution(1) asks; the Intake delivers an attestation correctly signed by S2 for this contract and this questionHash. Expected: accepted. Actual: `recovered != config.signer` -> revert BadAttestation; the Intake records the callback as not delivered. The suite's test_rejectsWrongSigner shows exactly this revert for any key other than the snapshot. The same happens if S1 is replaced by a registry contract: tryRecover never yields a contract address.","severity":"low","snippet":"        (address recovered, ECDSA.RecoverError err,) =\n            ECDSA.tryRecover(_hashTypedDataV4(_hashAttestation(a)), signature);\n        if (err != ECDSA.RecoverError.NoError || recovered != config.signer) revert BadAttestation();","title":"Signer check is ECDSA-only and pinned per market; an attester rotation strands open markets"},{"citation":"resolved","description":"`createMarket` has no `maxDeposit` argument: it reads `oracleConfig.price` at execution time and pulls twice that from `msg.sender`, who has typically approved `type(uint256).max` (the frontend does). `price` may be set up to MAX_PRICE = 10 IMD by the owner (`proposeOracleConfig`, then `activateOracleConfig` after 3 days at a moment of the owner's choosing). A creator who submits createMarket expecting to pay 1 IMD can be charged 20 IMD if the activation lands first in the block. The deposit is only returned at settlement or void, up to 760 days later, and is spent if the asks happen. This is an owner trust assumption the brief accepts in principle; the missing guard is the creator's. Add an expected-budget (or expected price) argument to createMarket and revert when the active configuration charges more.","line":321,"path":"src/Conditionals.sol","reproduction":"oracleConfig.price = 0.5 IMD; owner has a pending config with price = 10 IMD whose delay has elapsed. Block N includes owner.activateOracleConfig() then creator.createMarket(...). Expected: the creator pays 1 IMD, or the call reverts because the price is not what they agreed to. Actual: imd.transferFrom(creator, contract, 20 IMD) succeeds and getMarket(id).budget == 20e18.","severity":"low","snippet":"        m.budget = uint256(m.oracle.price) * 2;","title":"createMarket pulls 2 x oracleConfig.price with no cap from the caller, so a config activation can front-run a creator for up to 20 IMD"}],"hash":"fbd6d8dca3b3e36da4988aed6b51fb9731a3ae982323849f11d862170deb993c","nodeId":"4fdf86c3-25e9-42a8-af3a-d6162d70fbf3","outcome":"completed","summary":"Final pass done. The findings file holds five findings, each with a snippet that matches its line, and the high one carries a failing proof. Writing the report now.\n\n**Audit of `src/Conditionals.sol` complete. Five findings written to `.imd-findings.json`: one high with a failing Foundry proof, four low.**\n\n**What I read.** `src/Conditionals.sol` in full, both interfaces, the author's unit, invariant, fork tests, mocks and fixture, the frontend's settlement model and world labels, and the supplied oracle-consumer, intake-client, Pashov, eth-security and review checklists. I could not read the live Intake or IMD source on Robinhood Chain, only probe them over RPC, and the oracle quote endpoint needs a token, so the author's pinned question-hash vector was taken on trust after checking the document layout against the canonical `OracleQuestion` byte for byte.\n\n**Verified clean.** The EIP-712 digest reproduces the protocol's conformance vector and signature at the vector address, and both callback selectors are canonical. In-EVM `block.number` on Robinhood Chain is the Ethereum block (checked by `eth_call` with a code override), so the pinned window is valid. The Intake sells `oracle.request@oracle-1` at 0.5 IMD, the signer is an EOA, and the live wording check returns no blockers for the contract's body. Runtime is 18,036 bytes with no forbidden opcodes, no proxy, pause, mint or blacklist. Reentrancy, fee rounding, claim accounting and callback binding to the contract's own request held up. The existing 42 tests pass.\n\n**Findings.**\n- **High, `_settle` line 554.** In the \"condition does not happen\" world the contract always declares NO the winner. The outcome is never asked once the condition is false, and an outcome answer that does arrive is ignored. World-1 YES stakers forfeit their stake to NO stakers whatever actually happened, and a 3% fee is taken on it. The author's own test asserts this behaviour. Proof: `test/scratch/World1Outcome.t.sol` fails now with alice's claimable at 0 instead of at least her 10 IMD.\n- **Low, `voidMarket` line 422.** Void ignores live asks and a partial answer, so a loser can void one second after `voidAfter` while the outcome answer is in flight. Reproduced in `test/scratch/VoidRace.t.sol`.\n- **Low, line 83.** The action id is a constant, so when `oracle-1` is retired every market can only void. The reference requires it to be owner-settable like the intake and signer.\n- **Low, lines 390 to 392.** Signature check is ECDSA-only against a per-market snapshot. An attester rotation or a registry signer strands open markets for up to 760 days.\n- **Low, line 321.** `createMarket` pulls twice the current price with no cap, so a config activation of 10 IMD landing first in the block charges a creator 20 IMD.\n\n**Untested edges worth noting for the adapter.** No test answers the outcome in world 1 and checks who is paid. No test covers void with a live ask, the Intake retiring the action, or a signer change against an open market.","treeHash":null,"usage":{"cachedInputTokens":3341215,"inputTokens":898,"model":"claude-fable-5-1","outputTokens":50428,"runtime":"claude","turns":29,"wallClockMs":838057}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"91067b3439961e55","findings":[{"citation":"resolved","description":"Merged from audit_math, audit_economics, audit_permissions (medium) and write_foundry_tests, audit_flow (info): one root cause. _settle picks the fee branch on a strict `yes == 0 || no == 0` test and then charges BURN_BPS + BUILDER_BPS on the entire pot of the world that happened, i.e. on the winners' own principal, not on what the losing side risked. The README and the brief promise that 'a one-sided world is refunded with no fee'; anyone can flip that world into the fee branch for MIN_STAKE (1e15 wei) on the empty side before closeTime. The winners then receive pot * 0.97 * share, which is below their stake whenever the losing pool is under ~3.09% of the pot. feeRecipient (owner-settable) has a repeatable profitable move: dust every one-sided world and collect 1% of the pot for 0.001 IMD, and 2% of the stakers' principal is burned. This matches the literal arithmetic the brief states (fee on the pot) but breaks the guarantee the brief and README also state for one-sided worlds, and gives the owner-chosen fee recipient a lever on stakes the brief says the owner cannot touch. Loss is bounded to 3% of the affected world's pot, so medium. Fix options that keep the design: cap the fee at the losing pool (fee = min(3% of pot, losing pool)), or charge the fee on the losing pool only, or treat a counter-pool below some ratio of the pot as one-sided.","line":599,"path":"src/Conditionals.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {Conditionals} from \"src/Conditionals.sol\";\nimport {IIntake} from \"src/interfaces/IIntake.sol\";\nimport {IOracleAttestation} from \"src/interfaces/IOracleAttestation.sol\";\n\ncontract ScratchIMD is ERC20 {\n    constructor() ERC20(\"Identity.md\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\ncontract ScratchIntake is IIntake {\n    struct Request {\n        address target;\n        bytes4 selector;\n    }\n\n    uint256 public nonce;\n    mapping(bytes32 => Request) public requests;\n    bytes32[] public ids;\n\n    function requestWithFailure(bytes32, bytes calldata, Callback calldata callback, bytes4, address asset, uint256 amount)\n        external\n        returns (bytes32 requestId)\n    {\n        IERC20(asset).transferFrom(msg.sender, address(0xBEEF), amount);\n        requestId = keccak256(abi.encode(address(this), ++nonce));\n        requests[requestId] = Request(callback.target, callback.selector);\n        ids.push(requestId);\n    }\n\n    function complete(bytes32 requestId, IOracleAttestation.Attestation memory a, bytes memory sig)\n        external\n        returns (bool ok)\n    {\n        Request memory r = requests[requestId];\n        (ok,) = r.target.call{gas: 200_000}(abi.encodeWithSelector(r.selector, requestId, a, sig));\n    }\n}\n\n/// @dev A 0.001 IMD stake on the empty side of an otherwise one-sided world turns the fee-free refund into a 3% fee\n///      on the whole pot: the sole winner gets back less than they staked, and the fee recipient is paid 1% of the pot.\ncontract MinStakeFeeBoundaryTest is Test {\n    ScratchIMD imd;\n    ScratchIntake intake;\n    Conditionals c;\n\n    uint256 signerKey = 0xA11CE;\n    address signer;\n    address owner = makeAddr(\"owner\");\n    address feeRecipient = makeAddr(\"feeRecipient\");\n    address creator = makeAddr(\"creator\");\n    address whale = makeAddr(\"whale\");\n    address griefer = makeAddr(\"griefer\");\n\n    uint64 closeTime;\n    uint64 resolveTime;\n\n    function setUp() public {\n        vm.warp(1_790_000_000);\n        vm.roll(26_000_000);\n        signer = vm.addr(signerKey);\n        imd = new ScratchIMD();\n        intake = new ScratchIntake();\n        c = new Conditionals(owner, IERC20(address(imd)), IIntake(address(intake)), signer, feeRecipient);\n        address[3] memory users = [creator, whale, griefer];\n        for (uint256 i; i < 3; ++i) {\n            imd.mint(users[i], 10_000 ether);\n            vm.prank(users[i]);\n            imd.approve(address(c), type(uint256).max);\n        }\n        closeTime = uint64(block.timestamp + 7 days);\n        resolveTime = uint64(block.timestamp + 14 days);\n    }\n\n    function test_minimumCounterStakeMakesTheWinnerLoseAndPaysTheFeeRecipient() public {\n        vm.prank(creator);\n        uint256 id = c.createMarket(\"Condition happens\", \"Outcome happens\", \"Use public sources.\", closeTime, resolveTime, 1 ether);\n\n        // World 0 is one-sided: 1000 IMD on YES. By the README this world is \"refunded with no fee\" if it happens.\n        vm.prank(whale);\n        c.stake(id, 0, 0, 1_000 ether);\n        // The griefer (or the fee recipient itself) adds the minimum stake on NO.\n        uint256 minStake = c.MIN_STAKE();\n        vm.prank(griefer);\n        c.stake(id, 0, 1, minStake);\n\n        vm.warp(resolveTime);\n        c.requestResolution(id);\n        _answer(id, intake.ids(0), 0, true); // condition true -> world 0 happened\n        _answer(id, intake.ids(1), 1, true); // outcome true -> YES wins\n\n        vm.prank(whale);\n        uint256 paid = c.claim(id);\n\n        // Pot = 1000.001 IMD; burned = 20.00002, fee = 10.00001, netPot = 970.00097 IMD.\n        assertEq(paid, 970_000_970_000_000_000_000);\n        assertEq(imd.balanceOf(feeRecipient), 10_000_010_000_000_000_000);\n        assertEq(imd.balanceOf(0x000000000000000000000000000000000000dEaD), 20_000_020_000_000_000_000);\n\n        // The expected property: a winner whose only counterparty is 0.001 IMD must not get back less than they staked.\n        assertGe(paid, 1_000 ether, \"sole winner ends with less than their stake\");\n    }\n\n    function _answer(uint256 id, bytes32 requestId, uint8 q, bool value) internal {\n        IOracleAttestation.Attestation memory a;\n        a.requestId = bytes32(bytes16(keccak256(abi.encode(\"oracle\", requestId))));\n        a.chainId = 1;\n        a.questionHash = keccak256(bytes(c.questionDocument(id, q, block.number - c.WINDOW_DEPTH())));\n        a.answerType = 0;\n        a.answer = abi.encode(value);\n        a.fromBlock = uint64(block.number - 10);\n        a.toBlock = a.fromBlock;\n        a.panelSize = 5;\n        a.quorum = 4;\n        a.agreed = 5;\n        a.issuedAt = uint64(block.timestamp);\n        a.expiresAt = uint64(block.timestamp + 1 days);\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerKey, c.attestationDigest(a));\n        assertTrue(intake.complete(requestId, a, abi.encodePacked(r, s, v)), \"not delivered\");\n    }\n}","reproduction":"Fixture: createMarket(cond, outcome, rules, close, resolve, 1e18). whale: stake(id, 0, YES, 1000e18). griefer (or feeRecipient): stake(id, 0, NO, 1e15). warp to resolveTime; requestResolution(id); the Intake delivers condition=true and outcome=true signed by the market's signer. whale: claim(id). Expected (README/brief: a world whose only counterparty is dust is refunded with no fee; a correct staker never nets less than staked): whale receives >= 1000e18. Actual: pot = 1000.001e18, burned = 20.00002e18 to 0xdEaD, builderFee = 10.00001e18 to feeRecipient, netPot = 970.00097e18; claim() returns 970000970000000000000, a loss of 29.99903 IMD for a correct prediction; feeRecipient gains 10.00001 IMD for 0.001 IMD. Without the dust stake the same market pays whale exactly 1000e18 and burns nothing (test_oneSidedWorldIsRefundedWithoutFee). Proof test/scratch/P1.t.sol fails on this tree with 'sole winner ends with less than their stake: 970000970000000000000 < 1000000000000000000000'.","severity":"medium","snippet":"        if (yes == 0 || no == 0) {\n            m.worldRefunded = true;\n        } else {\n            uint256 pot = yes + no;\n            burned = pot * BURN_BPS / BPS;\n            builderFee = pot * BUILDER_BPS / BPS;","title":"A 0.001 IMD counter-stake defeats the fee-free one-sided refund: the 3% fee is then charged on the whole pot, so a correct side gets back less than it staked and the fee recipient collects 1% of it"},{"citation":"resolved","description":"From audit_economics. The IMD at 0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127 on Robinhood Chain is not a plain ERC-20: queried over the chain's public RPC it answers blocked(address) (false today for 0xdEaD and the fee recipient) and transfersEnabled() (true today), so its owner can refuse transfers to or from one address. _settle transfers to BURN_ADDRESS (a constant), feeRecipient and, via _returnBudget, the creator, and voidMarket calls _returnBudget, all in the same call that every staker depends on. If the token refuses feeRecipient, every fee-bearing settlement reverts, and because the market is settleable voidMarket reverts NotVoidable, so the stakers' pot is stuck until the owner rotates feeRecipient; if 0xdEaD were refused nothing could unstick it. If the token refuses the creator while the budget is unspent, voidMarket reverts on the budget push; this case is recoverable by anyone calling requestResolution before voidAfter (the budget is spent on the asks and _returnBudget then transfers nothing), which keeps it low. Fix: make the fee and the budget return pull-based (credit owed[feeRecipient] / owed[creator] and let them withdraw), or wrap the third-party pushes so a failed push does not block stakers' claims.","line":612,"path":"src/Conditionals.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {Conditionals} from \"src/Conditionals.sol\";\nimport {IIntake} from \"src/interfaces/IIntake.sol\";\nimport {IOracleAttestation} from \"src/interfaces/IOracleAttestation.sol\";\n\n/// @dev Like the live IMD (a \"BridgedFP\" LayerZero OFT exposing `blocked(address)`, `transfersEnabled()` and a\n///      v4 transfer gate): the token owner can make transfers to or from one address revert.\ncontract BlocklistIMD is ERC20 {\n    mapping(address => bool) public blocked;\n    constructor() ERC20(\"Identity.md\", \"IMD\") {}\n    function mint(address to, uint256 amount) external { _mint(to, amount); }\n    function block_(address a, bool b) external { blocked[a] = b; }\n    function _update(address from, address to, uint256 value) internal override {\n        require(!blocked[from] && !blocked[to], \"blocked\");\n        super._update(from, to, value);\n    }\n}\n\ncontract ScratchIntake2 is IIntake {\n    struct Req { IIntake.Callback cb; bytes4 onFailure; }\n    uint256 public nonce;\n    mapping(bytes32 => Req) public reqs;\n    bytes32[] public ids;\n    function requestWithFailure(bytes32, bytes calldata, Callback calldata cb, bytes4 onFailure, address asset, uint256 amount)\n        external returns (bytes32 id)\n    {\n        IERC20(asset).transferFrom(msg.sender, address(0xBEEF), amount);\n        id = keccak256(abi.encode(address(this), ++nonce));\n        reqs[id] = Req(cb, onFailure);\n        ids.push(id);\n    }\n    function complete(bytes32 id, IOracleAttestation.Attestation memory a, bytes memory sig) external returns (bool ok) {\n        (ok,) = reqs[id].cb.target.call{gas: 200_000}(abi.encodeWithSelector(reqs[id].cb.selector, id, a, sig));\n    }\n}\n\n/// @dev Settlement and void push IMD to third parties (fee recipient, creator). A receiver the token refuses\n///      makes settle, claim and void revert for every staker. Passes once those payments are pull-based (or\n///      tolerate a failed push), so stakers can always claim their own money.\ncontract PushTransferBricksTest is Test {\n    BlocklistIMD imd;\n    ScratchIntake2 intake;\n    Conditionals c;\n    uint256 signerKey = 0xA11CE;\n    address signer;\n    address owner = makeAddr(\"owner\");\n    address builder = makeAddr(\"builder\");\n    address creator = makeAddr(\"creator\");\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n    uint64 closeTime;\n    uint64 resolveTime;\n\n    function setUp() public {\n        vm.warp(1_790_000_000);\n        vm.roll(26_000_000);\n        signer = vm.addr(signerKey);\n        imd = new BlocklistIMD();\n        intake = new ScratchIntake2();\n        c = new Conditionals(owner, IERC20(address(imd)), IIntake(address(intake)), signer, builder);\n        address[3] memory users = [creator, alice, bob];\n        for (uint256 i; i < 3; ++i) {\n            imd.mint(users[i], 10_000 ether);\n            vm.prank(users[i]);\n            imd.approve(address(c), type(uint256).max);\n        }\n        closeTime = uint64(block.timestamp + 7 days);\n        resolveTime = uint64(block.timestamp + 14 days);\n    }\n\n    function _digest(IOracleAttestation.Attestation memory a) internal view returns (bytes32) {\n        bytes32 domain = keccak256(abi.encode(\n            keccak256(\"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"),\n            keccak256(\"IdentityMD Oracle\"), keccak256(\"2\"), block.chainid, address(c)));\n        bytes32 structHash = keccak256(bytes.concat(\n            abi.encode(c.ATTESTATION_TYPEHASH(), a.requestId, a.chainId, a.questionHash, a.answerType, keccak256(a.answer), a.figure, a.fromBlock),\n            abi.encode(a.toBlock, a.blockHash, a.panelJobId, a.panelSize, a.quorum, a.agreed, a.issuedAt, a.expiresAt)));\n        return keccak256(abi.encodePacked(\"\\x19\\x01\", domain, structHash));\n    }\n\n    function _answer(uint256 id, bytes32 rid, uint8 q, uint256 windowBlock, bool value) internal {\n        IOracleAttestation.Attestation memory a;\n        a.requestId = bytes32(bytes16(rid));\n        a.chainId = 1;\n        a.questionHash = keccak256(bytes(c.questionDocument(id, q, windowBlock)));\n        a.answer = abi.encode(value);\n        a.fromBlock = uint64(windowBlock);\n        a.toBlock = uint64(windowBlock);\n        a.panelSize = 5; a.quorum = 4; a.agreed = 5;\n        a.issuedAt = uint64(block.timestamp);\n        a.expiresAt = uint64(block.timestamp + 1 days);\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerKey, _digest(a));\n        assertTrue(intake.complete(rid, a, abi.encodePacked(r, s, v)), \"not delivered\");\n    }\n\n    function _market() internal returns (uint256 id) {\n        vm.prank(creator);\n        id = c.createMarket(\"Condition happens\", \"Outcome happens\", \"Use public sources.\", closeTime, resolveTime, 1 ether);\n        vm.prank(alice);\n        c.stake(id, 0, 0, 100 ether);\n        vm.prank(bob);\n        c.stake(id, 0, 1, 100 ether);\n    }\n\n    /// Both answers are in, then the token refuses the fee recipient. Alice (the winner) must still be able to\n    /// collect her payout; today settle/claim revert on the fee push and void is refused (the market is settleable).\n    function test_winnerCanClaimWhenTheFeeRecipientIsBlocked() public {\n        uint256 id = _market();\n        vm.warp(resolveTime);\n        uint256 wb = block.number - c.WINDOW_DEPTH();\n        c.requestResolution(id);\n        _answer(id, intake.ids(0), 0, wb, true);\n        _answer(id, intake.ids(1), 1, wb, true);\n        imd.block_(builder, true);\n\n        uint256 before = imd.balanceOf(alice);\n        vm.prank(alice);\n        c.claim(id);\n        assertGt(imd.balanceOf(alice), before, \"winner could not claim\");\n    }\n\n    /// No answer ever came and the creator (who still has the 1 IMD budget to receive) is blocked by the token.\n    /// Stakers must still get their refunds after voidAfter; today voidMarket reverts on the budget push.\n    function test_stakersCanBeRefundedWhenTheCreatorIsBlocked() public {\n        uint256 id = _market();\n        imd.block_(creator, true);\n        vm.warp(resolveTime + 31 days);\n        c.voidMarket(id);\n        vm.prank(alice);\n        assertEq(c.claim(id), 100 ether);\n    }\n}","reproduction":"Token: an ERC-20 whose _update reverts for a blocked address (as the live IMD's owner can configure). Case A: alice stake(id,0,YES,100e18), bob stake(id,0,NO,100e18); both answers delivered (true,true); token blocks feeRecipient. settle(id) and alice's claim(id) revert with the token's error at line 613; voidMarket(id) at resolveTime+31 days reverts NotVoidable. Expected: alice collects her payout; actual: 200 IMD stuck until setFeeRecipient. Case B: same stakes, nobody asks, token blocks creator; at resolveTime+31 days voidMarket(id) reverts at line 621 and claim(id) reverts NotSettleable until someone first calls requestResolution. Proof test/scratch/P3.t.sol: both tests fail on this tree with 'blocked'.","severity":"low","snippet":"        if (burned != 0) imd.safeTransfer(BURN_ADDRESS, burned);\n        if (builderFee != 0) imd.safeTransfer(feeRecipient, builderFee);\n        _returnBudget(marketId, m);","title":"Settlement and void push IMD to the burn address, the fee recipient and the creator inside the stakers' transaction; a receiver the live IMD's blocklist refuses makes settle, claim and void revert for"},{"citation":"resolved","description":"Merged from audit_permissions, audit_flow (price up) and audit_economics (price down): one root cause, _ask passes config.price (frozen at createMarket) as the amount of requestWithFailure (lines 567-575) and IIntake.sol does not even declare priceOf. The oracle-consumer reference says the price is owner-settable at the Intake and 'a moved price must not need a redeploy'; markets may run 730 days. Up: the Intake refuses the short amount, requestResolution reverts for every caller until voidAfter, askable keeps returning true, the owner cannot repair it (config changes reach future markets only) and the market voids with stakes refunded; the README documents this outcome. Down: the Intake pulls and forwards exactly amount, so every ask of every existing market overpays the difference from the creator's budget (which _returnBudget would otherwise refund) and from each caller-paid retry; this case is undocumented. Fix: read priceOf(action, imd) at ask time, pay min(live, config.price) and let the caller top up a difference above the snapshot, or let the budget cover it.","line":553,"path":"src/Conditionals.sol","reproduction":"Down: market created at price 0.5e18 (budget 1e18); replace the market's Intake code with one whose price is 0.25e18 and that pulls exactly amount (vm.etch); warp to resolveTime; requestResolution(id). Expected: 0.25e18 paid per question, 0.5e18 returned to the creator at settlement or void. Actual: the Intake receives 1e18, getMarket(id).budget == 0, totalOracleSpend == 1e18, the creator gets nothing back (scratch test test_priceDropOverpays on this tree). Up: same market, Intake code requiring amount >= 0.6e18; requestResolution(id) reverts 'PriceNotMet' for every caller until voidAfter; voidable(id) becomes true at resolveTime + 30 days + 1 and the market never settles.","severity":"low","snippet":"        uint256 price = config.price;","title":"Each ask pays the market's snapshotted price and never reads the Intake's live price: a higher live price strands every open market (void only), a lower one is silently overpaid from the budget and by"},{"citation":"resolved","description":"From write_foundry_tests. The oracle-consumer reference (OracleAttestationConsumer._verifyAttestation) refuses an attestation with a.issuedAt > block.timestamp + 5 minutes (AttestationNotYetValid) as well as an expired one; Conditionals checks only block.timestamp > a.expiresAt. The signer is trusted, so the impact is limited to accepting a pre-dated answer the protocol's own consumer would refuse; the skill says to inherit the reference rather than retype it. Add the issuedAt tolerance check.","line":397,"path":"src/Conditionals.sol","reproduction":"Market with stakes; requestResolution; build the attestation for the condition ask with every field correct except issuedAt = block.timestamp + 365 days and expiresAt = block.timestamp + 366 days, signed by the market's signer, delivered by the Intake. Expected (reference behaviour): the callback reverts and the ask stays pending. Actual: delivery returns true and getMarket(id).answers[0] == 1 (scratch test test_issuedAtInFutureAccepted on this tree).","severity":"low","snippet":"        if (\n            a.questionHash != ask.questionHash || a.answerType != 0 || a.answer.length != 32\n                || a.panelSize < config.panelSize || a.quorum < config.quorum || a.agreed < a.quorum\n                || a.agreed > a.panelSize || block.timestamp > a.expiresAt\n        ) revert BadAttestation();","title":"onOracleResult reimplements the protocol's _verifyAttestation and drops the issuedAt check: an attestation issued arbitrarily far in the future is accepted"},{"citation":"resolved","description":"Merged from audit_permissions (low) and write_foundry_tests (info). The author guarded the race between activateOracleConfig and an in-flight createMarket with maxBudget, but it covers one of six fields. The signer (who decides every answer), the intake (who is paid and may call back), the action id and the panel/quorum can all change under a pending createMarket, and pendingOracleConfig has no expiry, so a proposal can sit past its 3-day delay for months and be activated in the same block a market is created. The creator deposits 1 IMD and opens a market whose oracle it never agreed to; stakers can read getMarket(id).oracle before staking, so the exposure is the creator's budget and the market's usefulness. Fix: let createMarket pin a hash of the configuration it expects (or the signer and intake), and/or expire a pending configuration.","line":332,"path":"src/Conditionals.sol","reproduction":"Owner proposes _config() with signer = 0xBEEF and quorum = 2, price unchanged; warp 3 days; in one block the owner calls activateOracleConfig() and then the creator calls createMarket(CONDITION, OUTCOME, RULES, close, resolve, 1 ether), having read signer == original and quorum == 4 before sending. Expected: the creator can refuse a configuration other than the one it read. Actual: the maxBudget check passes and getMarket(id).oracle.signer == 0xBEEF, .quorum == 2 (scratch test test_configRaceOnlyPinsPrice on this tree).","severity":"low","snippet":"        m.oracle = oracleConfig;\n        m.budget = uint256(m.oracle.price) * 2;\n        if (m.budget > maxBudget) revert BudgetTooHigh(m.budget, maxBudget);","title":"createMarket binds the market to whatever oracle configuration is active when it is mined; maxBudget pins only the price, so a creator racing activateOracleConfig gets a signer, intake, action or pane"},{"citation":"resolved","description":"Merged from audit_math (low) and write_foundry_tests (info); the README documents it ('at most a few wei per market stay in the contract'), so it is a note. Each winner is paid floor(mine * netPot / pool); the floors do not sum to netPot and no path moves the remainder. Minimal fix if wanted: pay the remainder to the last claimant of the winning side.","line":636,"path":"src/Conditionals.sol","reproduction":"World 0: alice, bob, carol each stake 1e15 on YES, dave 1e15 on NO; condition true, outcome true. pot = 4e15, burned = 8e13, builderFee = 4e13, netPot = 3.88e15. Each winner claims 1293333333333333; sum = 3879999999999999 < netPot = 3880000000000000; 1 wei remains with claimable() == 0 for everyone (scratch test test_dustLocked on this tree).","severity":"info","snippet":"            if (mine != 0) amount += mine * m.netPot / m.pools[w][m.winningSide];","title":"Pro-rata payouts round down per claimant and the dust is never sweepable, so up to (winners - 1) wei per settled market stays in the contract forever"},{"citation":"resolved","description":"Merged from write_foundry_tests and audit_flow. onFailure deletes the ask and decrements openAsks to zero, and _askable (line 531) returns true as soon as openAsks[q] == 0 regardless of lastAskAt. The README ('anyone can ask again, paying the 0.5 IMD') and test_failureThenRetryPaidByCaller encode the immediate re-ask; the comments encode a one-day wait. The caller pays each retry, so no third party loses; resolve the comment or the code.","line":415,"path":"src/Conditionals.sol","reproduction":"createMarket; warp to resolveTime; requestResolution(id); intake.fail(conditionAsk, 1, 'body_refused'); in the same block askable(id, 0) returns true and requestResolution(id) succeeds, pulling 0.5 IMD from the caller. Expected per line 415: askable false until lastAskAt[0] + 1 day.","severity":"info","snippet":"    ///         Unsigned, so it only clears the ask: the question can be asked again after RETRY_DELAY.","title":"onFailure's NatSpec says a failed question can be asked again after RETRY_DELAY; _askable allows it in the same block"},{"citation":"resolved","description":"From audit_permissions. A creator with no stake who calls claim to recover the budget runs _settle (burn, fee, budget return) and then hits NothingToClaim, undoing it all; only settle() or a staker's claim completes it. Gas only; no funds at risk.","line":451,"path":"src/Conditionals.sol","reproduction":"alice 10 IMD world0/YES, bob 10 IMD world0/NO; both answers delivered. vm.prank(creator); c.claim(id) reverts NothingToClaim and getMarket(id).status is still Open afterwards (scratch test test_claimByNonStakerRevertsAfterSettling on this tree).","severity":"info","snippet":"        if (amount == 0) revert NothingToClaim();","title":"claim() settles the market and then reverts NothingToClaim for a caller without a payout, rolling the settlement back"},{"citation":"resolved","description":"From write_foundry_tests. Markets snapshot their oracle configuration but not the fee recipient. The brief lets the owner set the recipient and the amount is fixed, so stakes are untouched; noted because the brief also says the owner cannot touch open markets, and because with finding 1 the recipient is the party with an incentive to dust one-sided worlds. Pinned by test_feeRecipientIsReadAtSettlement.","line":613,"path":"src/Conditionals.sol","reproduction":"Market open with 10/10 IMD on world 0; owner setFeeRecipient(next); resolve (true,false) and settle: next receives 0.2 IMD, the recipient at creation receives 0.","severity":"info","snippet":"        if (builderFee != 0) imd.safeTransfer(feeRecipient, builderFee);","title":"feeRecipient is read at settlement, not snapshotted, so setFeeRecipient redirects the 1% of markets already open"}],"hash":"fe3b1670d415104693333f562f5e8bdd8d0c9aa993fb971eb98fea22cce92c76","nodeId":"f25d6cb4-098b-46ac-897e-69901b922e62","outcome":"completed","summary":"The review is complete. The findings file is in place at the repository root, the working tree is otherwise clean, and the scratch directory is removed.\n\n**Outcome.** I kept nine findings from the specialists, merged into one medium, four lows and four notes, and added nothing new: my own pass over the code found no defect the four areas missed. The deployment path holds: runtime is 18,661 bytes, the constructor makes no external calls, and the manifest names the brief's addresses with `$owner`.\n\n**What I confirmed against the live chain.** The IMD token on Robinhood Chain answers `blocked(address)` and `transfersEnabled()`, so the blocklist premise behind the push-transfer finding is real. The Intake's current price is 0.5 IMD and the oracle signer is an EOA.\n\n**Findings, by severity.**\n- **Medium, merged from five reports.** A 0.001 IMD stake on the empty side of a one-sided world switches it from the fee-free refund to a 3% fee on the whole pot. The sole correct side gets back 970.001 IMD on 1000 staked, and the fee recipient earns 1% of the pot for 0.001 IMD. Proof attached; it fails on this tree.\n- **Low.** Settlement and void push IMD to the burn address, the fee recipient and the creator inside the stakers' call, so a blocked receiver reverts settle, claim and void. Proof attached. The creator case is recoverable by asking the oracle first, which keeps it low.\n- **Low, merged from three reports.** Asks pay the snapshotted price and never read the live one. A higher price strands open markets until void, and a lower one is silently overpaid from the budget.\n- **Low.** The callback omits the reference's `issuedAt` tolerance check, so an attestation dated a year ahead is accepted.\n- **Low, merged.** A creator racing an owner's config activation gets a signer, intake, action or panel it never saw, since `maxBudget` pins only the price, and pending configs never expire.\n- **Info.** Locked rounding dust, the `onFailure` comment that contradicts the immediate re-ask, `claim` settling and then reverting for a non-staker, and the fee recipient being read at settlement.\n\n**Coverage.** All 14 entry points are answered, plus three invariant rows for deployability, answer binding and conservation.","treeHash":null,"usage":{"cachedInputTokens":1849849,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":35482,"runtime":"claude","turns":27,"wallClockMs":570510}}],"verification":[{"checks":[{"durationMs":32088,"exitCode":0,"name":"build","output":"Compiling 56 files with Solc 0.8.26\nSolc 0.8.26 finished in 31.97s\nCompiler run successful!\n","passed":true},{"durationMs":4303,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/ConditionalsFork.t.sol:ConditionalsForkTest\n[PASS] test_fork_realIntakeAcceptsTheAsks() (gas: 3375)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 456.74µs (81.66µs CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] test_acceptsAFreshSignatureFromTheVectorKeyAsDelivered() (gas: 2463715)\n[PASS] test_callbackSelectorIsCanonical() (gas: 696)\n[PASS] test_digestMatchesTheProtocol() (gas: 10367)\n[PASS] test_protocolSignatureVerifiesAgainstTheContractsDigest() (gas: 20527)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 2.85ms (2.30ms CPU time)\n\nRan 49 tests for test/Conditionals.t.sol:ConditionalsTest\n[PASS] testFuzz_payoutsConserveFunds(uint96[8],bool,bool) (runs: 512, μ: 3101253, ~: 3121539)\nLogs:\n  Bound result 517767090460757084\n  Bound result 936116161410357792\n  Bound result 1633165023325\n  Bound result 3\n  Bound result 956\n  Bound result 708291819061493\n  Bound result 4870828139005\n  Bound result 44669950828345465720\n\n[PASS] test_acceptsAnErc1271SignerRegistry() (gas: 4215615)\n[PASS] test_answerAfterVoidIsIgnored() (gas: 3017156)\n[PASS] test_answerIsAcceptedOnlyOnce() (gas: 3017071)\n[PASS] test_callbackFitsTheIntakeGasLimit() (gas: 2833141)\n[PASS] test_callbackSelectorsMatchTheIntakeDocs() (gas: 7181)\n[PASS] test_cannotRequestAfterVoidAfter() (gas: 582864)\n[PASS] test_cannotVoidASettleableMarket() (gas: 3246929)\n[PASS] test_cannotVoidWhileAnAskIsLive() (gas: 3006092)\n[PASS] test_claimBeforeAnswersReverts() (gas: 1100204)\n[PASS] test_claimTwiceReverts() (gas: 3235777)\n[PASS] test_claimableMatchesClaim() (gas: 3258924)\n[PASS] test_conditionFalseOutcomeNo() (gas: 3461831)\n[PASS] test_conditionFalseOutcomeYes() (gas: 3504554)\n[PASS] test_conditionFalseWaitsForTheOutcome() (gas: 3991975)\n[PASS] test_conditionTrueOutcomeNo() (gas: 3432735)\n[PASS] test_conditionTrueOutcomeYes() (gas: 3490320)\n[PASS] test_constructorValidation() (gas: 12751)\n[PASS] test_createMarketTakesBudgetAndSnapshotsConfig() (gas: 652830)\n[PASS] test_createRejectsBadText() (gas: 2865515)\n[PASS] test_createRejectsBadTimes() (gas: 813049)\n[PASS] test_createRejectsBudgetAboveTheCreatorsCap() (gas: 1153981)\n[PASS] test_documentedQuestionHashExample() (gas: 682)\n[PASS] test_domainSeparatorMatchesOracleDomain() (gas: 11061)\n[PASS] test_failureThenRetryPaidByCaller() (gas: 4330873)\n[PASS] test_feeRecipient() (gas: 2963545)\n[PASS] test_oneSidedWorldIsRefundedWithoutFee() (gas: 3128465)\n[PASS] test_oracleConfigBounds() (gas: 239370)\n[PASS] test_oracleConfigChangeIsDelayedAndOnlyAffectsNewMarkets() (gas: 4275227)\n[PASS] test_outcomeAnsweredBeforeCondition() (gas: 3221827)\n[PASS] test_questionDocumentIsCanonical() (gas: 604915)\n[PASS] test_questionHashMatchesLiveOracleQuote() (gas: 686661)\n[PASS] test_rejectsAnswerToAnotherQuestion() (gas: 3653171)\n[PASS] test_rejectsCallsNotFromTheIntake() (gas: 2855853)\n[PASS] test_rejectsExpiredWrongTypeOrMalformedAnswer() (gas: 3352641)\n[PASS] test_rejectsTamperedFields() (gas: 2829936)\n[PASS] test_rejectsWeakPanel() (gas: 3077146)\n[PASS] test_rejectsWrongSigner() (gas: 3143908)\n[PASS] test_requestBodyIsTheDocumentPlusPanelTerms() (gas: 612166)\n[PASS] test_requestRules() (gas: 2301424)\n[PASS] test_requestSendsTheBodyAndPaysFromBudget() (gas: 2568389)\n[PASS] test_rotatedActionIsUsedByNewMarkets() (gas: 4697520)\n[PASS] test_rotatedSignerCannotAnswerAnOlderMarket() (gas: 3111254)\n[PASS] test_stakeRecordsPoolsAndStakes() (gas: 945992)\n[PASS] test_stakeRejections() (gas: 756990)\n[PASS] test_unansweredAskCanBeRetriedAfterDelay() (gas: 5088353)\n[PASS] test_voidAllowedOnceTheLiveAskIsStale() (gas: 5177390)\n[PASS] test_voidRefundsEverythingAndBudget() (gas: 1513531)\n[PASS] test_world1YesWinsWhenOutcomeTrue() (gas: 2981815)\nSuite result: ok. 49 passed; 0 failed; 0 skipped; finished in 142.04ms (258.10ms CPU time)\n\nRan 1 test for test/ConditionalsInvariant.t.sol:ConditionalsInvariantTest\n[PASS]\nConditionalsInvariantTest invariants:\n[PASS] invariant_conservation\n[PASS] invariant_feesBounded\n[PASS] invariant_solvent\n ConditionalsInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭----------+--------------+-------+---------+----------╮\n| Contract | Selector     | Calls | Reverts | Discards |\n+======================================================+\n| Handler  | answer       | 1001  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | claim        | 1007  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | create       | 1037  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | failAsk      | 1058  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | request      | 994   | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | settleOrVoid | 1039  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | stake        | 1034  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | warp         | 1022  | 0       | 0        |\n╰----------+--------------+-------+---------+----------╯\n\nLogs:\n  Bound result 8\n  Bound result 8\n  Bound result 2491\n  Bound result 15\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1000000000000000\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 999999000003338429893\n  Bound result 1\n  Bound result 1\n  Bound result 999999000000000008563\n  Bound result 110279\n  Bound result 8798\n  Bound result 102\n  Bound result 102\n  Bound result 1\n  Bound result 2\n  Bound result 2\n  Bound result 1\n  Bound result 1\n  Bound result 4388\n  Bound result 388\n  Bound result 11129\n  Bound result 621796\n  Bound result 2\n  Bound result 13\n  Bound result 278374\n  Bound result 156931\n  Bound result 18324\n  Bound result 5\n  Bound result 5\n  Bound result 5\n  Bound result 4\n  Bound result 5\n  Bound result 5\n  Bound result 7\n  Bound result 4660\n  Bound result 6\n  Bound result 2\n  Bound result 6\n  Bound result 15017\n  Bound result 2321\n  Bound result 10727\n  Bound result 2230\n  Bound result 2\n  Bound result 5\n  Bound result 1\n  Bound result 7\n  Bound result 86551243693061822409\n  Bound result 7\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.15s (4.14s CPU time)\n\nRan 4 test suites in 4.15s (4.29s CPU time): 55 tests passed, 0 failed, 0 skipped (55 total tests)\n","passed":true},{"durationMs":113,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"Conditionals.acceptOwnership()\",\"Conditionals.activateOracleConfig()\",\"Conditionals.claim(uint256)\",\"Conditionals.createMarket(string,string,string,uint64,uint64,uint256)\",\"Conditionals.onFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"Conditionals.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"Conditionals.proposeOracleConfig((address,address,uint128,uint16,uint16,bytes32))\",\"Conditionals.renounceOwnership()\",\"Conditionals.requestResolution(uint256)\",\"Conditionals.setFeeRecipient(address)\",\"Conditionals.settle(uint256)\",\"Conditionals.stake(uint256,uint8,uint8,uint256)\",\"Conditionals.transferOwnership(address)\",\"Conditionals.voidMarket(uint256)\"],\"files\":{\".gitignore\":9,\".gitmodules\":6,\"ADAPTATION.md\":107,\"HANDOFF.md\":40,\"README.md\":108,\"foundry.lock\":14,\"foundry.toml\":31,\"frontend/.gitignore\":42,\"frontend/eslint.config.mjs\":18,\"frontend/next.config.ts\":14,\"frontend/package.json\":32,\"frontend/pnpm-lock.yaml\":4541,\"frontend/pnpm-workspace.yaml\":3,\"frontend/scripts/gen-abi.mjs\":14,\"frontend/src/app/api/check/route.ts\":37,\"frontend/src/app/globals.css\":264,\"frontend/src/app/icon.svg\":1,\"frontend/src/app/layout.tsx\":38,\"frontend/src/app/page.tsx\":70,\"frontend/src/components/CreateModal.tsx\":354,\"frontend/src/components/Featured.tsx\":96,\"frontend/src/components/ForkCanvas.tsx\":234,\"frontend/src/components/Glyphs.tsx\":120,\"frontend/src/components/Header.tsx\":72,\"frontend/src/components/Hero.tsx\":89,\"frontend/src/components/MarketCard.tsx\":85,\"frontend/src/components/MarketDrawer.tsx\":486,\"frontend/src/components/Markets.tsx\":88,\"frontend/src/components/Providers.tsx\":45,\"frontend/src/components/Sections.tsx\":237,\"frontend/src/components/Stats.tsx\":52,\"frontend/src/hooks/useDemoMarkets.ts\":183,\"frontend/src/hooks/useLiveMarkets.ts\":274,\"frontend/src/hooks/useNow.ts\":15,\"frontend/src/lib/abi.ts\":2163,\"frontend/src/lib/config.ts\":54,\"frontend/src/lib/demo.ts\":147,\"frontend/src/lib/format.ts\":46,\"frontend/src/lib/market.ts\":102,\"frontend/src/lib/types.ts\":82,\"frontend/tsconfig.json\":34,\"launch/buy-imd.sh\":27,\"launch/launch.template.json\":15,\"launch/pay.sh\":52,\"src/Conditionals.sol\":767,\"src/interfaces/IIntake.sol\":20,\"src/interfaces/IOracleAttestation.sol\":24,\"test/Conditionals.t.sol\":953,\"test/ConditionalsFork.t.sol\":50,\"test/ConditionalsInvariant.t.sol\":200,\"test/OracleConformance.t.sol\":145,\"test/utils/Fixture.sol\":187,\"test/utils/Mocks.sol\":111},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":6615,"exitCode":0,"name":"slither","output":"[medium/medium] uninitialized-local at src/Conditionals.sol:597: Conditionals._settle(uint256,Conditionals.Market).burned (src/Conditionals.sol#597) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Conditionals.sol:598: Conditionals._settle(uint256,Conditionals.Market).builderFee (src/Conditionals.sol#598) is a local variable never initialized\n[low/medium] calls-loop at src/Conditionals.sol:551: Conditionals._ask(uint256,Conditionals.Market,uint8) (src/Conditionals.sol#551-585) has external calls inside a loop: requestId = config.intake.requestWithFailure(config.action,body,IIntake.Callback({target:address(this),selector:this.onOracleResult.selector}),this.onFailure.selector,address(imd),price) (src/Conditionals.sol#567-575)\n[low/medium] reentrancy-benign at src/Conditionals.sol:551: Reentrancy in Conditionals._ask(uint256,Conditionals.Market,uint8) (src/Conditionals.sol#551-585):\n[low/medium] timestamp at src/Conditionals.sol:479: Conditionals.askable(uint256,uint8) (src/Conditionals.sol#479-484) uses timestamp for comparisons\n[low/medium] timestamp at src/Conditionals.sol:540: Conditionals._voidable(Conditionals.Market) (src/Conditionals.sol#540-549) uses timestamp for comparisons\n[low/medium] timestamp at src/Conditionals.sol:279: Conditionals.activateOracleConfig() (src/Conditionals.sol#279-286) uses timestamp for comparisons\n[low/medium] timestamp at src/Conditionals.sol:344: Conditionals.stake(uint256,uint8,uint8,uint256) (src/Conditionals.sol#344-356) uses timestamp for comparisons\n[low/medium] timestamp at src/Conditionals.sol:366: Conditionals.requestResolution(uint256) (src/Conditionals.sol#366-379) uses timestamp for comparisons\n[low/medium] timestamp at src/Conditionals.sol:529: Conditionals._askable(Conditionals.Market,uint8) (src/Conditionals.sol#529-532) uses timestamp for comparisons\n[low/medium] timestamp at src/Conditionals.sol:382: Conditionals.onOracleResult(bytes32,IOracleAttestation.Attestation,bytes) (src/Conditionals.sol#382-412) uses timestamp for comparisons\n[low/medium] timestamp at src/Conditionals.sol:308: Conditionals.createMarket(string,string,string,uint64,uint64,uint256) (src/Conditionals.sol#308-341) uses timestamp for comparisons","passed":true},{"durationMs":863,"exitCode":0,"name":"aderyn","output":"[low] centralization-risk at src/Conditionals.sol:36: Centralization Risk (4 places)\n[low] costly-loop at src/Conditionals.sol:372: Costly operations inside loop\n[low] large-numeric-literal at src/Conditionals.sol:48: Large Numeric Literal\n[low] literal-instead-of-constant at src/Conditionals.sol:747: Literal Instead of Constant (2 places)\n[low] require-revert-in-loop at src/Conditionals.sol:372: Loop Contains `require`/`revert` (2 places)\n[low] uninitialized-local-variable at src/Conditionals.sol:745: Uninitialized Local Variable (2 places)\n[low] unused-public-function at src/Conditionals.sol:503: Public Function Not Used Internally (2 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"36b28f99cb08768d8c81ac5c7d14194892516e33048ed9aa25e0e91eafcb0f4c","verifiedTreeHash":"b61ddcda8379efd634988ddedbfd9879761847cf","verifierVersion":"0.1.0+6a374fcd"},{"checks":[{"durationMs":35546,"exitCode":0,"name":"build","output":"Compiling 56 files with Solc 0.8.26\nSolc 0.8.26 finished in 35.42s\nCompiler run successful!\n","passed":true},{"durationMs":4010,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/ConditionalsFork.t.sol:ConditionalsForkTest\n[PASS] test_fork_realIntakeAcceptsTheAsks() (gas: 3375)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 483.12µs (101.50µs CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] test_acceptsAFreshSignatureFromTheVectorKeyAsDelivered() (gas: 2479701)\n[PASS] test_callbackSelectorIsCanonical() (gas: 696)\n[PASS] test_digestMatchesTheProtocol() (gas: 10367)\n[PASS] test_protocolSignatureVerifiesAgainstTheContractsDigest() (gas: 20527)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 174.61ms (2.38ms CPU time)\n\nRan 60 tests for test/Conditionals.t.sol:ConditionalsTest\n[PASS] testFuzz_payoutsConserveFunds(uint96[8],bool,bool) (runs: 512, μ: 3141246, ~: 3163613)\nLogs:\n  Bound result 18763\n  Bound result 18863\n  Bound result 78656\n  Bound result 38716\n  Bound result 74069604400909906164\n  Bound result 12340\n  Bound result 554\n  Bound result 6164\n\n[PASS] testFuzz_winnerNeverNetsLessThanStaked(uint96,uint96,bool) (runs: 512, μ: 2964317, ~: 2964442)\nLogs:\n  Bound result 999999000000000014417\n  Bound result 999999000000000015572\n\n[PASS] test_acceptsAnErc1271SignerRegistry() (gas: 4267366)\n[PASS] test_answerAfterVoidIsIgnored() (gas: 3060199)\n[PASS] test_answerIsAcceptedOnlyOnce() (gas: 3055886)\n[PASS] test_askPaysTheLivePriceWhenItIsLower() (gas: 4373160)\n[PASS] test_askPaysTheSnapshotWithoutALivePrice() (gas: 2376782)\n[PASS] test_askRevertsWhenTheLivePriceIsHigher() (gas: 1436729)\n[PASS] test_blockedCreatorDoesNotBlockVoid() (gas: 1695512)\n[PASS] test_blockedFeeRecipientDoesNotBlockSettlement() (gas: 3224143)\n[PASS] test_blockedStakerOnlyBlocksTheirOwnClaim() (gas: 3494784)\n[PASS] test_callbackFitsTheIntakeGasLimit() (gas: 2871560)\n[PASS] test_callbackSelectorsMatchTheIntakeDocs() (gas: 7467)\n[PASS] test_cannotRequestAfterVoidAfter() (gas: 587541)\n[PASS] test_cannotVoidASettleableMarket() (gas: 3295838)\n[PASS] test_cannotVoidWhileAnAskIsLive() (gas: 3041719)\n[PASS] test_claimBeforeAnswersReverts() (gas: 1127218)\n[PASS] test_claimTwiceReverts() (gas: 3284801)\n[PASS] test_claimableMatchesClaim() (gas: 3309789)\n[PASS] test_conditionFalseOutcomeNo() (gas: 3519582)\n[PASS] test_conditionFalseOutcomeYes() (gas: 3564550)\n[PASS] test_conditionFalseWaitsForTheOutcome() (gas: 4043134)\n[PASS] test_conditionTrueOutcomeNo() (gas: 3494814)\n[PASS] test_conditionTrueOutcomeYes() (gas: 3548148)\n[PASS] test_constructorValidation() (gas: 12751)\n[PASS] test_createMarketTakesBudgetAndSnapshotsConfig() (gas: 657397)\n[PASS] test_createRejectsBadText() (gas: 2870164)\n[PASS] test_createRejectsBadTimes() (gas: 817544)\n[PASS] test_createRejectsBudgetAboveTheCreatorsCap() (gas: 1158907)\n[PASS] test_documentedQuestionHashExample() (gas: 682)\n[PASS] test_domainSeparatorMatchesOracleDomain() (gas: 11347)\n[PASS] test_failureThenRetryPaidByCaller() (gas: 4389939)\n[PASS] test_feeIsCappedAtTheLosingPool() (gas: 3170639)\n[PASS] test_feeIsThreePercentOfThePotWhenTheLosingPoolCoversIt() (gas: 5686629)\n[PASS] test_feeRecipient() (gas: 2996814)\n[PASS] test_oneSidedWorldIsRefundedWithoutFee() (gas: 3173931)\n[PASS] test_oracleConfigBounds() (gas: 239937)\n[PASS] test_oracleConfigChangeIsDelayedAndOnlyAffectsNewMarkets() (gas: 4329061)\n[PASS] test_outcomeAnsweredBeforeCondition() (gas: 3270851)\n[PASS] test_pendingConfigExpires() (gas: 336654)\n[PASS] test_questionDocumentIsCanonical() (gas: 609322)\n[PASS] test_questionHashMatchesLiveOracleQuote() (gas: 691156)\n[PASS] test_rejectsAnswerToAnotherQuestion() (gas: 3696331)\n[PASS] test_rejectsCallsNotFromTheIntake() (gas: 2894216)\n[PASS] test_rejectsExpiredWrongTypeOrMalformedAnswer() (gas: 3391638)\n[PASS] test_rejectsIssuedAtInTheFuture() (gas: 3282607)\n[PASS] test_rejectsTamperedFields() (gas: 2868443)\n[PASS] test_rejectsWeakPanel() (gas: 3115657)\n[PASS] test_rejectsWrongSigner() (gas: 3183187)\n[PASS] test_requestBodyIsTheDocumentPlusPanelTerms() (gas: 616705)\n[PASS] test_requestRules() (gas: 2317456)\n[PASS] test_requestSendsTheBodyAndPaysFromBudget() (gas: 2584556)\n[PASS] test_rotatedActionIsUsedByNewMarkets() (gas: 4729377)\n[PASS] test_rotatedSignerCannotAnswerAnOlderMarket() (gas: 3149930)\n[PASS] test_stakeRecordsPoolsAndStakes() (gas: 964206)\n[PASS] test_stakeRejections() (gas: 761925)\n[PASS] test_unansweredAskCanBeRetriedAfterDelay() (gas: 5151687)\n[PASS] test_voidAllowedOnceTheLiveAskIsStale() (gas: 5236286)\n[PASS] test_voidRefundsEverythingAndBudget() (gas: 1563055)\n[PASS] test_world1YesWinsWhenOutcomeTrue() (gas: 3015033)\nSuite result: ok. 60 passed; 0 failed; 0 skipped; finished in 174.87ms (481.17ms CPU time)\n\nRan 1 test for test/ConditionalsInvariant.t.sol:ConditionalsInvariantTest\n[PASS]\nConditionalsInvariantTest invariants:\n[PASS] invariant_conservation\n[PASS] invariant_feesBounded\n[PASS] invariant_solvent\n ConditionalsInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭----------+--------------+-------+---------+----------╮\n| Contract | Selector     | Calls | Reverts | Discards |\n+======================================================+\n| Handler  | answer       | 1001  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | claim        | 1007  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | create       | 1037  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | failAsk      | 1058  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | request      | 994   | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | settleOrVoid | 1039  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | stake        | 1034  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | warp         | 1022  | 0       | 0        |\n╰----------+--------------+-------+---------+----------╯\n\nLogs:\n  Bound result 8\n  Bound result 8\n  Bound result 2710\n  Bound result 15\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1000000000000000\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 999999000003628442805\n  Bound result 1\n  Bound result 1\n  Bound result 999999000000000000117\n  Bound result 32000\n  Bound result 9009\n  Bound result 102\n  Bound result 102\n  Bound result 2\n  Bound result 2\n  Bound result 2\n  Bound result 2\n  Bound result 2\n  Bound result 6174\n  Bound result 174\n  Bound result 11498\n  Bound result 246\n  Bound result 2\n  Bound result 13\n  Bound result 278374\n  Bound result 156931\n  Bound result 18324\n  Bound result 5\n  Bound result 5\n  Bound result 5\n  Bound result 5\n  Bound result 2\n  Bound result 5\n  Bound result 432000\n  Bound result 9\n  Bound result 3\n  Bound result 2\n  Bound result 6\n  Bound result 15668\n  Bound result 13526\n  Bound result 12138\n  Bound result 2233\n  Bound result 4\n  Bound result 5\n  Bound result 1\n  Bound result 3\n  Bound result 999999000000000017858\n  Bound result 7\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.86s (3.86s CPU time)\n\nRan 4 test suites in 3.86s (4.21s CPU time): 66 tests passed, 0 failed, 0 skipped (66 total tests)\n","passed":true},{"durationMs":103,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"Conditionals.acceptOwnership()\",\"Conditionals.activateOracleConfig()\",\"Conditionals.claim(uint256)\",\"Conditionals.createMarket(string,string,string,uint64,uint64,uint256)\",\"Conditionals.onFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"Conditionals.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"Conditionals.proposeOracleConfig((address,address,uint128,uint16,uint16,bytes32))\",\"Conditionals.renounceOwnership()\",\"Conditionals.requestResolution(uint256)\",\"Conditionals.setFeeRecipient(address)\",\"Conditionals.settle(uint256)\",\"Conditionals.stake(uint256,uint8,uint8,uint256)\",\"Conditionals.transferOwnership(address)\",\"Conditionals.voidMarket(uint256)\",\"Conditionals.withdrawOwed()\"],\"files\":{\".gitignore\":9,\".gitmodules\":6,\"ADAPTATION.md\":207,\"HANDOFF.md\":40,\"README.md\":119,\"foundry.lock\":14,\"foundry.toml\":31,\"frontend/.gitignore\":42,\"frontend/eslint.config.mjs\":18,\"frontend/next.config.ts\":14,\"frontend/package.json\":32,\"frontend/pnpm-lock.yaml\":4541,\"frontend/pnpm-workspace.yaml\":3,\"frontend/scripts/gen-abi.mjs\":14,\"frontend/src/app/api/check/route.ts\":37,\"frontend/src/app/globals.css\":264,\"frontend/src/app/icon.svg\":1,\"frontend/src/app/layout.tsx\":38,\"frontend/src/app/page.tsx\":70,\"frontend/src/components/CreateModal.tsx\":354,\"frontend/src/components/Featured.tsx\":96,\"frontend/src/components/ForkCanvas.tsx\":234,\"frontend/src/components/Glyphs.tsx\":120,\"frontend/src/components/Header.tsx\":72,\"frontend/src/components/Hero.tsx\":89,\"frontend/src/components/MarketCard.tsx\":85,\"frontend/src/components/MarketDrawer.tsx\":487,\"frontend/src/components/Markets.tsx\":88,\"frontend/src/components/Providers.tsx\":45,\"frontend/src/components/Sections.tsx\":237,\"frontend/src/components/Stats.tsx\":52,\"frontend/src/hooks/useDemoMarkets.ts\":189,\"frontend/src/hooks/useLiveMarkets.ts\":274,\"frontend/src/hooks/useNow.ts\":15,\"frontend/src/lib/abi.ts\":2264,\"frontend/src/lib/config.ts\":54,\"frontend/src/lib/demo.ts\":147,\"frontend/src/lib/format.ts\":46,\"frontend/src/lib/market.ts\":107,\"frontend/src/lib/types.ts\":82,\"frontend/tsconfig.json\":34,\"launch/buy-imd.sh\":27,\"launch/launch.template.json\":15,\"launch/pay.sh\":52,\"src/Conditionals.sol\":821,\"src/interfaces/IIntake.sol\":26,\"src/interfaces/IOracleAttestation.sol\":24,\"test/Conditionals.t.sol\":1172,\"test/ConditionalsFork.t.sol\":50,\"test/ConditionalsInvariant.t.sol\":200,\"test/OracleConformance.t.sol\":145,\"test/utils/Fixture.sol\":187,\"test/utils/Mocks.sol\":141},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":6537,"exitCode":0,"name":"slither","output":"[medium/medium] reentrancy-no-eth at src/Conditionals.sol:623: Reentrancy in Conditionals._settle(uint256,Conditionals.Market) (src/Conditionals.sol#623-658):\n[medium/medium] reentrancy-no-eth at src/Conditionals.sol:623: Reentrancy in Conditionals._settle(uint256,Conditionals.Market) (src/Conditionals.sol#623-658):\n[medium/medium] uninitialized-local at src/Conditionals.sol:633: Conditionals._settle(uint256,Conditionals.Market).burned (src/Conditionals.sol#633) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Conditionals.sol:634: Conditionals._settle(uint256,Conditionals.Market).builderFee (src/Conditionals.sol#634) is a local variable never initialized\n[low/medium] calls-loop at src/Conditionals.sol:580: Conditionals._askPrice(Conditionals.OracleConfig) (src/Conditionals.sol#580-585) has external calls inside a loop: live = IIntakePrice(address(config.intake)).priceOf(config.action,address(imd)) (src/Conditionals.sol#582-584)\n[low/medium] calls-loop at src/Conditionals.sol:587: Conditionals._ask(uint256,Conditionals.Market,uint8) (src/Conditionals.sol#587-621) has external calls inside a loop: requestId = config.intake.requestWithFailure(config.action,body,IIntake.Callback({target:address(this),selector:this.onOracleResult.selector}),this.onFailure.selector,address(imd),price) (src/Conditionals.sol#603-611)\n[low/medium] reentrancy-benign at src/Conditionals.sol:587: Reentrancy in Conditionals._ask(uint256,Conditionals.Market,uint8) (src/Conditionals.sol#587-621):\n[low/medium] reentrancy-benign at src/Conditionals.sol:459: Reentrancy in Conditionals.claim(uint256) (src/Conditionals.sol#459-471):\n[low/medium] reentrancy-benign at src/Conditionals.sol:671: Reentrancy in Conditionals._push(address,uint256) (src/Conditionals.sol#671-677):\n[low/medium] timestamp at src/Conditionals.sol:381: Conditionals.requestResolution(uint256) (src/Conditionals.sol#381-394) uses timestamp for comparisons\n[low/medium] timestamp at src/Conditionals.sol:397: Conditionals.onOracleResult(bytes32,IOracleAttestation.Attestation,bytes) (src/Conditionals.sol#397-428) uses timestamp for comparisons\n[low/medium] timestamp at src/Conditionals.sol:359: Conditionals.stake(uint256,uint8,uint8,uint256) (src/Conditionals.sol#359-371) uses timestamp for comparisons\n[low/medium] timestamp at src/Conditionals.sol:504: Conditionals.askable(uint256,uint8) (src/Conditionals.sol#504-509) uses timestamp for comparisons\n[low/medium] timestamp at src/Conditionals.sol:293: Conditionals.activateOracleConfig() (src/Conditionals.sol#293-301) uses timestamp for comparisons\n[low/medium] timestamp at src/Conditionals.sol:323: Conditionals.createMarket(string,string,string,uint64,uint64,uint256) (src/Conditionals.sol#323-356) uses timestamp for comparisons\n[low/medium] timestamp at src/Conditionals.sol:565: Conditionals._voidable(Conditionals.Market) (src/Conditionals.sol#565-574) uses timestamp for comparisons\n[low/medium] timestamp at src/Conditionals.sol:554: Conditionals._askable(Conditionals.Market,uint8) (src/Conditionals.sol#554-557) uses timestamp for comparisons","passed":true},{"durationMs":860,"exitCode":0,"name":"aderyn","output":"[low] centralization-risk at src/Conditionals.sol:37: Centralization Risk (4 places)\n[low] costly-loop at src/Conditionals.sol:387: Costly operations inside loop\n[low] large-numeric-literal at src/Conditionals.sol:49: Large Numeric Literal\n[low] literal-instead-of-constant at src/Conditionals.sol:801: Literal Instead of Constant (2 places)\n[low] require-revert-in-loop at src/Conditionals.sol:387: Loop Contains `require`/`revert` (2 places)\n[low] uninitialized-local-variable at src/Conditionals.sol:799: Uninitialized Local Variable (2 places)\n[low] unsafe-erc20-operation at src/Conditionals.sol:673: Unsafe ERC20 Operation\n[low] unused-public-function at src/Conditionals.sol:528: Public Function Not Used Internally (2 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"3ceaf9349a7d594a1637d41f4fa77fdbc58eb2a3147aea34982eeb2ac6530782","verifiedTreeHash":"0c1b5f5e90ed97c376d659c9acb2406e1205d6d0","verifierVersion":"0.1.0+6a374fcd"},{"checks":[{"durationMs":61441,"exitCode":0,"name":"build","output":"Compiling 60 files with Solc 0.8.26\nSolc 0.8.26 finished in 61.32s\nCompiler run successful!\n","passed":true},{"durationMs":10640,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/ConditionalsFork.t.sol:ConditionalsForkTest\n[PASS] test_fork_realIntakeAcceptsTheAsks() (gas: 3375)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 336.64µs (80.34µs CPU time)\n\nRan 60 tests for test/Conditionals.t.sol:ConditionalsTest\n[PASS] testFuzz_payoutsConserveFunds(uint96[8],bool,bool) (runs: 512, μ: 3141246, ~: 3163613)\nLogs:\n  Bound result 18120\n  Bound result 31103\n  Bound result 1998\n  Bound result 17126\n  Bound result 9913\n  Bound result 11723\n  Bound result 9996\n  Bound result 92044410\n\n[PASS] testFuzz_winnerNeverNetsLessThanStaked(uint96,uint96,bool) (runs: 512, μ: 2964317, ~: 2964442)\nLogs:\n  Bound result 999999000000000014417\n  Bound result 999999000000000015572\n\n[PASS] test_acceptsAnErc1271SignerRegistry() (gas: 4267366)\n[PASS] test_answerAfterVoidIsIgnored() (gas: 3060199)\n[PASS] test_answerIsAcceptedOnlyOnce() (gas: 3055886)\n[PASS] test_askPaysTheLivePriceWhenItIsLower() (gas: 4373160)\n[PASS] test_askPaysTheSnapshotWithoutALivePrice() (gas: 2376782)\n[PASS] test_askRevertsWhenTheLivePriceIsHigher() (gas: 1436729)\n[PASS] test_blockedCreatorDoesNotBlockVoid() (gas: 1695512)\n[PASS] test_blockedFeeRecipientDoesNotBlockSettlement() (gas: 3224143)\n[PASS] test_blockedStakerOnlyBlocksTheirOwnClaim() (gas: 3494784)\n[PASS] test_callbackFitsTheIntakeGasLimit() (gas: 2871560)\n[PASS] test_callbackSelectorsMatchTheIntakeDocs() (gas: 7467)\n[PASS] test_cannotRequestAfterVoidAfter() (gas: 587541)\n[PASS] test_cannotVoidASettleableMarket() (gas: 3295838)\n[PASS] test_cannotVoidWhileAnAskIsLive() (gas: 3041719)\n[PASS] test_claimBeforeAnswersReverts() (gas: 1127218)\n[PASS] test_claimTwiceReverts() (gas: 3284801)\n[PASS] test_claimableMatchesClaim() (gas: 3309789)\n[PASS] test_conditionFalseOutcomeNo() (gas: 3519582)\n[PASS] test_conditionFalseOutcomeYes() (gas: 3564550)\n[PASS] test_conditionFalseWaitsForTheOutcome() (gas: 4043134)\n[PASS] test_conditionTrueOutcomeNo() (gas: 3494814)\n[PASS] test_conditionTrueOutcomeYes() (gas: 3548148)\n[PASS] test_constructorValidation() (gas: 12751)\n[PASS] test_createMarketTakesBudgetAndSnapshotsConfig() (gas: 657397)\n[PASS] test_createRejectsBadText() (gas: 2870164)\n[PASS] test_createRejectsBadTimes() (gas: 817544)\n[PASS] test_createRejectsBudgetAboveTheCreatorsCap() (gas: 1158907)\n[PASS] test_documentedQuestionHashExample() (gas: 682)\n[PASS] test_domainSeparatorMatchesOracleDomain() (gas: 11347)\n[PASS] test_failureThenRetryPaidByCaller() (gas: 4389939)\n[PASS] test_feeIsCappedAtTheLosingPool() (gas: 3170639)\n[PASS] test_feeIsThreePercentOfThePotWhenTheLosingPoolCoversIt() (gas: 5686629)\n[PASS] test_feeRecipient() (gas: 2996814)\n[PASS] test_oneSidedWorldIsRefundedWithoutFee() (gas: 3173931)\n[PASS] test_oracleConfigBounds() (gas: 239937)\n[PASS] test_oracleConfigChangeIsDelayedAndOnlyAffectsNewMarkets() (gas: 4329061)\n[PASS] test_outcomeAnsweredBeforeCondition() (gas: 3270851)\n[PASS] test_pendingConfigExpires() (gas: 336654)\n[PASS] test_questionDocumentIsCanonical() (gas: 609322)\n[PASS] test_questionHashMatchesLiveOracleQuote() (gas: 691156)\n[PASS] test_rejectsAnswerToAnotherQuestion() (gas: 3696331)\n[PASS] test_rejectsCallsNotFromTheIntake() (gas: 2894216)\n[PASS] test_rejectsExpiredWrongTypeOrMalformedAnswer() (gas: 3391638)\n[PASS] test_rejectsIssuedAtInTheFuture() (gas: 3282607)\n[PASS] test_rejectsTamperedFields() (gas: 2868443)\n[PASS] test_rejectsWeakPanel() (gas: 3115657)\n[PASS] test_rejectsWrongSigner() (gas: 3183187)\n[PASS] test_requestBodyIsTheDocumentPlusPanelTerms() (gas: 616705)\n[PASS] test_requestRules() (gas: 2317456)\n[PASS] test_requestSendsTheBodyAndPaysFromBudget() (gas: 2584556)\n[PASS] test_rotatedActionIsUsedByNewMarkets() (gas: 4729377)\n[PASS] test_rotatedSignerCannotAnswerAnOlderMarket() (gas: 3149930)\n[PASS] test_stakeRecordsPoolsAndStakes() (gas: 964206)\n[PASS] test_stakeRejections() (gas: 761925)\n[PASS] test_unansweredAskCanBeRetriedAfterDelay() (gas: 5151687)\n[PASS] test_voidAllowedOnceTheLiveAskIsStale() (gas: 5236286)\n[PASS] test_voidRefundsEverythingAndBudget() (gas: 1563055)\n[PASS] test_world1YesWinsWhenOutcomeTrue() (gas: 3015033)\nSuite result: ok. 60 passed; 0 failed; 0 skipped; finished in 340.33ms (800.58ms CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] test_acceptsAFreshSignatureFromTheVectorKeyAsDelivered() (gas: 2479701)\n[PASS] test_callbackSelectorIsCanonical() (gas: 696)\n[PASS] test_digestMatchesTheProtocol() (gas: 10367)\n[PASS] test_protocolSignatureVerifiesAgainstTheContractsDigest() (gas: 20527)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 382.86ms (18.95ms CPU time)\n\nRan 6 tests for test/ConditionalsQuestion.t.sol:ConditionalsQuestionTest\n[PASS] testFuzz_acceptedTextNeedsNoEscaping(bytes) (runs: 1000, μ: 85924, ~: 62499)\n[PASS] testFuzz_askPinsTheDocumentOfTheWindowItAsked(uint32) (runs: 256, μ: 2703821, ~: 2700566)\nLogs:\n  Bound result 1042010978\n\n[PASS] testFuzz_bodyCarriesTheDocumentAndThePanelTerms(bytes,uint32) (runs: 256, μ: 709974, ~: 690553)\n[PASS] testFuzz_documentMatchesTheReferenceLibrary(bytes,bytes,bytes,uint32,uint32) (runs: 512, μ: 971446, ~: 954116)\nLogs:\n  Bound result 6\n\n[PASS] test_liveApiVectorAgreesWithTheReferenceLibrary() (gas: 902287)\n[PASS] test_referenceLibraryReproducesTheProtocolVector() (gas: 37688)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 382.90ms (1.39s CPU time)\n\nRan 28 tests for test/ConditionalsAdversarial.t.sol:ConditionalsAdversarialTest\n[PASS] testFuzz_winnersSplitTheNetPotWithBoundedDust(uint96,uint96,uint96,uint96,bool) (runs: 1000, μ: 3599965, ~: 3601077)\nLogs:\n  Bound result 71422328063911440318\n  Bound result 228162514343337593543950256\n  Bound result 1071119245516996670458866\n  Bound result 1000000000000000\n\n[PASS] test_answerForAnOlderWindowDoesNotFitTheRetry() (gas: 4038448)\n[PASS] test_answerToTheOtherQuestionOfTheSameMarketIsRefused() (gas: 2610993)\n[PASS] test_constructorRefusesZeroOwnerIntakeOrSigner() (gas: 13682)\n[PASS] test_expiryBoundary() (gas: 2585156)\n[PASS] test_failureAfterAnAnswerIsUnknown() (gas: 2487050)\n[PASS] test_failureFitsTheIntakeGasLimit() (gas: 2272034)\n[PASS] test_failureMovesNoValueAndClearsOnlyItsAsk() (gas: 2753819)\n[PASS] test_feeCapBoundary() (gas: 5736349)\n[PASS] test_feeRecipientIsReadAtSettlement() (gas: 2960495)\n[PASS] test_lastSecondAskDelaysVoidByOneDayAtMost() (gas: 2500505)\n[PASS] test_launchConstructorOnAnEmptyRobinhoodChain() (gas: 312235)\n[PASS] test_lopsidedWinnersAreMadeWholeByTheFeeCap() (gas: 3017928)\n[PASS] test_malformedCallbackLeavesTheAskPending() (gas: 2512035)\n[PASS] test_minimumStakesSettle() (gas: 3173215)\n[PASS] test_ownerOnlyFunctionsRefuseEveryoneElse() (gas: 294725)\n[PASS] test_ownerRotationCannotReachAnOpenMarket() (gas: 4500940)\n[PASS] test_panelChecks() (gas: 2937426)\n[PASS] test_resolutionOpensExactlyAtResolveTimeAndEndsAtVoidAfter() (gas: 726729)\n[PASS] test_retryWithoutPaymentRevertsAndChangesNothing() (gas: 2454696)\n[PASS] test_sameRequestIdFromAnotherIntakeIsUnknown() (gas: 3342372)\n[PASS] test_settledMarketCannotReopenOrBeVoided() (gas: 3245325)\n[PASS] test_signatureForAnotherChainIsRefused() (gas: 2447800)\n[PASS] test_signatureForAnotherDeploymentIsRefused() (gas: 2623811)\n[PASS] test_stakeClosesExactlyAtCloseTime() (gas: 696123)\n[PASS] test_unknownMarketIds() (gas: 906873)\n[PASS] test_voidOpensOnlyAfterVoidAfter() (gas: 958123)\n[PASS] test_voidedMarketCannotBeSettledOrVoidedAgain() (gas: 2953898)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 383.02ms (502.17ms CPU time)\n\nRan 1 test for test/ConditionalsInvariant.t.sol:ConditionalsInvariantTest\n[PASS]\nConditionalsInvariantTest invariants:\n[PASS] invariant_conservation\n[PASS] invariant_feesBounded\n[PASS] invariant_solvent\n ConditionalsInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭----------+--------------+-------+---------+----------╮\n| Contract | Selector     | Calls | Reverts | Discards |\n+======================================================+\n| Handler  | answer       | 1001  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | claim        | 1007  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | create       | 1037  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | failAsk      | 1058  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | request      | 994   | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | settleOrVoid | 1039  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | stake        | 1034  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | warp         | 1022  | 0       | 0        |\n╰----------+--------------+-------+---------+----------╯\n\nLogs:\n  Bound result 180\n  Bound result 180\n  Bound result 2710\n  Bound result 66\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1000000000000000\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 999999000003628442805\n  Bound result 1\n  Bound result 1\n  Bound result 999999000000000000117\n  Bound result 2827\n  Bound result 8499\n  Bound result 102\n  Bound result 102\n  Bound result 1\n  Bound result 2\n  Bound result 2\n  Bound result 1\n  Bound result 1\n  Bound result 6174\n  Bound result 174\n  Bound result 11499\n  Bound result 1672\n  Bound result 3\n  Bound result 13\n  Bound result 278374\n  Bound result 156931\n  Bound result 18324\n  Bound result 5\n  Bound result 5\n  Bound result 5\n  Bound result 5\n  Bound result 5\n  Bound result 5\n  Bound result 45234\n  Bound result 164\n  Bound result 3\n  Bound result 6\n  Bound result 6\n  Bound result 15749\n  Bound result 3064\n  Bound result 11995\n  Bound result 1225\n  Bound result 3\n  Bound result 5\n  Bound result 1\n  Bound result 6\n  Bound result 666339874681738179154\n  Bound result 7\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.12s (4.11s CPU time)\n\nRan 1 test for test/ConditionalsLifecycleInvariant.t.sol:ConditionalsLifecycleInvariantTest\n[PASS]\nConditionalsLifecycleInvariantTest invariants:\n[PASS] invariant_finishedMarketsAndAnswersNeverChange\n[PASS] invariant_forgedAnswersNeverCount\n[PASS] invariant_nobodyTakesMoreThanTheMarketHeld\n[PASS] invariant_solventAndNothingLeftApproved\n ConditionalsLifecycleInvariantTest invariants (runs: 128, calls: 10240, reverts: 0)\n\n╭------------------+-----------------+-------+---------+----------╮\n| Contract         | Selector        | Calls | Reverts | Discards |\n+=================================================================+\n| LifecycleHandler | activate        | 762   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | answer          | 734   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | claim           | 776   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | create          | 742   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | failAsk         | 715   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | forge           | 767   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | request         | 785   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | rotateSigner    | 697   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | setBlocked      | 721   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | setFeeRecipient | 677   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | settleOrVoid    | 724   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | stake           | 702   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | warp            | 720   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | withdrawOwed    | 718   | 0       | 0        |\n╰------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 163\n  Bound result 163\n  Bound result 161\n  Bound result 161\n  Bound result 50412\n  Bound result 612\n  Bound result 117\n  Bound result 117\n  Bound result 126\n  Bound result 15709\n  Bound result 1\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 10.49s (10.48s CPU time)\n\nRan 7 test suites in 10.50s (16.10s CPU time): 101 tests passed, 0 failed, 0 skipped (101 total tests)\n","passed":true},{"durationMs":128,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"Conditionals.acceptOwnership()\",\"Conditionals.activateOracleConfig()\",\"Conditionals.claim(uint256)\",\"Conditionals.createMarket(string,string,string,uint64,uint64,uint256)\",\"Conditionals.onFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"Conditionals.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"Conditionals.proposeOracleConfig((address,address,uint128,uint16,uint16,bytes32))\",\"Conditionals.renounceOwnership()\",\"Conditionals.requestResolution(uint256)\",\"Conditionals.setFeeRecipient(address)\",\"Conditionals.settle(uint256)\",\"Conditionals.stake(uint256,uint8,uint8,uint256)\",\"Conditionals.transferOwnership(address)\",\"Conditionals.voidMarket(uint256)\",\"Conditionals.withdrawOwed()\"],\"files\":{\".gitignore\":9,\".gitmodules\":6,\"ADAPTATION.md\":207,\"HANDOFF.md\":40,\"README.md\":119,\"foundry.lock\":14,\"foundry.toml\":31,\"frontend/.gitignore\":42,\"frontend/eslint.config.mjs\":18,\"frontend/next.config.ts\":14,\"frontend/package.json\":32,\"frontend/pnpm-lock.yaml\":4541,\"frontend/pnpm-workspace.yaml\":3,\"frontend/scripts/gen-abi.mjs\":14,\"frontend/src/app/api/check/route.ts\":37,\"frontend/src/app/globals.css\":264,\"frontend/src/app/icon.svg\":1,\"frontend/src/app/layout.tsx\":38,\"frontend/src/app/page.tsx\":70,\"frontend/src/components/CreateModal.tsx\":354,\"frontend/src/components/Featured.tsx\":96,\"frontend/src/components/ForkCanvas.tsx\":234,\"frontend/src/components/Glyphs.tsx\":120,\"frontend/src/components/Header.tsx\":72,\"frontend/src/components/Hero.tsx\":89,\"frontend/src/components/MarketCard.tsx\":85,\"frontend/src/components/MarketDrawer.tsx\":487,\"frontend/src/components/Markets.tsx\":88,\"frontend/src/components/Providers.tsx\":45,\"frontend/src/components/Sections.tsx\":237,\"frontend/src/components/Stats.tsx\":52,\"frontend/src/hooks/useDemoMarkets.ts\":189,\"frontend/src/hooks/useLiveMarkets.ts\":274,\"frontend/src/hooks/useNow.ts\":15,\"frontend/src/lib/abi.ts\":2264,\"frontend/src/lib/config.ts\":54,\"frontend/src/lib/demo.ts\":147,\"frontend/src/lib/format.ts\":46,\"frontend/src/lib/market.ts\":107,\"frontend/src/lib/types.ts\":82,\"frontend/tsconfig.json\":34,\"launch/buy-imd.sh\":27,\"launch/launch.template.json\":15,\"launch/pay.sh\":52,\"src/Conditionals.sol\":821,\"src/interfaces/IIntake.sol\":26,\"src/interfaces/IOracleAttestation.sol\":24,\"test/Conditionals.t.sol\":1172,\"test/ConditionalsAdversarial.t.sol\":620,\"test/ConditionalsFork.t.sol\":50,\"test/ConditionalsInvariant.t.sol\":200,\"test/ConditionalsLifecycleInvariant.t.sol\":365,\"test/ConditionalsQuestion.t.sol\":188,\"test/OracleConformance.t.sol\":145,\"test/utils/Fixture.sol\":187,\"test/utils/Mocks.sol\":141,\"test/utils/OracleQuestion.sol\":265},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"4c4be87d9c5f52419b087764f993b87215d390c42f29454275e00b753c0e3c0f","verifiedTreeHash":"9f5e907d69c7d39afa56f1d34e106f7bfc2e9c71","verifierVersion":"0.1.0+6a374fcd"},{"checks":[{"durationMs":22457,"exitCode":0,"name":"build","output":"Compiling 56 files with Solc 0.8.26\nSolc 0.8.26 finished in 22.39s\nCompiler run successful!\n","passed":true},{"durationMs":3084,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/ConditionalsFork.t.sol:ConditionalsForkTest\n[PASS] test_fork_realIntakeAcceptsTheAsks() (gas: 3375)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 222.40µs (52.39µs CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] test_acceptsAFreshSignatureFromTheVectorKeyAsDelivered() (gas: 2479701)\n[PASS] test_callbackSelectorIsCanonical() (gas: 696)\n[PASS] test_digestMatchesTheProtocol() (gas: 10367)\n[PASS] test_protocolSignatureVerifiesAgainstTheContractsDigest() (gas: 20527)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.95ms (1.52ms CPU time)\n\nRan 60 tests for test/Conditionals.t.sol:ConditionalsTest\n[PASS] testFuzz_payoutsConserveFunds(uint96[8],bool,bool) (runs: 512, μ: 3141246, ~: 3163613)\nLogs:\n  Bound result 18763\n  Bound result 18863\n  Bound result 78656\n  Bound result 38716\n  Bound result 74069604400909906164\n  Bound result 12340\n  Bound result 554\n  Bound result 6164\n\n[PASS] testFuzz_winnerNeverNetsLessThanStaked(uint96,uint96,bool) (runs: 512, μ: 2964317, ~: 2964442)\nLogs:\n  Bound result 999999087819433503809\n  Bound result 327448686927516075688\n\n[PASS] test_acceptsAnErc1271SignerRegistry() (gas: 4267366)\n[PASS] test_answerAfterVoidIsIgnored() (gas: 3060199)\n[PASS] test_answerIsAcceptedOnlyOnce() (gas: 3055886)\n[PASS] test_askPaysTheLivePriceWhenItIsLower() (gas: 4373160)\n[PASS] test_askPaysTheSnapshotWithoutALivePrice() (gas: 2376782)\n[PASS] test_askRevertsWhenTheLivePriceIsHigher() (gas: 1436729)\n[PASS] test_blockedCreatorDoesNotBlockVoid() (gas: 1695512)\n[PASS] test_blockedFeeRecipientDoesNotBlockSettlement() (gas: 3224143)\n[PASS] test_blockedStakerOnlyBlocksTheirOwnClaim() (gas: 3494784)\n[PASS] test_callbackFitsTheIntakeGasLimit() (gas: 2871560)\n[PASS] test_callbackSelectorsMatchTheIntakeDocs() (gas: 7467)\n[PASS] test_cannotRequestAfterVoidAfter() (gas: 587541)\n[PASS] test_cannotVoidASettleableMarket() (gas: 3295838)\n[PASS] test_cannotVoidWhileAnAskIsLive() (gas: 3041719)\n[PASS] test_claimBeforeAnswersReverts() (gas: 1127218)\n[PASS] test_claimTwiceReverts() (gas: 3284801)\n[PASS] test_claimableMatchesClaim() (gas: 3309789)\n[PASS] test_conditionFalseOutcomeNo() (gas: 3519582)\n[PASS] test_conditionFalseOutcomeYes() (gas: 3564550)\n[PASS] test_conditionFalseWaitsForTheOutcome() (gas: 4043134)\n[PASS] test_conditionTrueOutcomeNo() (gas: 3494814)\n[PASS] test_conditionTrueOutcomeYes() (gas: 3548148)\n[PASS] test_constructorValidation() (gas: 12751)\n[PASS] test_createMarketTakesBudgetAndSnapshotsConfig() (gas: 657397)\n[PASS] test_createRejectsBadText() (gas: 2870164)\n[PASS] test_createRejectsBadTimes() (gas: 817544)\n[PASS] test_createRejectsBudgetAboveTheCreatorsCap() (gas: 1158907)\n[PASS] test_documentedQuestionHashExample() (gas: 682)\n[PASS] test_domainSeparatorMatchesOracleDomain() (gas: 11347)\n[PASS] test_failureThenRetryPaidByCaller() (gas: 4389939)\n[PASS] test_feeIsCappedAtTheLosingPool() (gas: 3170639)\n[PASS] test_feeIsThreePercentOfThePotWhenTheLosingPoolCoversIt() (gas: 5686629)\n[PASS] test_feeRecipient() (gas: 2996814)\n[PASS] test_oneSidedWorldIsRefundedWithoutFee() (gas: 3173931)\n[PASS] test_oracleConfigBounds() (gas: 239937)\n[PASS] test_oracleConfigChangeIsDelayedAndOnlyAffectsNewMarkets() (gas: 4329061)\n[PASS] test_outcomeAnsweredBeforeCondition() (gas: 3270851)\n[PASS] test_pendingConfigExpires() (gas: 336654)\n[PASS] test_questionDocumentIsCanonical() (gas: 609322)\n[PASS] test_questionHashMatchesLiveOracleQuote() (gas: 691156)\n[PASS] test_rejectsAnswerToAnotherQuestion() (gas: 3696331)\n[PASS] test_rejectsCallsNotFromTheIntake() (gas: 2894216)\n[PASS] test_rejectsExpiredWrongTypeOrMalformedAnswer() (gas: 3391638)\n[PASS] test_rejectsIssuedAtInTheFuture() (gas: 3282607)\n[PASS] test_rejectsTamperedFields() (gas: 2868443)\n[PASS] test_rejectsWeakPanel() (gas: 3115657)\n[PASS] test_rejectsWrongSigner() (gas: 3183187)\n[PASS] test_requestBodyIsTheDocumentPlusPanelTerms() (gas: 616705)\n[PASS] test_requestRules() (gas: 2317456)\n[PASS] test_requestSendsTheBodyAndPaysFromBudget() (gas: 2584556)\n[PASS] test_rotatedActionIsUsedByNewMarkets() (gas: 4729377)\n[PASS] test_rotatedSignerCannotAnswerAnOlderMarket() (gas: 3149930)\n[PASS] test_stakeRecordsPoolsAndStakes() (gas: 964206)\n[PASS] test_stakeRejections() (gas: 761925)\n[PASS] test_unansweredAskCanBeRetriedAfterDelay() (gas: 5151687)\n[PASS] test_voidAllowedOnceTheLiveAskIsStale() (gas: 5236286)\n[PASS] test_voidRefundsEverythingAndBudget() (gas: 1563055)\n[PASS] test_world1YesWinsWhenOutcomeTrue() (gas: 3015033)\nSuite result: ok. 60 passed; 0 failed; 0 skipped; finished in 123.98ms (360.90ms CPU time)\n\nRan 1 test for test/ConditionalsInvariant.t.sol:ConditionalsInvariantTest\n[PASS]\nConditionalsInvariantTest invariants:\n[PASS] invariant_conservation\n[PASS] invariant_feesBounded\n[PASS] invariant_solvent\n ConditionalsInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭----------+--------------+-------+---------+----------╮\n| Contract | Selector     | Calls | Reverts | Discards |\n+======================================================+\n| Handler  | answer       | 1001  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | claim        | 1007  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | create       | 1037  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | failAsk      | 1058  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | request      | 994   | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | settleOrVoid | 1039  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | stake        | 1034  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | warp         | 1022  | 0       | 0        |\n╰----------+--------------+-------+---------+----------╯\n\nLogs:\n  Bound result 8\n  Bound result 8\n  Bound result 2710\n  Bound result 15\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1000000000000000\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 999999000003628442805\n  Bound result 1\n  Bound result 1\n  Bound result 999999000000000000117\n  Bound result 32000\n  Bound result 9009\n  Bound result 102\n  Bound result 102\n  Bound result 2\n  Bound result 2\n  Bound result 2\n  Bound result 2\n  Bound result 2\n  Bound result 6174\n  Bound result 174\n  Bound result 11498\n  Bound result 246\n  Bound result 2\n  Bound result 13\n  Bound result 278374\n  Bound result 156931\n  Bound result 18324\n  Bound result 5\n  Bound result 5\n  Bound result 5\n  Bound result 5\n  Bound result 2\n  Bound result 5\n  Bound result 432000\n  Bound result 9\n  Bound result 3\n  Bound result 2\n  Bound result 6\n  Bound result 15668\n  Bound result 13526\n  Bound result 12138\n  Bound result 2233\n  Bound result 4\n  Bound result 5\n  Bound result 1\n  Bound result 3\n  Bound result 999999000000000017858\n  Bound result 7\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.99s (2.99s CPU time)\n\nRan 4 test suites in 2.99s (3.12s CPU time): 66 tests passed, 0 failed, 0 skipped (66 total tests)\n","passed":true},{"durationMs":63,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"Conditionals.acceptOwnership()\",\"Conditionals.activateOracleConfig()\",\"Conditionals.claim(uint256)\",\"Conditionals.createMarket(string,string,string,uint64,uint64,uint256)\",\"Conditionals.onFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"Conditionals.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"Conditionals.proposeOracleConfig((address,address,uint128,uint16,uint16,bytes32))\",\"Conditionals.renounceOwnership()\",\"Conditionals.requestResolution(uint256)\",\"Conditionals.setFeeRecipient(address)\",\"Conditionals.settle(uint256)\",\"Conditionals.stake(uint256,uint8,uint8,uint256)\",\"Conditionals.transferOwnership(address)\",\"Conditionals.voidMarket(uint256)\",\"Conditionals.withdrawOwed()\"],\"files\":{\".gitignore\":9,\".gitmodules\":6,\"ADAPTATION.md\":207,\"HANDOFF.md\":40,\"README.md\":119,\"foundry.lock\":14,\"foundry.toml\":31,\"frontend/.gitignore\":42,\"frontend/eslint.config.mjs\":18,\"frontend/next.config.ts\":14,\"frontend/package.json\":32,\"frontend/pnpm-lock.yaml\":4541,\"frontend/pnpm-workspace.yaml\":3,\"frontend/scripts/gen-abi.mjs\":14,\"frontend/src/app/api/check/route.ts\":37,\"frontend/src/app/globals.css\":264,\"frontend/src/app/icon.svg\":1,\"frontend/src/app/layout.tsx\":38,\"frontend/src/app/page.tsx\":70,\"frontend/src/components/CreateModal.tsx\":354,\"frontend/src/components/Featured.tsx\":96,\"frontend/src/components/ForkCanvas.tsx\":234,\"frontend/src/components/Glyphs.tsx\":120,\"frontend/src/components/Header.tsx\":72,\"frontend/src/components/Hero.tsx\":89,\"frontend/src/components/MarketCard.tsx\":85,\"frontend/src/components/MarketDrawer.tsx\":487,\"frontend/src/components/Markets.tsx\":88,\"frontend/src/components/Providers.tsx\":45,\"frontend/src/components/Sections.tsx\":237,\"frontend/src/components/Stats.tsx\":52,\"frontend/src/hooks/useDemoMarkets.ts\":189,\"frontend/src/hooks/useLiveMarkets.ts\":274,\"frontend/src/hooks/useNow.ts\":15,\"frontend/src/lib/abi.ts\":2264,\"frontend/src/lib/config.ts\":54,\"frontend/src/lib/demo.ts\":147,\"frontend/src/lib/format.ts\":46,\"frontend/src/lib/market.ts\":107,\"frontend/src/lib/types.ts\":82,\"frontend/tsconfig.json\":34,\"launch.json\":16,\"launch/buy-imd.sh\":27,\"launch/launch.template.json\":15,\"launch/pay.sh\":52,\"src/Conditionals.sol\":821,\"src/interfaces/IIntake.sol\":26,\"src/interfaces/IOracleAttestation.sol\":24,\"test/Conditionals.t.sol\":1172,\"test/ConditionalsFork.t.sol\":50,\"test/ConditionalsInvariant.t.sol\":200,\"test/OracleConformance.t.sol\":145,\"test/utils/Fixture.sol\":187,\"test/utils/Mocks.sol\":141},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"71a290b6fab631c16498495281b0b447b37e09dd2bd631a2270e54b39e182063","verifiedTreeHash":"d10ba8582faab098f316e87ed2c432019f810b6a","verifierVersion":"0.1.0+6a374fcd"},{"checks":[{"durationMs":32599,"exitCode":0,"name":"build","output":"Compiling 56 files with Solc 0.8.26\nSolc 0.8.26 finished in 32.49s\nCompiler run successful!\n","passed":true},{"durationMs":4012,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/ConditionalsFork.t.sol:ConditionalsForkTest\n[PASS] test_fork_realIntakeAcceptsTheAsks() (gas: 3375)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 537.54µs (96.95µs CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] test_acceptsAFreshSignatureFromTheVectorKeyAsDelivered() (gas: 2463715)\n[PASS] test_callbackSelectorIsCanonical() (gas: 696)\n[PASS] test_digestMatchesTheProtocol() (gas: 10367)\n[PASS] test_protocolSignatureVerifiesAgainstTheContractsDigest() (gas: 20527)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 13.84ms (2.55ms CPU time)\n\nRan 49 tests for test/Conditionals.t.sol:ConditionalsTest\n[PASS] testFuzz_payoutsConserveFunds(uint96[8],bool,bool) (runs: 512, μ: 3101253, ~: 3121539)\nLogs:\n  Bound result 149\n  Bound result 12351\n  Bound result 3195\n  Bound result 16203\n  Bound result 10087\n  Bound result 11140\n  Bound result 9728\n  Bound result 5329\n\n[PASS] test_acceptsAnErc1271SignerRegistry() (gas: 4215615)\n[PASS] test_answerAfterVoidIsIgnored() (gas: 3017156)\n[PASS] test_answerIsAcceptedOnlyOnce() (gas: 3017071)\n[PASS] test_callbackFitsTheIntakeGasLimit() (gas: 2833141)\n[PASS] test_callbackSelectorsMatchTheIntakeDocs() (gas: 7181)\n[PASS] test_cannotRequestAfterVoidAfter() (gas: 582864)\n[PASS] test_cannotVoidASettleableMarket() (gas: 3246929)\n[PASS] test_cannotVoidWhileAnAskIsLive() (gas: 3006092)\n[PASS] test_claimBeforeAnswersReverts() (gas: 1100204)\n[PASS] test_claimTwiceReverts() (gas: 3235777)\n[PASS] test_claimableMatchesClaim() (gas: 3258924)\n[PASS] test_conditionFalseOutcomeNo() (gas: 3461831)\n[PASS] test_conditionFalseOutcomeYes() (gas: 3504554)\n[PASS] test_conditionFalseWaitsForTheOutcome() (gas: 3991975)\n[PASS] test_conditionTrueOutcomeNo() (gas: 3432735)\n[PASS] test_conditionTrueOutcomeYes() (gas: 3490320)\n[PASS] test_constructorValidation() (gas: 12751)\n[PASS] test_createMarketTakesBudgetAndSnapshotsConfig() (gas: 652830)\n[PASS] test_createRejectsBadText() (gas: 2865515)\n[PASS] test_createRejectsBadTimes() (gas: 813049)\n[PASS] test_createRejectsBudgetAboveTheCreatorsCap() (gas: 1153981)\n[PASS] test_documentedQuestionHashExample() (gas: 682)\n[PASS] test_domainSeparatorMatchesOracleDomain() (gas: 11061)\n[PASS] test_failureThenRetryPaidByCaller() (gas: 4330873)\n[PASS] test_feeRecipient() (gas: 2963545)\n[PASS] test_oneSidedWorldIsRefundedWithoutFee() (gas: 3128465)\n[PASS] test_oracleConfigBounds() (gas: 239370)\n[PASS] test_oracleConfigChangeIsDelayedAndOnlyAffectsNewMarkets() (gas: 4275227)\n[PASS] test_outcomeAnsweredBeforeCondition() (gas: 3221827)\n[PASS] test_questionDocumentIsCanonical() (gas: 604915)\n[PASS] test_questionHashMatchesLiveOracleQuote() (gas: 686661)\n[PASS] test_rejectsAnswerToAnotherQuestion() (gas: 3653171)\n[PASS] test_rejectsCallsNotFromTheIntake() (gas: 2855853)\n[PASS] test_rejectsExpiredWrongTypeOrMalformedAnswer() (gas: 3352641)\n[PASS] test_rejectsTamperedFields() (gas: 2829936)\n[PASS] test_rejectsWeakPanel() (gas: 3077146)\n[PASS] test_rejectsWrongSigner() (gas: 3143908)\n[PASS] test_requestBodyIsTheDocumentPlusPanelTerms() (gas: 612166)\n[PASS] test_requestRules() (gas: 2301424)\n[PASS] test_requestSendsTheBodyAndPaysFromBudget() (gas: 2568389)\n[PASS] test_rotatedActionIsUsedByNewMarkets() (gas: 4697520)\n[PASS] test_rotatedSignerCannotAnswerAnOlderMarket() (gas: 3111254)\n[PASS] test_stakeRecordsPoolsAndStakes() (gas: 945992)\n[PASS] test_stakeRejections() (gas: 756990)\n[PASS] test_unansweredAskCanBeRetriedAfterDelay() (gas: 5088353)\n[PASS] test_voidAllowedOnceTheLiveAskIsStale() (gas: 5177390)\n[PASS] test_voidRefundsEverythingAndBudget() (gas: 1513531)\n[PASS] test_world1YesWinsWhenOutcomeTrue() (gas: 2981815)\nSuite result: ok. 49 passed; 0 failed; 0 skipped; finished in 145.42ms (278.98ms CPU time)\n\nRan 1 test for test/ConditionalsInvariant.t.sol:ConditionalsInvariantTest\n[PASS]\nConditionalsInvariantTest invariants:\n[PASS] invariant_conservation\n[PASS] invariant_feesBounded\n[PASS] invariant_solvent\n ConditionalsInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭----------+--------------+-------+---------+----------╮\n| Contract | Selector     | Calls | Reverts | Discards |\n+======================================================+\n| Handler  | answer       | 1001  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | claim        | 1007  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | create       | 1037  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | failAsk      | 1058  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | request      | 994   | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | settleOrVoid | 1039  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | stake        | 1034  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | warp         | 1022  | 0       | 0        |\n╰----------+--------------+-------+---------+----------╯\n\nLogs:\n  Bound result 8\n  Bound result 8\n  Bound result 2491\n  Bound result 15\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1000000000000000\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 999999000003338429893\n  Bound result 1\n  Bound result 1\n  Bound result 999999000000000008563\n  Bound result 110279\n  Bound result 8798\n  Bound result 102\n  Bound result 102\n  Bound result 1\n  Bound result 2\n  Bound result 2\n  Bound result 1\n  Bound result 1\n  Bound result 4388\n  Bound result 388\n  Bound result 11129\n  Bound result 621796\n  Bound result 2\n  Bound result 13\n  Bound result 278374\n  Bound result 156931\n  Bound result 18324\n  Bound result 5\n  Bound result 5\n  Bound result 5\n  Bound result 4\n  Bound result 5\n  Bound result 5\n  Bound result 7\n  Bound result 4660\n  Bound result 6\n  Bound result 2\n  Bound result 6\n  Bound result 15017\n  Bound result 2321\n  Bound result 10727\n  Bound result 2230\n  Bound result 2\n  Bound result 5\n  Bound result 1\n  Bound result 7\n  Bound result 86551243693061822409\n  Bound result 7\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.87s (3.87s CPU time)\n\nRan 4 test suites in 3.87s (4.03s CPU time): 55 tests passed, 0 failed, 0 skipped (55 total tests)\n","passed":true},{"durationMs":106,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"Conditionals.acceptOwnership()\",\"Conditionals.activateOracleConfig()\",\"Conditionals.claim(uint256)\",\"Conditionals.createMarket(string,string,string,uint64,uint64,uint256)\",\"Conditionals.onFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"Conditionals.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"Conditionals.proposeOracleConfig((address,address,uint128,uint16,uint16,bytes32))\",\"Conditionals.renounceOwnership()\",\"Conditionals.requestResolution(uint256)\",\"Conditionals.setFeeRecipient(address)\",\"Conditionals.settle(uint256)\",\"Conditionals.stake(uint256,uint8,uint8,uint256)\",\"Conditionals.transferOwnership(address)\",\"Conditionals.voidMarket(uint256)\"],\"files\":{\".gitignore\":9,\".gitmodules\":6,\"ADAPTATION.md\":107,\"HANDOFF.md\":40,\"README.md\":108,\"foundry.lock\":14,\"foundry.toml\":31,\"frontend/.gitignore\":42,\"frontend/eslint.config.mjs\":18,\"frontend/next.config.ts\":14,\"frontend/package.json\":32,\"frontend/pnpm-lock.yaml\":4541,\"frontend/pnpm-workspace.yaml\":3,\"frontend/scripts/gen-abi.mjs\":14,\"frontend/src/app/api/check/route.ts\":37,\"frontend/src/app/globals.css\":264,\"frontend/src/app/icon.svg\":1,\"frontend/src/app/layout.tsx\":38,\"frontend/src/app/page.tsx\":70,\"frontend/src/components/CreateModal.tsx\":354,\"frontend/src/components/Featured.tsx\":96,\"frontend/src/components/ForkCanvas.tsx\":234,\"frontend/src/components/Glyphs.tsx\":120,\"frontend/src/components/Header.tsx\":72,\"frontend/src/components/Hero.tsx\":89,\"frontend/src/components/MarketCard.tsx\":85,\"frontend/src/components/MarketDrawer.tsx\":486,\"frontend/src/components/Markets.tsx\":88,\"frontend/src/components/Providers.tsx\":45,\"frontend/src/components/Sections.tsx\":237,\"frontend/src/components/Stats.tsx\":52,\"frontend/src/hooks/useDemoMarkets.ts\":183,\"frontend/src/hooks/useLiveMarkets.ts\":274,\"frontend/src/hooks/useNow.ts\":15,\"frontend/src/lib/abi.ts\":2163,\"frontend/src/lib/config.ts\":54,\"frontend/src/lib/demo.ts\":147,\"frontend/src/lib/format.ts\":46,\"frontend/src/lib/market.ts\":102,\"frontend/src/lib/types.ts\":82,\"frontend/tsconfig.json\":34,\"launch.json\":16,\"launch/buy-imd.sh\":27,\"launch/launch.template.json\":15,\"launch/pay.sh\":52,\"src/Conditionals.sol\":767,\"src/interfaces/IIntake.sol\":20,\"src/interfaces/IOracleAttestation.sol\":24,\"test/Conditionals.t.sol\":953,\"test/ConditionalsFork.t.sol\":50,\"test/ConditionalsInvariant.t.sol\":200,\"test/OracleConformance.t.sol\":145,\"test/utils/Fixture.sol\":187,\"test/utils/Mocks.sol\":111},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"7b5adfb2a99404ae5a78cab54fdc19f921e41106626c06edd391b422e3dc5d5b","verifiedTreeHash":"af79b9ae3f81ef90d1b26037a9a60fd106aed304","verifierVersion":"0.1.0+6a374fcd"},{"checks":[{"durationMs":31517,"exitCode":0,"name":"build","output":"Compiling 60 files with Solc 0.8.26\nSolc 0.8.26 finished in 31.45s\nCompiler run successful!\n","passed":true},{"durationMs":7244,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/ConditionalsFork.t.sol:ConditionalsForkTest\n[PASS] test_fork_realIntakeAcceptsTheAsks() (gas: 3375)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 263.02µs (82.69µs CPU time)\n\nRan 49 tests for test/Conditionals.t.sol:ConditionalsTest\n[PASS] testFuzz_payoutsConserveFunds(uint96[8],bool,bool) (runs: 512, μ: 3101253, ~: 3121539)\nLogs:\n  Bound result 5034317\n  Bound result 120830783\n  Bound result 1826799062244\n  Bound result 14264337592751668710\n  Bound result 0\n  Bound result 1595618332937234182\n  Bound result 56742796161663200504\n  Bound result 449888023908498\n\n[PASS] test_acceptsAnErc1271SignerRegistry() (gas: 4215615)\n[PASS] test_answerAfterVoidIsIgnored() (gas: 3017156)\n[PASS] test_answerIsAcceptedOnlyOnce() (gas: 3017071)\n[PASS] test_callbackFitsTheIntakeGasLimit() (gas: 2833141)\n[PASS] test_callbackSelectorsMatchTheIntakeDocs() (gas: 7181)\n[PASS] test_cannotRequestAfterVoidAfter() (gas: 582864)\n[PASS] test_cannotVoidASettleableMarket() (gas: 3246929)\n[PASS] test_cannotVoidWhileAnAskIsLive() (gas: 3006092)\n[PASS] test_claimBeforeAnswersReverts() (gas: 1100204)\n[PASS] test_claimTwiceReverts() (gas: 3235777)\n[PASS] test_claimableMatchesClaim() (gas: 3258924)\n[PASS] test_conditionFalseOutcomeNo() (gas: 3461831)\n[PASS] test_conditionFalseOutcomeYes() (gas: 3504554)\n[PASS] test_conditionFalseWaitsForTheOutcome() (gas: 3991975)\n[PASS] test_conditionTrueOutcomeNo() (gas: 3432735)\n[PASS] test_conditionTrueOutcomeYes() (gas: 3490320)\n[PASS] test_constructorValidation() (gas: 12751)\n[PASS] test_createMarketTakesBudgetAndSnapshotsConfig() (gas: 652830)\n[PASS] test_createRejectsBadText() (gas: 2865515)\n[PASS] test_createRejectsBadTimes() (gas: 813049)\n[PASS] test_createRejectsBudgetAboveTheCreatorsCap() (gas: 1153981)\n[PASS] test_documentedQuestionHashExample() (gas: 682)\n[PASS] test_domainSeparatorMatchesOracleDomain() (gas: 11061)\n[PASS] test_failureThenRetryPaidByCaller() (gas: 4330873)\n[PASS] test_feeRecipient() (gas: 2963545)\n[PASS] test_oneSidedWorldIsRefundedWithoutFee() (gas: 3128465)\n[PASS] test_oracleConfigBounds() (gas: 239370)\n[PASS] test_oracleConfigChangeIsDelayedAndOnlyAffectsNewMarkets() (gas: 4275227)\n[PASS] test_outcomeAnsweredBeforeCondition() (gas: 3221827)\n[PASS] test_questionDocumentIsCanonical() (gas: 604915)\n[PASS] test_questionHashMatchesLiveOracleQuote() (gas: 686661)\n[PASS] test_rejectsAnswerToAnotherQuestion() (gas: 3653171)\n[PASS] test_rejectsCallsNotFromTheIntake() (gas: 2855853)\n[PASS] test_rejectsExpiredWrongTypeOrMalformedAnswer() (gas: 3352641)\n[PASS] test_rejectsTamperedFields() (gas: 2829936)\n[PASS] test_rejectsWeakPanel() (gas: 3077146)\n[PASS] test_rejectsWrongSigner() (gas: 3143908)\n[PASS] test_requestBodyIsTheDocumentPlusPanelTerms() (gas: 612166)\n[PASS] test_requestRules() (gas: 2301424)\n[PASS] test_requestSendsTheBodyAndPaysFromBudget() (gas: 2568389)\n[PASS] test_rotatedActionIsUsedByNewMarkets() (gas: 4697520)\n[PASS] test_rotatedSignerCannotAnswerAnOlderMarket() (gas: 3111254)\n[PASS] test_stakeRecordsPoolsAndStakes() (gas: 945992)\n[PASS] test_stakeRejections() (gas: 756990)\n[PASS] test_unansweredAskCanBeRetriedAfterDelay() (gas: 5088353)\n[PASS] test_voidAllowedOnceTheLiveAskIsStale() (gas: 5177390)\n[PASS] test_voidRefundsEverythingAndBudget() (gas: 1513531)\n[PASS] test_world1YesWinsWhenOutcomeTrue() (gas: 2981815)\nSuite result: ok. 49 passed; 0 failed; 0 skipped; finished in 169.34ms (255.78ms CPU time)\n\nRan 6 tests for test/ConditionalsQuestion.t.sol:ConditionalsQuestionTest\n[PASS] testFuzz_acceptedTextNeedsNoEscaping(bytes) (runs: 1000, μ: 84280, ~: 62446)\n[PASS] testFuzz_askPinsTheDocumentOfTheWindowItAsked(uint32) (runs: 256, μ: 2688463, ~: 2684525)\nLogs:\n  Bound result 23272\n\n[PASS] testFuzz_bodyCarriesTheDocumentAndThePanelTerms(bytes,uint32) (runs: 256, μ: 704899, ~: 685818)\n[PASS] testFuzz_documentMatchesTheReferenceLibrary(bytes,bytes,bytes,uint32,uint32) (runs: 512, μ: 966215, ~: 948776)\nLogs:\n  Bound result 2678400\n\n[PASS] test_liveApiVectorAgreesWithTheReferenceLibrary() (gas: 897792)\n[PASS] test_referenceLibraryReproducesTheProtocolVector() (gas: 37688)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 179.13ms (653.25ms CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] test_acceptsAFreshSignatureFromTheVectorKeyAsDelivered() (gas: 2463715)\n[PASS] test_callbackSelectorIsCanonical() (gas: 696)\n[PASS] test_digestMatchesTheProtocol() (gas: 10367)\n[PASS] test_protocolSignatureVerifiesAgainstTheContractsDigest() (gas: 20527)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 180.94ms (14.04ms CPU time)\n\nRan 27 tests for test/ConditionalsAdversarial.t.sol:ConditionalsAdversarialTest\n[PASS] testFuzz_winnersSplitTheNetPotWithBoundedDust(uint96,uint96,uint96,uint96,bool) (runs: 1000, μ: 3526759, ~: 3526826)\nLogs:\n  Bound result 999999999999000000001132471\n  Bound result 999999999999000000000008457\n  Bound result 1143229919862570508\n  Bound result 130065758499360943\n\n[PASS] test_answerForAnOlderWindowDoesNotFitTheRetry() (gas: 4008256)\n[PASS] test_answerToTheOtherQuestionOfTheSameMarketIsRefused() (gas: 2594911)\n[PASS] test_constructorRefusesZeroOwnerIntakeOrSigner() (gas: 13638)\n[PASS] test_expiryBoundary() (gas: 2568972)\n[PASS] test_failureAfterAnAnswerIsUnknown() (gas: 2470908)\n[PASS] test_failureFitsTheIntakeGasLimit() (gas: 2256221)\n[PASS] test_failureMovesNoValueAndClearsOnlyItsAsk() (gas: 2728656)\n[PASS] test_feeRecipientIsReadAtSettlement() (gas: 2927414)\n[PASS] test_lastSecondAskDelaysVoidByOneDayAtMost() (gas: 2480144)\n[PASS] test_launchConstructorOnAnEmptyRobinhoodChain() (gas: 311707)\n[PASS] test_lopsidedWinnersPayTheFeeOnTheirOwnStake() (gas: 2908983)\n[PASS] test_malformedCallbackLeavesTheAskPending() (gas: 2495849)\n[PASS] test_minimumStakesSettle() (gas: 3126908)\n[PASS] test_ownerOnlyFunctionsRefuseEveryoneElse() (gas: 293938)\n[PASS] test_ownerRotationCannotReachAnOpenMarket() (gas: 4376096)\n[PASS] test_panelChecks() (gas: 2921116)\n[PASS] test_resolutionOpensExactlyAtResolveTimeAndEndsAtVoidAfter() (gas: 722002)\n[PASS] test_retryWithoutPaymentRevertsAndChangesNothing() (gas: 2431147)\n[PASS] test_sameRequestIdFromAnotherIntakeIsUnknown() (gas: 3237571)\n[PASS] test_settledMarketCannotReopenOrBeVoided() (gas: 3211942)\n[PASS] test_signatureForAnotherChainIsRefused() (gas: 2431602)\n[PASS] test_signatureForAnotherDeploymentIsRefused() (gas: 2607555)\n[PASS] test_stakeClosesExactlyAtCloseTime() (gas: 687089)\n[PASS] test_unknownMarketIds() (gas: 901536)\n[PASS] test_voidOpensOnlyAfterVoidAfter() (gas: 939692)\n[PASS] test_voidedMarketCannotBeSettledOrVoidedAgain() (gas: 2928591)\nSuite result: ok. 27 passed; 0 failed; 0 skipped; finished in 181.22ms (281.25ms CPU time)\n\nRan 1 test for test/ConditionalsInvariant.t.sol:ConditionalsInvariantTest\n[PASS]\nConditionalsInvariantTest invariants:\n[PASS] invariant_conservation\n[PASS] invariant_feesBounded\n[PASS] invariant_solvent\n ConditionalsInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭----------+--------------+-------+---------+----------╮\n| Contract | Selector     | Calls | Reverts | Discards |\n+======================================================+\n| Handler  | answer       | 1001  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | claim        | 1007  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | create       | 1037  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | failAsk      | 1058  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | request      | 994   | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | settleOrVoid | 1039  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | stake        | 1034  | 0       | 0        |\n|----------+--------------+-------+---------+----------|\n| Handler  | warp         | 1022  | 0       | 0        |\n╰----------+--------------+-------+---------+----------╯\n\nLogs:\n  Bound result 65250\n  Bound result 250\n  Bound result 2491\n  Bound result 66\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1000000000000000\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 999999000003338429893\n  Bound result 1\n  Bound result 1\n  Bound result 999999000000000008563\n  Bound result 65\n  Bound result 8804\n  Bound result 102\n  Bound result 102\n  Bound result 2\n  Bound result 2\n  Bound result 2\n  Bound result 2\n  Bound result 1\n  Bound result 4388\n  Bound result 388\n  Bound result 11129\n  Bound result 555\n  Bound result 3\n  Bound result 13\n  Bound result 278374\n  Bound result 156931\n  Bound result 18324\n  Bound result 2\n  Bound result 3\n  Bound result 5\n  Bound result 1\n  Bound result 4\n  Bound result 2\n  Bound result 177\n  Bound result 864000\n  Bound result 6\n  Bound result 6\n  Bound result 6\n  Bound result 562887\n  Bound result 3456\n  Bound result 11854\n  Bound result 193883\n  Bound result 4\n  Bound result 5\n  Bound result 1\n  Bound result 1\n  Bound result 7\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.66s (2.65s CPU time)\n\nRan 1 test for test/ConditionalsLifecycleInvariant.t.sol:ConditionalsLifecycleInvariantTest\n[PASS]\nConditionalsLifecycleInvariantTest invariants:\n[PASS] invariant_finishedMarketsAndAnswersNeverChange\n[PASS] invariant_forgedAnswersNeverCount\n[PASS] invariant_nobodyTakesMoreThanTheMarketHeld\n[PASS] invariant_solventAndNothingLeftApproved\n ConditionalsLifecycleInvariantTest invariants (runs: 128, calls: 10240, reverts: 0)\n\n╭------------------+-----------------+-------+---------+----------╮\n| Contract         | Selector        | Calls | Reverts | Discards |\n+=================================================================+\n| LifecycleHandler | activate        | 812   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | answer          | 869   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | claim           | 877   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | create          | 869   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | failAsk         | 816   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | forge           | 878   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | request         | 857   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | rotateSigner    | 844   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | setFeeRecipient | 859   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | settleOrVoid    | 856   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | stake           | 843   | 0       | 0        |\n|------------------+-----------------+-------+---------+----------|\n| LifecycleHandler | warp            | 860   | 0       | 0        |\n╰------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1835\n  Bound result 3\n  Bound result 201432\n  Bound result 232\n  Bound result 35\n  Bound result 35\n  Bound result 12546\n  Bound result 117\n  Bound result 2\n  Bound result 6309\n  Bound result 309\n  Bound result 1\n  Bound result 2\n  Bound result 2\n  Bound result 2\n  Bound result 2\n  Bound result 1\n  Bound result 2\n  Bound result 2\n  Bound result 7\n  Bound result 40198\n  Bound result 90470\n  Bound result 470\n  Bound result 2\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 3\n  Bound result 1\n  Bound result 2\n  Bound result 110279\n  Bound result 141260\n  Bound result 2\n  Bound result 3\n  Bound result 3\n  Bound result 3\n  Bound result 2\n  Bound result 4\n  Bound result 4\n  Bound result 499999000000000008279\n  Bound result 79133\n  Bound result 533\n  Bound result 4\n  Bound result 3\n  Bound result 20\n  Bound result 20\n  Bound result 2\n  Bound result 3\n  Bound result 15\n  Bound result 15\n  Bound result 6\n  Bound result 6\n  Bound result 2\n  Bound result 115\n  Bound result 473\n  Bound result 5\n  Bound result 499999000000000011158\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 7.17s (7.16s CPU time)\n\nRan 7 test suites in 7.17s (10.53s CPU time): 89 tests passed, 0 failed, 0 skipped (89 total tests)\n","passed":true},{"durationMs":51,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"Conditionals.acceptOwnership()\",\"Conditionals.activateOracleConfig()\",\"Conditionals.claim(uint256)\",\"Conditionals.createMarket(string,string,string,uint64,uint64,uint256)\",\"Conditionals.onFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"Conditionals.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"Conditionals.proposeOracleConfig((address,address,uint128,uint16,uint16,bytes32))\",\"Conditionals.renounceOwnership()\",\"Conditionals.requestResolution(uint256)\",\"Conditionals.setFeeRecipient(address)\",\"Conditionals.settle(uint256)\",\"Conditionals.stake(uint256,uint8,uint8,uint256)\",\"Conditionals.transferOwnership(address)\",\"Conditionals.voidMarket(uint256)\"],\"files\":{\".gitignore\":9,\".gitmodules\":6,\"ADAPTATION.md\":107,\"HANDOFF.md\":40,\"README.md\":108,\"foundry.lock\":14,\"foundry.toml\":31,\"frontend/.gitignore\":42,\"frontend/eslint.config.mjs\":18,\"frontend/next.config.ts\":14,\"frontend/package.json\":32,\"frontend/pnpm-lock.yaml\":4541,\"frontend/pnpm-workspace.yaml\":3,\"frontend/scripts/gen-abi.mjs\":14,\"frontend/src/app/api/check/route.ts\":37,\"frontend/src/app/globals.css\":264,\"frontend/src/app/icon.svg\":1,\"frontend/src/app/layout.tsx\":38,\"frontend/src/app/page.tsx\":70,\"frontend/src/components/CreateModal.tsx\":354,\"frontend/src/components/Featured.tsx\":96,\"frontend/src/components/ForkCanvas.tsx\":234,\"frontend/src/components/Glyphs.tsx\":120,\"frontend/src/components/Header.tsx\":72,\"frontend/src/components/Hero.tsx\":89,\"frontend/src/components/MarketCard.tsx\":85,\"frontend/src/components/MarketDrawer.tsx\":486,\"frontend/src/components/Markets.tsx\":88,\"frontend/src/components/Providers.tsx\":45,\"frontend/src/components/Sections.tsx\":237,\"frontend/src/components/Stats.tsx\":52,\"frontend/src/hooks/useDemoMarkets.ts\":183,\"frontend/src/hooks/useLiveMarkets.ts\":274,\"frontend/src/hooks/useNow.ts\":15,\"frontend/src/lib/abi.ts\":2163,\"frontend/src/lib/config.ts\":54,\"frontend/src/lib/demo.ts\":147,\"frontend/src/lib/format.ts\":46,\"frontend/src/lib/market.ts\":102,\"frontend/src/lib/types.ts\":82,\"frontend/tsconfig.json\":34,\"launch/buy-imd.sh\":27,\"launch/launch.template.json\":15,\"launch/pay.sh\":52,\"src/Conditionals.sol\":767,\"src/interfaces/IIntake.sol\":20,\"src/interfaces/IOracleAttestation.sol\":24,\"test/Conditionals.t.sol\":953,\"test/ConditionalsAdversarial.t.sol\":584,\"test/ConditionalsFork.t.sol\":50,\"test/ConditionalsInvariant.t.sol\":200,\"test/ConditionalsLifecycleInvariant.t.sol\":315,\"test/ConditionalsQuestion.t.sol\":188,\"test/OracleConformance.t.sol\":145,\"test/utils/Fixture.sol\":187,\"test/utils/Mocks.sol\":111,\"test/utils/OracleQuestion.sol\":265},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"f9f3cc8db45a7335433a83194101476a7667448049a97ee57e076796ae87d040","verifiedTreeHash":"6fb173825bf1b08b462487c69178404074cd9949","verifierVersion":"0.1.0+6a374fcd"}]}