{"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":"f672522a-c16f-4528-9ef1-f18bec5d5a2a","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"0d94575d3919f91b060431fc944fe8adda7fdc6bfdbe07c62edfe12a6b95f30c","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"575d6e9ab952c26041d319916d7835d9c0d4d732d2fc89eed95e461fe630cf94","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"07e45da803a955aa1f60c9c656a3979e65164e155badb4510b808ff947f4a5a0","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"b89279670f5a00f5d26286edee00c97dd158b997031350a01cafe6e0329ad227","skillId":"audit-judge","subagents":4,"tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"b89279670f5a00f5d26286edee00c97dd158b997031350a01cafe6e0329ad227","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"ef392b191bda88b9c8f07714a679b4aa50d8f2a2a411d25d1b6387ad3c921f26","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"08dbb745352ce793df7fc2f1d2f25609a5fb45bf475814932bab49744f4f9a17","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"6702a9d2a6a4956416fc8e533d7b418780a082bcb9bf584e3cbfa3882d27b4e0","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"15b04fc8f60521936b9e2b37c09a804685ec689fc98cd5b04510d986d3106a15","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"15b04fc8f60521936b9e2b37c09a804685ec689fc98cd5b04510d986d3106a15","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"83d9be53c5e98a4d76adac3177d7722ca31bbfd3560e5d909e130a3e6b100d53","dependsOn":["build_contract_project","write_foundry_tests"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"2294b37b524be9a30d6673b59e5bda188d61f40218bc8dfb84fe43a17a6b09ac","dependsOn":["build_contract_project"],"execution":{"forkChains":[4663],"network":false,"profile":"foundry","requires":[],"skillHash":"1ddb4d02eb75e52d5f38cfd5c9bb54f332687b81929c68880cf1e8a6ab27254d","skillId":"write-foundry-tests","subagents":4,"tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"1ddb4d02eb75e52d5f38cfd5c9bb54f332687b81929c68880cf1e8a6ab27254d","skillId":"write-foundry-tests","state":"accepted"}],"objective":"SIMD TEST 3 Vault, a TEST of the SIMD relaunch, not the real thing: on Robinhood Chain: the contract that pays for and owns the fees of the \"SIMD TEST 3 - DO NOT BUY\" token launch and splits everything it receives every 7 days, plus a staking rewards contract, a jobs vault and the 30-day claim contract for that launch's remainder. Four linked contracts, no owner, no admin, no upgrade, no pause, no oracle. This is a test run: the token has no value.\r\n\r\nFixed addresses (Robinhood Chain):\r\n- IMD token: 0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127\r\n- Identity.md Intake: 0xa43e6f75ee006411f79ac1c84120606c2330de82 (request(bytes32 action, bytes body, (address target, bytes4 selector) callback, address asset, uint256 amount); price from priceOf(action, asset))\r\n- Identity.md HOOK launch factory: 0xa25b02a1e93903b790e6aaf7da1b8b8d50294645. Do NOT use 0x9c9d2fcb75c2c132c0ac0c42df3819a42347265e. claimFees(uint64 launchNumber) can be called by anyone and pays the launch's requester; positionOf(uint64) returns (PoolKey key, int24 tickLower, int24 tickUpper, address requester).\r\n- Dev wallet: 0xdfa1f1f6feaa04f7874eff0b7754b7ffaebd8bc0\r\n- Marketing wallet: 0xdfa1f1f6feaa04f7874eff0b7754b7ffaebd8bc0\r\n\r\n1. SIMDVault\r\n   - Constructor deploys StakeRewards, JobsVault (given the StakeRewards address) and SimdAirdrop, calls StakeRewards.setJobsVault(jobsVault) once, and exposes all three as immutable public getters.\r\n   - launch(bytes body): only the dev wallet, only once. Pays Intake.request(\"launch.open@launch-1\", body, no callback, IMD, priceOf) from its own IMD, approving exactly the price and resetting it to 0.\r\n   - bindLaunch(uint64 launchNumber, address token): only the dev wallet, only once, only if factory.positionOf(launchNumber).requester == address(this), token is one of that pool's two currencies, has code, is not IMD, and the other currency is IMD. Stores both for good, calls StakeRewards.setToken(token), JobsVault.setToken(token) and SimdAirdrop.bind(launchNumber) inside try/catch (a failed airdrop bind never blocks the vault). bindAirdrop(): anyone, after bindLaunch, calls SimdAirdrop.bind(launchNumber).\r\n   - distribute(): anyone, at most once every 7 days, only after bindLaunch; the first call can run at once. First calls factory.claimFees(launchNumber) inside try/catch with a 1,000,000 gas limit. Then splits the vault's whole IMD balance with mulDiv: 40% to StakeRewards followed by StakeRewards.notifyDeposit(), 25.72% to JobsVault, 17.14% to the dev wallet, 17.14% to the marketing wallet. The vault's whole balance of the launched token is sent to 0x000000000000000000000000000000000000dEaD with a low-level call; a failed burn never blocks the split. The vault accepts native ETH (receive()); any native balance is pushed 100% to the marketing wallet with a 50,000 gas call, and a rejected push never blocks the split or the burn.\r\n   - No other way for IMD or tokens to leave.\r\n2. StakeRewards: holders stake the launched token here and earn the vault's weekly IMD. Exactly this algorithm (Synthetix StakingRewards with carry), all integers, SCALE = 1e36, DURATION = 7 days:\r\n   - update(a): t = min(now, periodFinish); if t > lastUpdate: streamed = (t - lastUpdate) * rewardRate; if totalStaked == 0 then carryScaled += streamed else rewardPerTokenStored += streamed / totalStaked; lastUpdate = t. If a != 0: rewards[a] = earned(a); rewardPerTokenPaid[a] = rewardPerTokenStored.\r\n   - earned(a) = rewards[a] + stakeOf[a] * (rewardPerToken() - rewardPerTokenPaid[a]) / SCALE, where rewardPerToken() adds the not-yet-stored stream to rewardPerTokenStored (unchanged when totalStaked == 0).\r\n   - notifyDeposit(): only the vault. update(0); received = IMD.balanceOf(this) - reserved; if 0 return; reserved += received; total = received * SCALE + carryScaled + (now < periodFinish ? (periodFinish - now) * rewardRate : 0); rewardRate = total / DURATION; carryScaled = total % DURATION; lastUpdate = now; periodFinish = now + DURATION.\r\n   - stake(amount): token set, amount > 0; update(sender); counts the amount actually received (balance difference). withdraw(amount): reverts while now < lockedUntil[sender]; update(sender); reverts if amount > stakeOf. claim(): update(sender); pays rewards[sender] in IMD, reserved -= it. exit(): withdraw all, then claim. All three nonReentrant.\r\n   - setToken(token) and setJobsVault(address): only the vault, once each. lock(account, until): only the JobsVault; only ever extends lockedUntil. A locked stake keeps earning.\r\n   - No other way for IMD or staked tokens to leave. No owner.\r\n3. JobsVault: requestJob(bytes body): the caller must have at least 1,000,000 whole tokens (times 10^decimals, set in setToken) staked in StakeRewards (StakeRewards.stakeOf), at most once per 24 hours per wallet; body not empty and at most 16,384 bytes. It calls StakeRewards.lock(caller, now + 24 hours), then pays Intake.request(\"job.open@job-1\", body, no callback, IMD, priceOf) from its own IMD the same way. setToken only by the vault, once. No withdraw.\r\n4. SimdAirdrop: receives the launch's remainder from the launch factory. Each wallet on a fixed list claims its own share once, pro rata to its weight, for 30 days.\r\n   - bind(uint64 launchNumber): only the SIMDVault (from bindLaunch), once, only if launchNumber is greater than 3187, factory.positionOf(launchNumber).requester is the SIMDVault, the pool pairs with IMD, and the contract already holds at least the list's total weight of the launched token. startBalance and claimEnd (bind time + 30 days) are fixed at bind.\r\n   - amountOf(weight) = startBalance * weight / totalWeight.\r\n   - claim(uint256 weight, bytes32[] proof): msg.sender only, once, only while the window is open. Leaf: keccak256(bytes.concat(keccak256(abi.encode(address account, uint256 weight)))) with sorted-pair hashing (OpenZeppelin MerkleProof).\r\n   - burnUnclaimed(): anyone, only after claimEnd. Sends everything left to 0x000000000000000000000000000000000000dEaD.\r\n   - The list is committed as constants: Merkle root 0xe1fc34a5aee64c3cd01e62934929bc57511b5718c2f5ae80072ae25045e50cb4, total weight 633028988023500000000000000, 457 wallets. Nobody can change the list. No other way for tokens to leave.\r\n\r\nEvery contract carries a public constant string NOTICE = \"TEST ONLY. SIMD TEST 3 vault set. Not the real SIMD. Do not buy.\". Verify the source of every deployed contract on Sourcify (chain 4663).\r\n\r\nFork tests with the real Intake and hook factory: launch and bindLaunch only by the dev wallet and only once; bindLaunch rejects a launch whose requester is not the vault and binds the airdrop in the same call; the 40 / 25.72 / 17.14 / 17.14 split and the token burn; the 7-day limit, with the first distribute allowed at once; StakeRewards: a sole staker receives the whole deposit over 7 days, two stakers split by stake, a wallet staking just before a deposit and leaving after 1 hour gets only 1/168 of its share, time with nobody staked is carried into the next deposit, a deposit mid-stream keeps the remainder, a fuzz test that never pays out more than deposited and keeps balance >= reserved; JobsVault: stake below 1,000,000 rejected, one job per 24 hours, the stake is locked for 24 hours and keeps earning; airdrop: double claim, a bad proof and an unlisted wallet get nothing, claim closes at claimEnd, burnUnclaimed only after claimEnd.\r\n\r\nHarness note: Foundry answers ArbSys(address(100)).arbBlockNumber() with block.number on chain 4663; never mock ArbSys, use vm.roll and vm.warp.\r\n","parentJobId":null,"planHash":"0f273747b35afd7165f7e09d49295f59de4d19c0aaf1fee0fbbbdba1a90d9384","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"f672522a-c16f-4528-9ef1-f18bec5d5a2a","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-3191-simd-test-3-vault"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51081","feedbackHash":"01c77498ac5deb41b4d7632dbc56ff277054c13ee28398876a2f325483e4fafe","nodeKey":"audit_economics","submissionHash":"0d94575d3919f91b060431fc944fe8adda7fdc6bfdbe07c62edfe12a6b95f30c","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51316","feedbackHash":"b6a4867d0944439ebb861023b3b5e7c8ad04d5776a91a84574378843ffcb45d7","nodeKey":"audit_flow","submissionHash":"575d6e9ab952c26041d319916d7835d9c0d4d732d2fc89eed95e461fe630cf94","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"53924","feedbackHash":"77170218dd11d64c559bc9d87630962a3b35039e97fdd9b5bf9322d26e75ad47","nodeKey":"audit_judge","submissionHash":"07e45da803a955aa1f60c9c656a3979e65164e155badb4510b808ff947f4a5a0","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52158","feedbackHash":"4cdb0d8ab195233dc8dfe6f823370771c29d481b472964594d0876502d2b8357","nodeKey":"audit_math","submissionHash":"ef392b191bda88b9c8f07714a679b4aa50d8f2a2a411d25d1b6387ad3c921f26","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51317","feedbackHash":"9c097ec6fc282e765b0584827da72020b4bd8b3033601258b1b6e7953671c9e7","nodeKey":"audit_permissions","submissionHash":"08dbb745352ce793df7fc2f1d2f25609a5fb45bf475814932bab49744f4f9a17","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51755","feedbackHash":"0c96c5f1c618e601d7ec9fce4893fa0dcec6fa5368c70f8b4cbcca606a19a029","nodeKey":"build_contract_project","submissionHash":"6702a9d2a6a4956416fc8e533d7b418780a082bcb9bf584e3cbfa3882d27b4e0","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"56008","feedbackHash":"566a75b06b72d3ef335e0fb430f0336d0f10a4cf0388032982b05e236d0375ff","nodeKey":"manifest","submissionHash":"83d9be53c5e98a4d76adac3177d7722ca31bbfd3560e5d909e130a3e6b100d53","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51019","feedbackHash":"59201878caded15ce6a3dc25e4063c48adc0abd66ca94b21834231a88abc820c","nodeKey":"write_foundry_tests","submissionHash":"2294b37b524be9a30d6673b59e5bda188d61f40218bc8dfb84fe43a17a6b09ac","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"5ec64902a03c9aa4295a0e85f331b682ce0c4cbba11b5ed846fd476f4626eed3","state":"completed","submissions":[{"artifacts":[],"attempt":2,"bundleHash":null,"device":"a7ac9f7a7687c25e","findings":[{"citation":"resolved","description":"bind() is the only function that sets token and claimEnd and it requires the airdrop to already hold at least TOTAL_WEIGHT (633,028,988.0235e18 raw units) of the launched token (line 54). claim() (line 67) and burnUnclaimed() (line 80) both revert NotBound while token == address(0). The factory sends the launch remainder exactly once at launch time (LaunchFactory: `if (remainder > 0) IERC20(token).safeTransfer(params.remainderTo ...)`), so if that remainder is even one unit short, or remainderTo names another address, the tokens already in the airdrop have no exit at any time, including after the 30-day window the brief describes. SIMDVault.bindLaunch swallows the failed bind in try/catch (src/SIMDVault.sol:79-83) and bindAirdrop() keeps reverting InsufficientRemainder, so the vault, staking and jobs run while the remainder is stuck. The >= TOTAL_WEIGHT precondition is what the brief asks for; the defect is that the shortfall case is unrecoverable. Merged from audit_economics. Fix needs a scope decision: let burnUnclaimed() run while unbound after a fixed deadline, or let bind() accept any nonzero balance and keep TOTAL_WEIGHT only as the pro-rata denominator.","line":54,"path":"src/SimdAirdrop.sol","reproduction":"Offline with the project's mocks (test/helpers/VaultTestBase): launchedToken.mint(address(drop), drop.TOTAL_WEIGHT() - 1); vm.prank(DEV); vault.bindLaunch(NUMBER, token). Expected: the airdrop's tokens can eventually be claimed or burned. Actual: vault.token() == token, drop.token() == address(0); vault.bindAirdrop() reverts SimdAirdrop.InsufficientRemainder; after vm.warp(+3650 days) drop.burnUnclaimed() reverts NotBound and drop.claim(1, []) reverts NotBound; launchedToken.balanceOf(drop) is still TOTAL_WEIGHT - 1 with no function able to move it. Reproduced in test/scratch/Judge.scratch.t.sol::test_F1_airdropShortRemainderStrandedForever (passes, i.e. the lock is real).","severity":"low","snippet":"        if (balance < TOTAL_WEIGHT) revert InsufficientRemainder();","title":"SimdAirdrop: a remainder below TOTAL_WEIGHT strands every token in the airdrop forever (no claim, no burn)"},{"citation":"resolved","description":"launch() sets launched = true before the Intake call (line 65) and the Intake forwards the price to the treasury in the same call with no refund or callback. If the plane later refuses or fails the launch body, or the dev wallet never binds, no factory position will ever name the vault as requester, so bindLaunch() always reverts LaunchPool.WrongRequester and distribute() always reverts NotBound (line 94). Every IMD unit in the vault beyond the exact price (overfunding, a price decrease, direct transfers) and any ETH accepted by receive() is then permanently stuck: the vault has no other transfer path by design, and launch() cannot be retried (AlreadyLaunched). The only recovery is an outside launch whose requester is handed to the vault with LaunchFactory.setRequester. This is the brief's 'only once' rule, so it is the requested behaviour; it is reported because the loss path is permanent and the dev must validate the body off-chain before paying. Merged from audit_economics (pre-bind funds stuck) and audit_permissions (launch flag consumed by an accepted payment). Fix needs a scope decision: tie `launched` to a successful bindLaunch with a deadline, or let distribute() split IMD before binding (skipping claimFees and the burn).","line":65,"path":"src/SIMDVault.sol","reproduction":"Offline with the project's mocks: imd.mint(address(vault), PRICE + 1 ether); vm.deal(address(vault), 1 ether); vm.prank(DEV); vault.launch('{}') succeeds and the Intake takes PRICE, launched() == true. Expected: the remaining 1 IMD and 1 ETH can eventually be distributed or the launch retried. Actual: vault.distribute() reverts NotBound; vm.prank(DEV); vault.bindLaunch(9999, token) reverts LaunchPool.WrongRequester because no position names the vault; vm.prank(DEV); vault.launch('{}') reverts AlreadyLaunched; imd.balanceOf(vault) stays 1 ether and address(vault).balance stays 1 ether with no function able to move either. Reproduced in test/scratch/Judge.scratch.t.sol::test_F2_prebindFundsStuckAfterLaunchFlagConsumed.","severity":"low","snippet":"        launched = true;","title":"SIMDVault: the one launch flag is consumed by the on-chain payment, so IMD and ETH held before a successful bindLaunch have no exit if the launch is never bound"},{"citation":"resolved","description":"SIMDVault.bindLaunch (src/SIMDVault.sol:76-77) calls stakeRewards.setToken and jobsVault.setToken outside any try/catch, and JobsVault.setToken makes an unguarded call to decimals() (line 44), an optional ERC-20 function that LaunchPool.tokenOf (src/libraries/LaunchPool.sol:16) does not check (it only requires code). The live factory deploys whatever tokenCreationCode the plane supplies, so a launched token without decimals() is representable. For such a token every bindLaunch attempt reverts, token stays address(0), distribute() reverts NotBound forever, and because the vault is the position's requester, every IMD fee that factory.claimFees pays it (anyone can trigger that) is stuck with no exit. StakeRewards.setToken and SimdAirdrop.bind accept any token with code; JobsVault.setToken silently adds a stricter requirement. Reading decimals in setToken is what the brief asks for ('times 10^decimals, set in setToken'), so this is a trust assumption on the plane's LaunchToken being ERC20Metadata. Merged from audit_permissions. Minimal fix preserving the design: read decimals() with a try/catch and fall back to 18, or let bindLaunch tolerate a failed JobsVault.setToken the way it tolerates a failed airdrop bind.","line":44,"path":"src/JobsVault.sol","reproduction":"State: factory.positionOf(N) = (currency0 = IMD, currency1 = T, requester = vault) where T is an ERC-20 with code but no decimals() selector. Call vault.bindLaunch(N, T) from DEV. Expected (spec): bind succeeds and StakeRewards and JobsVault get the token. Actual: the call reverts inside JobsVault.setToken (decimals() reverts), vault.token() stays address(0). Then mint 100 IMD to the vault (as factory.claimFees would) and call vault.distribute(): reverts NotBound, IMD permanently stuck. Reproduced in test/scratch/Judge.scratch.t.sol::test_F3_tokenWithoutDecimalsBlocksBindAndStrandsFees with a minimal ERC-20 lacking decimals().","severity":"low","snippet":"        uint8 decimals = IERC20Metadata(token_).decimals();","title":"JobsVault.setToken makes bindLaunch depend on the launched token's optional decimals(); a token without it can never be bound and every claimed fee is stuck in the vault"},{"citation":"resolved","description":"bindLaunch validates requester, pair, code and non-IMD token (line 73) but not the airdrop's own precondition `number > MIN_LAUNCH_EXCLUSIVE` (src/SimdAirdrop.sol:51 `if (number <= MIN_LAUNCH_EXCLUSIVE) revert OldLaunch();`). The airdrop bind runs inside try/catch, so a launch numbered <= 3187 whose requester is the vault binds the vault, StakeRewards and JobsVault for good while SimdAirdrop.bind reverts OldLaunch on every later bindAirdrop() retry. Because burnUnclaimed() and claim() both require a bound airdrop, any launched token already held by the airdrop has no exit, and the one-time vault binding cannot be redone (AlreadyBound). Reachable only through the dev wallet (an old launch's requester would have to hand the position to the vault with the factory's setRequester), and the token is a valueless test token, so impact is low; the failure is permanent and signalled only by AirdropBindAttempt(false). Merged from audit_flow. Fix: check `number > airdrop.MIN_LAUNCH_EXCLUSIVE()` in bindLaunch up front so the dev can bind the right launch.","line":73,"path":"src/SIMDVault.sol","reproduction":"State: factory.positionOf(3187) returns a pool pairing IMD with token T and requester == vault; SimdAirdrop holds >= TOTAL_WEIGHT of T. (1) DEV calls vault.bindLaunch(3187, T): succeeds, vault.token() == T, event AirdropBindAttempt(false), drop.token() == address(0). (2) anyone calls vault.bindAirdrop(): reverts SimdAirdrop.OldLaunch. (3) after 31 days drop.burnUnclaimed(): reverts SimdAirdrop.NotBound. (4) DEV calls vault.bindLaunch(3200, T): reverts AlreadyBound. (5) vault.distribute() still runs, bound to the old launch. Expected: bindLaunch rejects number <= 3187 up front, or the remainder has a recovery path. Reproduced in test/scratch/Judge.scratch.t.sol::test_F4_oldLaunchNumberBindsVaultButNeverAirdrop.","severity":"low","snippet":"        if (LaunchPool.tokenOf(FACTORY, number, address(this), IMD) != token_) revert WrongToken();","title":"SIMDVault.bindLaunch accepts a launch number the airdrop can never bind (<= 3187), fixing the vault to that launch for good while the remainder is stranded"},{"citation":"resolved","description":"notifyDeposit follows the specified algorithm: when the vault has nothing new (received == 0, line 95) it returns before restarting the stream, so carryScaled, which already holds every scaled unit streamed while totalStaked was 0 plus the rate-division remainder, is never paid to stakers until a later deposit with received > 0 arrives. The first distribute() is permissionless and allowed immediately after bindLaunch, typically before anyone has staked, so the entire first 40% share is carried. The IMD is counted in reserved (balance >= reserved holds) but no staker can claim it. Any party can release it by transferring >= 1 wei IMD to StakeRewards before the next distribute(). This matches the brief's 'if 0 return', so it is a design consequence, not a code deviation. Merged from audit_permissions.","line":95,"path":"src/StakeRewards.sol","reproduction":"After bindLaunch: mint 1000 IMD to the vault, call distribute() with nobody staked (StakeRewards receives 400 IMD, carried over the week). Warp 7 days, alice stakes 1 token, warp 7 days, call distribute() with the vault at 0 IMD, warp 7 days, alice.claim(). Expected by a staker: some of the 400 IMD. Actual: alice receives 0, rewards.reserved() == 400e18, rewards.carryScaled() == 400e18 * 1e36. Then mint 1 wei IMD to StakeRewards and call distribute() again: after 7 days alice claims > 399 IMD. Reproduced in test/scratch/Judge.scratch.t.sol::test_F5_carryReleasedOnlyByNonZeroDeposit.","severity":"info","snippet":"        if (received == 0) return;","title":"StakeRewards: carried IMD (zero-staker time and rate remainder) is released only by a later nonzero deposit; if fee income stops, up to a full week's share stays reserved but unreachable"},{"citation":"resolved","description":"The specified algorithm carries only the rate-division remainder (total % DURATION) into carryScaled. Two other floors are not carried: `streamed / totalStaked` in _update (line 138) and `Math.mulDiv(stakeOf, delta, SCALE)` in earned (line 89). What those floors drop stays counted in reserved (reserved only decreases by paid claims), so it is never claimable and never re-streamed, and there is no other exit for IMD. Magnitude is bounded by totalStaked / 1e36 raw IMD per update plus < 1 raw unit per account update: wei-level dust for realistic stakes. It only becomes the whole deposit when received * 1e36 < totalStaked, which is unreachable with real amounts. The algorithm is exactly the one the brief specifies. Merged from audit_math.","line":138,"path":"src/StakeRewards.sol","reproduction":"Mock IMD at the fixed address, StakeRewards with token set. alice stakes 1e18, bob stakes 1e18 (totalStaked = 2e18). Mint 1 raw IMD to StakeRewards, vault calls notifyDeposit(): rewardRate = 1e36/604800, carryScaled = 1e36 % 604800 = 92800. Warp 7 days, both claim: rewardPerTokenStored = (1e36 - 92800)/2e18 floored, earned(each) = 0, both receive 0, reserved stays 1. Then mint 604800e18 and notifyDeposit() again, warp 7 days, both claim: 604800e18 paid in total and reserved is still 1 (exact arithmetic: 604800e18 + 1 paid, reserved 0). Reproduced in test/scratch/Judge.scratch.t.sol::test_F6_floorDustStrandedInReserved.","severity":"info","snippet":"            else rewardPerTokenStored += streamed / totalStaked;","title":"StakeRewards: per-token and per-account floor dust stays in `reserved` forever and is never re-streamed (unlike rate-division dust)"},{"citation":"resolved","description":"requestJob() is the only path that moves IMD out of JobsVault, and it spends through IntakePayment.request, which reverts ActionNotSold when priceOf(action, IMD) returns 0 (line 17) and reverts when Intake.request itself rejects. The Intake is an external, IMD-operated contract; if it retires the action, changes the asset or pauses, every requestJob reverts and the jobs budget (25.72% of every weekly distribution, accumulated forever) is stuck with no admin, sweep or fallback. This is the brief's explicit design ('No withdraw'), recorded as a dependency trust assumption. The same single Intake price also bounds how fast a Sybil of 1,000,000-token wallets can spend the budget (one job of priceOf per wallet per 24 h), which the brief accepts. Merged from audit_economics.","line":17,"path":"src/libraries/IntakePayment.sol","reproduction":"State: JobsVault holds 100 IMD; alice has 1,000,000e18 staked; mock Intake configured with price 0 (intake.configure(0, false, false)). vm.prank(alice); jobs.requestJob('{}'). Expected per the brief: a job is paid. Actual: revert IntakePayment.ActionNotSold; imd.balanceOf(jobs) stays 100 IMD and no function on JobsVault can move it. Reproduced in test/scratch/Judge.scratch.t.sol::test_F7_jobsBudgetLockedWhenActionNotSold.","severity":"info","snippet":"        if (price == 0) revert ActionNotSold();","title":"JobsVault: the 25.72% budget is permanently locked if the Intake stops selling job.open@job-1 (trust assumption; no withdraw by design)"},{"citation":"resolved","description":"amountOf divides by the constant TOTAL_WEIGHT (line 63), not by the actual sum of the leaves under MERKLE_ROOT. The 457 wallet/weight records are not in this tree (test/README.md says so), so neither the tests nor this review can check that the leaves sum to exactly 633028988023500000000000000 or that no address appears twice (claimed[] is per address, so a duplicated address could claim only one of its leaves). If the real sum exceeds TOTAL_WEIGHT, the sum of amountOf over all leaves exceeds startBalance and the last claimants' safeTransfer reverts ERC20InsufficientBalance; if it is below, the difference is burned at claimEnd. A Merkle commitment cannot enforce this on chain; it is a precondition the list publisher must prove before claims open. Merged from audit_economics.","line":63,"path":"src/SimdAirdrop.sol","reproduction":"With a synthetic root (the production list cannot be exercised without the records): leaves (alice, TOTAL_WEIGHT) and (bob, 1e18); fund the airdrop with exactly TOTAL_WEIGHT and bind. alice.claim(TOTAL_WEIGHT, proof) transfers the whole startBalance. bob.claim(1e18, proof) with a valid proof: expected a share, actual revert ERC20InsufficientBalance (balance 0 < amountOf(1e18) = 1e18). Arithmetic: amountOf(w) = startBalance * w / TOTAL_WEIGHT, so sum over leaves = startBalance * sum(w) / TOTAL_WEIGHT > startBalance whenever sum(w) > TOTAL_WEIGHT. The real list's sum is the unverified input.","severity":"info","snippet":"        return Math.mulDiv(startBalance, weight, TOTAL_WEIGHT);","title":"SimdAirdrop: the pro-rata payout divides by the constant TOTAL_WEIGHT, which the contract cannot check against the committed list; a leaf sum above it makes late claimants fail"}],"hash":"07e45da803a955aa1f60c9c656a3979e65164e155badb4510b808ff947f4a5a0","nodeId":"0f91ace0-1f55-4401-a046-b428e769362a","outcome":"completed","summary":"The review is complete. The findings file is written and every snippet was verified against the tree; the scratch tests were removed so the working tree is unchanged.\n\n**Outcome.** No critical, high or medium defect. The four linked contracts implement the brief as specified. I kept 8 findings, all low or informational, and merged the two overlapping reports about pre-bind funds and the consumed launch flag into one. Every kept finding was reproduced on this code with the project's mocks in a scratch Foundry file, and all 7 reproduction tests passed, meaning each failure path is real.\n\n**Findings kept, by severity:**\n- **Low.** SimdAirdrop with a remainder below the committed total weight can never bind, so its tokens have no claim or burn path at any time.\n- **Low.** The vault's one launch flag is consumed by the on-chain payment. If the launch is never bound, IMD and ETH already in the vault are stuck forever.\n- **Low.** JobsVault.setToken calls the optional `decimals()` outside any try/catch, so a launched token without it can never be bound and claimed fees pile up in the vault with no exit.\n- **Low.** SIMDVault.bindLaunch does not mirror the airdrop's launch-number floor, so a launch numbered 3187 or lower binds the vault for good while the airdrop can never bind.\n- **Info.** Carry in StakeRewards only streams after a later nonzero deposit, floor dust stays in `reserved`, the jobs budget has no exit if the Intake stops selling the action, and the airdrop's weight sum cannot be checked on chain because the 457-entry list is not in the tree.\n\n**Dropped or confirmed as not defects.** Every slither and aderyn line traced to intended behaviour: the modulo is the specified carry, balance-difference staking is deliberate, the strict equalities are spec'd early returns, and the ignored `claimFees` return is inside try/catch. I confirmed on Sourcify that the live factory's `claimFees` and `positionOf` signatures match what the vault decodes, so the try/catch cannot be bypassed by a decoding failure. Contract sizes are far inside the factory's limits. Gas-starving `claimFees` through the 63/64 rule is not feasible because the remaining gas could not complete the split.\n\n**Coverage.** All 18 entry points are answered: 14 hold, 4 carry a finding. Two extra rows record the balance-versus-reserved invariant and deployment sizes.\n\n**Limits.** The fork suite against the real Intake and factory could not run here because no RPC URL is configured, so those 16 tests were skipped and remain owed to the verifier's run. Sourcify verification of the deployed contracts is a post-deployment item and was not checked.","treeHash":null,"usage":{"cachedInputTokens":956145,"cost":{"source":"runtime","usdMicros":3826076},"inputTokens":386,"model":"claude-fable-5-1","outputTokens":32668,"runtime":"claude","turns":13,"wallClockMs":429489}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"0476c44a80aa9574","findings":[{"citation":"resolved","description":"SIMDVault.bindLaunch (src/SIMDVault.sol:76-77) calls stakeRewards.setToken and jobsVault.setToken outside any try/catch, and JobsVault.setToken makes an unguarded staticcall to decimals(), an optional ERC-20 function that LaunchPool.tokenOf (src/libraries/LaunchPool.sol:16) does not check (it only requires code). The live factory (Sourcify, LaunchFactory.launch) deploys whatever tokenCreationCode the plane supplies and only checks balanceOf/totalSupply, so a launched token without decimals() is possible. For such a token every bindLaunch attempt reverts, token stays address(0), distribute() reverts with NotBound forever, and because the vault is the factory position's requester, every IMD fee that claimFees pays it (anyone can trigger that) is stuck in the vault with no exit. There is no owner or alternate path; the dev's one setup action is permanently blocked. Asymmetry: StakeRewards.setToken and SimdAirdrop.bind accept any token with code, JobsVault.setToken silently adds a stricter requirement. Trust assumption on the plane's LaunchToken being ERC20Metadata. Minimal fix preserving the design: read decimals() with a bounded try/catch and fall back to 18 (or revert with a named error and let bindLaunch tolerate a failed JobsVault.setToken the way it tolerates a failed airdrop bind).","line":44,"path":"src/JobsVault.sol","reproduction":"State: factory.positionOf(N) = (currency0 = IMD, currency1 = T, ..., requester = vault) where T is an ERC-20 with code but no decimals() selector. Call vault.bindLaunch(N, T) from DEV. Expected (spec): bind succeeds, StakeRewards and JobsVault get the token. Actual: the call reverts inside JobsVault.setToken (decimals() reverts), vault.token() stays 0. Then mint 100 IMD to the vault (as factory.claimFees would) and call vault.distribute(): reverts NotBound, IMD is permanently stuck. Shown by test_tokenWithoutDecimalsCannotBeBound_feesStuckInVault in test/scratch/Permissions.scratch.t.sol (passes on current code, i.e. the lock is real).","severity":"low","snippet":"        uint8 decimals = IERC20Metadata(token_).decimals();","title":"JobsVault.setToken makes bindLaunch depend on the launched token's optional decimals(); a token without it can never be bound and the vault's whole fee share is locked"},{"citation":"resolved","description":"notifyDeposit follows the specified algorithm exactly: when the vault has nothing new (received == 0) it returns before restarting the stream, so carryScaled, which already holds every scaled unit streamed while totalStaked was 0 plus the rate-division remainder, is never paid to stakers until a later deposit with received > 0 arrives. The first distribute() is permissionless and allowed immediately after bindLaunch, typically before anyone has staked, so the entire first 40% share is carried. For a test token with no trading volume there may never be another nonzero deposit, and weekly distribute() calls keep calling notifyDeposit with received == 0. The IMD is counted in reserved (so the balance >= reserved invariant holds) but no staker can ever claim it. This matches the spec text ('if 0 return'), so it is a design consequence rather than a code deviation; it is recorded because the money is reachable only by an outside donation. Any party can release it by transferring >= 1 wei IMD to StakeRewards before the next distribute(), which restarts a 7-day stream containing the carry. If the author wants the carry to flow without a donation, notifyDeposit could restart from carryScaled alone when received == 0, totalStaked != 0 and the period has ended; that is a spec change and needs the requester's decision.","line":95,"path":"src/StakeRewards.sol","reproduction":"After bindLaunch: mint 1000 IMD to the vault, call distribute() with nobody staked (StakeRewards receives 400 IMD, carry accrues over the week). Warp 7 days, alice stakes 1 token, warp 7 days, call distribute() with the vault at 0 IMD, warp 7 days, alice.claim(). Expected by a staker: some of the 400 IMD. Actual: alice receives 0, rewards.reserved() == 400e18, rewards.carryScaled() == 400e18 * 1e36, balance of StakeRewards 400e18 and no caller can move it. Then mint 1 wei IMD to StakeRewards and call distribute() again: after 7 days alice claims ~400 IMD. Shown by test_carryReleasedOnlyByNonZeroDeposit_andRecoverableByDonation in test/scratch/Permissions.scratch.t.sol.","severity":"info","snippet":"        if (received == 0) return;","title":"Carried IMD in StakeRewards (zero-staker time and rate remainder) is released only by a nonzero deposit; if fee income stops, up to a full week's deposit stays reserved but unreachable"},{"citation":"resolved","description":"launch() sets launched before the Intake call and the Intake forwards the price to the treasury in the same call with no refund path (intake-client reference). A body the live Intake accepts (non-empty, <= 16 KiB, exact IMD payment) but the off-chain plane refuses (status 1) or that fails (status 2) leaves launched == true permanently, so the dev wallet can never pay for a replacement launch through this vault. The spec requires 'only once', so this is the requested behaviour and the README documents it; it is recorded as an operational trust assumption because the only recovery is another launch whose requester is handed to the vault with LaunchFactory.setRequester (callable only by that launch's current requester), after which bindLaunch still works since it checks the factory position rather than the launched flag. No code change is recommended without a scope decision; the dev must validate the body (POST /requests/check) before paying.","line":65,"path":"src/SIMDVault.sol","reproduction":"Fund the vault with priceOf('launch.open@launch-1', IMD) = 0.5 IMD (live value read on chain). DEV calls vault.launch('{}'): the on-chain request succeeds, launched() == true, 0.5 IMD is spent. The plane refuses the body off-chain. DEV funds again and calls vault.launch(validBody): reverts AlreadyLaunched. Expected by the operator: a second attempt after an off-chain refusal; actual: impossible. Shown by test_launchFlagStaysSetAfterAcceptedPayment in test/scratch/Permissions.scratch.t.sol.","severity":"info","snippet":"        launched = true;","title":"The single launch opportunity is consumed by an accepted on-chain payment even when the plane later refuses the body; the vault has no callback, refund or retry"}],"hash":"08dbb745352ce793df7fc2f1d2f25609a5fb45bf475814932bab49744f4f9a17","nodeId":"fcbd37d3-fa0c-44c9-9c89-ef1c0f0d4ac9","outcome":"completed","summary":"Review complete. The findings file is at `.imd-findings.json` with 3 findings and 21 coverage rows; no source file was changed (the only additions are the findings file and a scratch test under `test/scratch/`).\n\n**Outcome for the permissions area (Access Control, Trust Gap, Asymmetry):** the permission model holds. Every state-changing entry point was traced; no unauthorised path moves IMD, staked tokens or airdrop tokens, no setter can be re-used, the only privileged actor is the fixed dev wallet with two one-shot actions, and the vault has no way to hand its fee share away on the live factory. The offline suite passes (61 tests, fork suite skipped for lack of an RPC URL). I did not rerun the three long invariant campaigns.\n\n**Findings recorded, all with reproductions confirmed by scratch tests:**\n\n- **Low, `JobsVault.setToken`:** it calls the launched token's optional `decimals()` outside the try/catch that protects the airdrop bind. A launched token without that function can never be bound, so `distribute` reverts forever and every IMD fee the factory pays the vault is stuck. The live factory only checks balance and supply of the token it deploys, so this rests on a trust assumption about the plane's token.\n- **Info, `StakeRewards.notifyDeposit`:** a zero-received call returns without restarting the stream, so IMD carried while nobody was staked stays reserved but unclaimable until a nonzero deposit. Spec-conformant; anyone can release it by donating one wei of IMD before a distribute.\n- **Info, `SIMDVault.launch`:** the single launch attempt is consumed by an accepted on-chain payment even if the plane later refuses the body. Spec-conformant and documented in the README; recovery is only through another launch handed over with `setRequester`.\n\n**Live checks made:** the Intake on chain 4663 exposes `request` and prices both actions at 0.5 IMD. The factory source on Sourcify confirms `claimFees` is permissionless and pays the current requester, and `setRequester` is callable only by that requester. No launch position exists on the new factory yet, so the real hook's `claimFees` gas against the vault's 1,000,000 cap could not be measured; a shortfall would only delay collection, since anyone can call `claimFees` directly.\n\nSources used for the public RPC: [Bitquery Robinhood Chain RPC page](https://docs.bitquery.io/docs/blockchain/robinhood/robinhood-chain-rpc-chain-id/), [QuickNode public RPC listing](https://www.quicknode.com/builders-guide/tools/robinhood-chain-public-rpc-by-robinhood-markets).","treeHash":null,"usage":{"cachedInputTokens":1689977,"cost":{"source":"runtime","usdMicros":5347787},"inputTokens":450,"model":"claude-fable-5-1","outputTokens":42066,"runtime":"claude","turns":40,"wallClockMs":768563}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"2c968e88904ec22b","findings":[{"citation":"resolved","description":"bind() is the only function that sets token/claimEnd, and it requires the contract to already hold at least TOTAL_WEIGHT (633,028,988.0235e18 raw units) of the launched token. claim() and burnUnclaimed() both revert with NotBound() while token == address(0) (src/SimdAirdrop.sol:67 and :80). The factory sends the launch remainder once, at launch time; if that remainder is even one unit short (totalSupply * (10000 - poolBps) / 10000 < TOTAL_WEIGHT, or economics.remainderTo pointing elsewhere), no further tokens arrive unless a third party donates, and the tokens already in the airdrop have no exit path at any time, not even after the 30-day window the brief describes. SIMDVault.bindLaunch deliberately swallows the failed bind (try/catch, src/SIMDVault.sol:79-83), so staking, jobs and distribution proceed while the remainder is stuck. Economic lens: this is the dependency-failure case where one external value (the remainder amount chosen in the launch body) permanently blocks every claim and the burn. Seam: execution (bind reverts, bindLaunch continues) x periphery (factory remainder) x first principles (the remainder is supposed to be claimable for 30 days and then burned). Fix needs a scope decision: either let burnUnclaimed() run while unbound once a fixed deadline has passed (for example 30 days after the vault's own bind, read from SIMDVault.token()/a stored bindTime), or let bind() accept any nonzero balance and keep TOTAL_WEIGHT only as the pro-rata denominator. The brief's text only says bind requires the full weight; it does not say the shortfall case should be unrecoverable.","line":54,"path":"src/SimdAirdrop.sol","reproduction":"Offline, with the project's mocks: mint TOTAL_WEIGHT - 1 of the launched token to vault.airdrop(); vm.prank(DEV); vault.bindLaunch(NUMBER, token). Expected: the airdrop eventually lets anyone recover or burn the funded tokens. Actual: vault.token() == token but airdrop.token() == address(0); vault.bindAirdrop() reverts InsufficientRemainder(); after vm.warp(+3650 days) airdrop.burnUnclaimed() reverts NotBound() and airdrop.claim(1, []) reverts NotBound(); token.balanceOf(airdrop) is still TOTAL_WEIGHT - 1 with no function able to move it. Verified with a scratch test on this tree (test passed asserting exactly these reverts and balance).","severity":"low","snippet":"        if (balance < TOTAL_WEIGHT) revert InsufficientRemainder();","title":"SimdAirdrop: a remainder below TOTAL_WEIGHT strands the airdrop's tokens forever (no claim, no burn)"},{"citation":"resolved","description":"launch() can be called once; the flag is consumed as soon as the Intake accepts the payment on chain. If the launch is then rejected or never deployed off-chain (there is no callback or refund, as the README states), no position with requester == vault will ever exist, so bindLaunch() always reverts LaunchPool.WrongRequester() and distribute() always reverts NotBound() (src/SIMDVault.sol:94). Every IMD unit in the vault beyond the exact price (overfunding, a price decrease between funding and launch, or a direct transfer) and any ETH received through receive() is then permanently stuck: the vault has no other transfer path by design. The same holds if the dev wallet simply never binds. The loss is bounded by what is sent to the vault before binding, which in the intended flow is exactly priceOf (0.5 IMD at the fork block), so the severity is low; it is reported because the Economic Security guide asks for every dependency failure that permanently blocks value, and because the brief's 'only once' wording leaves this failure path unaddressed. Fix needs a scope decision: either gate launched = true on the launch actually being bound (reset the flag if bindLaunch has not happened by a deadline), or allow distribute() to split IMD before binding (it would then skip claimFees and the burn). Both change the agreed 'only once' rule, so the author should decide rather than the reviewer.","line":64,"path":"src/SIMDVault.sol","reproduction":"Offline, with the project's mocks: imd.mint(vault, PRICE + 1 ether); vm.deal(vault, 1 ether); vm.prank(DEV); vault.launch('{}') succeeds and the Intake takes PRICE. Expected: the remaining 1 IMD and 1 ETH can eventually be distributed. Actual: vault.distribute() reverts NotBound(); vm.prank(DEV); vault.bindLaunch(anyNumber, token) reverts WrongRequester() because no factory position names the vault; vm.prank(DEV); vault.launch('{}') reverts AlreadyLaunched(). imd.balanceOf(vault) stays 1 ether and vault.balance stays 1 ether with no function able to move either. Verified with a scratch test on this tree.","severity":"low","snippet":"        if (launched) revert AlreadyLaunched();\n        launched = true;","title":"SIMDVault: IMD and ETH held before a successful bindLaunch have no exit once the one-time launch flag is consumed"},{"citation":"resolved","description":"requestJob() is the only path that moves IMD out of JobsVault, and it spends through IntakePayment.request, which reverts when priceOf(action, IMD) returns 0 and reverts when Intake.request itself rejects. The Intake is an external, IMD-operated contract; if it retires the action, changes the asset, or is paused, every requestJob reverts and the jobs budget (25.72% of every weekly distribution, accumulated forever) is stuck with no admin, sweep or fallback. This is the brief's explicit design ('No withdraw'), so it is recorded as a dependency trust assumption rather than a defect. Related: the same single Intake price also governs how fast a Sybil of 1,000,000-token wallets can drain the budget (one job of priceOf per wallet per 24h, 0.5 IMD at the fork block), which the brief accepts.","line":17,"path":"src/libraries/IntakePayment.sol","reproduction":"State: JobsVault holds any IMD; mock Intake configured with price 0 (or the live Intake returning 0 for bytes32('job.open@job-1')). Call: staker with >= minimumStake calls jobs.requestJob('{...}'). Expected per the brief: a job is paid. Actual: revert ActionNotSold(); no function on JobsVault can move its IMD. With the project's MockIntake: intake.configure(0, false, false); vm.prank(alice); jobs.requestJob(body) reverts IntakePayment.ActionNotSold().","severity":"info","snippet":"        if (price == 0) revert ActionNotSold();","title":"JobsVault: the 25.72% budget is permanently locked if the Intake stops selling job.open@job-1 (trust assumption, no withdraw by design)"},{"citation":"resolved","description":"amountOf divides by the constant TOTAL_WEIGHT, not by the actual sum of the leaves under MERKLE_ROOT. The 457 wallet/weight records are not in this tree, so neither the tests nor this review can check that the leaves sum to exactly 633028988023500000000000000 or that no address appears twice (claimed[] is per address, so a duplicated address could claim only one of its leaves). If the real sum exceeds TOTAL_WEIGHT, the sum of amountOf over all leaves exceeds startBalance and the last claimants' safeTransfer reverts with ERC20InsufficientBalance; if it is below, the difference is burned at claimEnd. The contract cannot enforce this; it is a precondition the list publisher must prove before claims open. Recorded as info so the judge has the concrete failure shape; no code change is implied unless the author wants on-chain enforcement, which a Merkle commitment cannot give.","line":63,"path":"src/SimdAirdrop.sol","reproduction":"Using the project's MerkleFixture to install a synthetic root (the production root cannot be exercised without the list): leaves (alice, TOTAL_WEIGHT) and (bob, 1e18); fund the airdrop with exactly TOTAL_WEIGHT, bind. alice.claim(TOTAL_WEIGHT, proof) transfers the whole startBalance. bob.claim(1e18, proof) with a valid proof: expected a share, actual revert ERC20InsufficientBalance (balance 0 < amountOf(1e18) = 1e18). The real list's sum is the unverified input.","severity":"info","snippet":"        return Math.mulDiv(startBalance, weight, TOTAL_WEIGHT);","title":"SimdAirdrop: pro-rata payout cannot verify the published list; a weight sum above TOTAL_WEIGHT makes late claimants fail"}],"hash":"0d94575d3919f91b060431fc944fe8adda7fdc6bfdbe07c62edfe12a6b95f30c","nodeId":"e62398e0-70c6-4dd8-9d56-94c376f496bc","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds four findings and 24 coverage rows. All snippets were verified against the tree, and my scratch files are removed.\n\n**Outcome.** The economics of this vault set hold. Every entry point in the Economic Security, Invariant and Flow Gap passes behaves as the brief specifies: the reward algorithm matches the stated Synthetix-with-carry formula line for line, the split sums to 100% with dust to marketing, conservation holds in StakeRewards, and no IMD or token can leave any contract except through the specified paths. The 66 offline tests and all 16 live fork tests pass here, run through a local forwarder to the contributor's endpoint.\n\n**Findings written:**\n- **Low, SimdAirdrop.bind.** If the launch remainder is below the list's total weight by even one unit, the airdrop never binds, and claim and burnUnclaimed both stay unreachable forever. The funded tokens have no exit. Reproduced offline.\n- **Low, SIMDVault.launch.** The one-time flag is consumed when the Intake accepts payment. If the launch is rejected off-chain or never bound, any IMD beyond the exact price and any ETH in the vault is stuck, since distribute needs a bound token. Reproduced offline.\n- **Info, JobsVault budget.** The jobs share has no withdraw by design, so if the Intake stops selling the job action, the accumulated budget is permanently locked. Recorded as a dependency trust assumption.\n- **Info, airdrop list integrity.** The payout divides by a constant total weight. If the published leaves sum higher, the last claimants' transfers revert. The list is not in the tree, so this cannot be checked on chain.\n\n**Leads I rejected after tracing.** A low-gas call to distribute cannot make the fee claim fail while the split proceeds, because the 63/64 rule starves the caller first. I probed gas limits from 60k to 400k and no such window exists. The real factory's fee claim used about 279k gas of the 1M limit on the fork, and the fee share arrives in IMD only. Scaled-math overflow needs a deposit above 1e41 units while IMD supply is near 7e22.\n\n**Not reached.** Sourcify verification has nothing to check yet, since no deployment exists. The 457-wallet dataset is absent, so the root and weight sum remain unverified. The claim-fee gas under the production hook could not be measured, but a heavier hook only degrades to manual fee claims by anyone, never to loss.","treeHash":null,"usage":{"cachedInputTokens":1188125,"chain":[{"calls":279,"chainId":4663,"failed":0,"own":275,"public":0,"refused":4}],"cost":{"source":"runtime","usdMicros":5318216},"inputTokens":418,"model":"claude-fable-5-1","outputTokens":47307,"runtime":"claude","turns":14,"wallClockMs":721872}},{"artifacts":[],"attempt":1,"bundleHash":"2f6d01fc1b3def480513c54172b25b690edaf9bffa3aa13685b7845a8cd3580d","device":"8b35536ee4821baa","findings":[],"hash":"2294b37b524be9a30d6673b59e5bda188d61f40218bc8dfb84fe43a17a6b09ac","nodeId":"dd8c5309-6d29-4f7e-a622-fbfc22c92d2c","outcome":"completed","summary":"Preserved the existing tests and documented the RPC issue in `test/README.md`.\n\n- `forge build` passes.\n- All **82 tests pass** with the required seed.\n- All **16 fork tests pass with RPC caching disabled**.\n- All three invariant campaigns pass.\n\nThe external checker may still reject Foundry’s optional `eth_getAccountInfo` probe despite its [supported fallback](https://github.com/foundry-rs/foundry-core/blob/327739f0d2b848725b462271276f27aecd8b8ff9/crates/fork-db/src/backend.rs#L357). Repairing that checker/RPC behavior is outside the permitted test paths.","treeHash":"afa21b3df61efcd6380cb4f4f9d4ec9409c7ffaf","usage":{"cachedInputTokens":4775296,"cost":{"source":"estimated","usdMicros":8894676},"inputTokens":228333,"model":"gpt-6-astra","outputTokens":36721,"runtime":"codex","turns":13,"wallClockMs":1210305}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"6b0a28df3d585600","findings":[{"citation":"resolved","description":"SIMDVault.bindLaunch validates requester, pair, code and non-IMD token (src/SIMDVault.sol:73) but not the airdrop's own precondition `number > MIN_LAUNCH_EXCLUSIVE` (src/SimdAirdrop.sol:51, `if (number <= MIN_LAUNCH_EXCLUSIVE) revert OldLaunch();`). The airdrop bind runs inside try/catch, so a launch numbered <= 3187 whose requester is the vault binds the vault, StakeRewards and JobsVault for good while SimdAirdrop.bind reverts with OldLaunch on every later bindAirdrop() retry. Because burnUnclaimed() and claim() both require a bound airdrop, any launched token already held by SimdAirdrop (the remainder) has no exit path at all, and the one-time vault binding cannot be redone. Reachable only through the dev wallet (an old launch's requester would have to hand the position to the vault with the factory's setRequester, or the brief's real launch would have to carry such a number), and the token is a valueless test token, so impact is low; but the failure is permanent and silent (only an AirdropBindAttempt(false) event).","line":73,"path":"src/SIMDVault.sol","reproduction":"State: factory.positionOf(3187) returns a pool pairing IMD with token T and requester == vault; SimdAirdrop holds >= TOTAL_WEIGHT of T. Calls: (1) DEV calls vault.bindLaunch(3187, T) -> succeeds, vault.token()==T, event AirdropBindAttempt(false), drop.token()==address(0). (2) anyone calls vault.bindAirdrop() -> reverts SimdAirdrop.OldLaunch. (3) anyone calls drop.burnUnclaimed() -> reverts SimdAirdrop.NotBound (also after 30 days: claimEnd is 0 but token is unset). (4) DEV calls vault.bindLaunch(<any other number>, T) -> reverts AlreadyBound. Expected: either bindLaunch rejects number <= 3187 up front (mirroring the airdrop) so the dev can bind the right launch, or the remainder has a recovery path. Actual: remainder stuck forever, vault bound to the wrong launch. Verified with a Foundry test on the mock harness (VaultTestBase): factory.setPosition(3187, IMD, token, vault); mint TOTAL_WEIGHT to drop; prank(DEV) bindLaunch(3187, token) passes; bindAirdrop reverts OldLaunch; burnUnclaimed reverts NotBound; distribute() still runs.","severity":"low","snippet":"        if (LaunchPool.tokenOf(FACTORY, number, address(this), IMD) != token_) revert WrongToken();","title":"bindLaunch accepts a launch number the airdrop can never bind (<= 3187), permanently stranding the remainder and fixing the vault to that launch"}],"hash":"575d6e9ab952c26041d319916d7835d9c0d4d732d2fc89eed95e461fe630cf94","nodeId":"e0c42ebf-3c3b-4cdd-b124-3fdaeb22f950","outcome":"completed","summary":"The review is complete. The findings file is written and the scratch directory removed; the only working-tree change is `.imd-findings.json`.\n\n**Outcome.** The control-flow area (Execution Trace, Periphery, First Principles) holds with one low-severity finding. I traced all 18 verifier-listed entry points plus three invariants and recorded a verdict for each. The 66 offline tests pass. I could not run the fork suite because this machine has no Robinhood Chain endpoint.\n\n**Finding 1, low.** `SIMDVault.bindLaunch` does not mirror the airdrop's `launchNumber > 3187` precondition. If the dev binds a launch numbered 3187 or lower whose requester is the vault, the vault, StakeRewards and JobsVault bind for good while `SimdAirdrop.bind` reverts with `OldLaunch` on every retry. Since `claim` and `burnUnclaimed` both require a bound airdrop, any remainder already held by the airdrop has no exit path, and the one-time vault binding cannot be redone. Reproduced on the mock harness with the exact call sequence recorded in the file. It needs a dev mistake and the token is valueless, hence low.\n\n**What holds, in brief.**\n- `distribute` sets the 7-day gate before any call, claims fees in try/catch, and the Sourcify ABI of the factory matches the project interface exactly. A `try` whose return data failed to decode would revert uncaught, so this was the key periphery check.\n- The split sums to the whole balance with dust to marketing. Burn and native push cannot block it. The vault's single reentrancy guard blocks a fee-callback re-entry.\n- StakeRewards implements the specified Synthetix-with-carry algorithm line for line. Reserved can only fall through `_claim`, and every streamed and earned division floors.\n- JobsVault locks before paying, and a failed Intake pull rolls back the lock, cooldown and allowance.\n- Every address constant matches the brief, no owner or sweep path exists, and SIMDVault's init code fits the protected floor's 49,152-byte limit.\n\n**Not verifiable here.** The Intake and IMD contracts are not verified on Sourcify, so their exact pull semantics rest on the pinned reference and the fork suite. The fork suite covers every case the brief lists, including the fuzz test, but a live run at block 111189501 is still owed by a machine with a chain 4663 endpoint.","treeHash":null,"usage":{"cachedInputTokens":622497,"cost":{"source":"runtime","usdMicros":4343728},"inputTokens":226,"model":"claude-fable-5-1","outputTokens":38835,"runtime":"claude","turns":27,"wallClockMs":585798}},{"artifacts":[],"attempt":1,"bundleHash":"93a2c7d029a1039dc14d1d12a42e02aab986e6e18f8ac9ca3d774cc66bfdd44f","device":"f90b809929565af3","findings":[],"hash":"6702a9d2a6a4956416fc8e533d7b418780a082bcb9bf584e3cbfa3882d27b4e0","nodeId":"0b0f2a8f-ff5e-4f18-ab77-94750080e81d","outcome":"completed","summary":"Implemented all four contracts, vendored dependencies, tests, and [deployment documentation](/root/.identitymd/work/f672522a-c16f-4528-9ef1-f18bec5d5a2a/0b0f2a8f-ff5e-4f18-ab77-94750080e81d/README.md).\n\n- `forge build` and `forge fmt --check` pass.\n- 52 offline tests and 12 Robinhood fork tests pass with the required seed.\n\nSourcify verification is prepared for post-deployment execution. Positive Merkle claim tests use labelled fixtures because the original proofs weren’t supplied.","treeHash":"362ef4f2b10f34eb1176aa5c39f6c15a9b73d2c2","usage":{"cachedInputTokens":5887232,"cost":{"source":"estimated","usdMicros":10041242},"inputTokens":141211,"model":"gpt-6-astra","outputTokens":54838,"runtime":"codex","turns":10,"wallClockMs":1545898}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"dc34db8e17664ebd","findings":[],"hash":"6727f0b3846a52f712b44e1be5693b9a0842bb2a2d054ff6cdcddfef48b02dc7","nodeId":"0f91ace0-1f55-4401-a046-b428e769362a","outcome":"failed","summary":"could not materialize reads: read pinned:0f91ace0-1f55-4401-a046-b428e769362a:18: fetch failed","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"claude","turns":0,"wallClockMs":47191}},{"artifacts":[],"attempt":1,"bundleHash":"9d24e0958941873e351f796a899ecacbbd1674ca61e386673e6daa39e8c6465e","device":"354bd4842bcfd989","findings":[],"hash":"83d9be53c5e98a4d76adac3177d7722ca31bbfd3560e5d909e130a3e6b100d53","nodeId":"8999d701-6885-466e-8e2e-f0595606cdbc","outcome":"completed","summary":"Created [launch.json](/root/.identitymd/work/f672522a-c16f-4528-9ef1-f18bec5d5a2a/8999d701-6885-466e-8e2e-f0595606cdbc/launch.json), listing `SIMDVault` with no constructor arguments; its constructor creates all three linked contracts.\n\nManifest structure and compiled ABI checked. `forge build` passed. Seeded `forge test`: 66 passed, 0 failed; fork suite skipped because no RPC URL was configured.\n\nOnly `launch.json` changed.","treeHash":"b9f1c7053d138d62b65c6c88fd68c1f82d74104e","usage":{"cachedInputTokens":521216,"inputTokens":52952,"model":"gpt-6-astra","outputTokens":3691,"runtime":"codex","turns":4,"wallClockMs":137818}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"4dd67dae195771b6","findings":[{"citation":"resolved","description":"The spec carries only the rate-division remainder (total % DURATION) into carryScaled. Two other floors are not carried: `streamed / totalStaked` in _update (line 138) and `Math.mulDiv(stakeOf, delta, SCALE)` in earned (line 89). Whatever those floors drop stays counted in `reserved` (reserved only decreases by paid claims), so it is never claimable, never re-streamed by a later notifyDeposit, and there is no other exit for IMD. Magnitude is bounded by totalStaked/1e36 raw IMD per _update plus < 1 raw unit per account update, so with realistic stakes (< 1e36 raw units) this is wei-level dust. It only becomes the whole deposit when received * 1e36 < totalStaked, which is not reachable with real IMD amounts. Reported as information because the algorithm is exactly the one the brief specifies; the brief's phrase 'No other way for IMD to leave' means this dust is intentionally locked. No action required unless the author wants the two floors folded into carryScaled as well.","line":138,"path":"src/StakeRewards.sol","reproduction":"Mock IMD at the fixed address, StakeRewards with token set. alice stakes 1e18, bob stakes 1e18 (totalStaked = 2e18). Mint 1 raw IMD to StakeRewards, vault calls notifyDeposit(): rewardRate = 1e36/604800 = 1653439153439153439153439153439, carryScaled = 1e36 % 604800 = 92800. Warp 7 days, both claim: rewardPerTokenStored = (1e36 - 92800)/2e18 = 499999999999999999 (floored), earned(each) = mulDiv(1e18, 499999999999999999, 1e36) = 0, both receive 0, reserved stays 1. Then mint 604800e18 and notifyDeposit() again, warp 7 days, both claim: they receive exactly 604800e18 in total and reserved is still 1 (expected under exact arithmetic: 604800e18 + 1 paid, reserved 0). Second example: alice stakes 3 raw tokens, deposit 700000 raw IMD, claim every hour for 7 days then once more a week later: paid 699888, reserved 112 permanently.","severity":"info","snippet":"            else rewardPerTokenStored += streamed / totalStaked;","title":"StakeRewards: per-token and per-account floor dust is stranded in `reserved` forever and never re-streamed (unlike rate-division dust)"}],"hash":"ef392b191bda88b9c8f07714a679b4aa50d8f2a2a411d25d1b6387ad3c921f26","nodeId":"31819bd6-28ec-4ec0-bf45-77e2158bd00e","outcome":"completed","summary":"The review is complete. The findings file is written and validated, the cited snippet matches the tree, and the scratch test directory is removed so no source file changed.\n\n**Outcome**\n\nThe math area (Math Precision, Boundary, Numerical Gap) came up clean of medium-or-higher defects. The four contracts implement the brief's algorithm exactly, and I found no input that pays out more than deposited, breaks a split, or misfires at a time boundary.\n\n**What I checked and how it held**\n\n- **StakeRewards streaming algebra.** Streamed plus unstreamed remainder equals rate times DURATION per period, so carry conserves value across deposits. Overflow in `received * SCALE` needs a cumulative deposit above about 1.15e41 raw IMD. `earned` uses 512-bit `mulDiv`, so a 1-wei `totalStaked` cannot make later whales' `earned` overflow.\n- **Split arithmetic.** The four basis-point shares sum to 10,000. Floors leave at most three wei, which goes to marketing, so the vault empties every run. The existing fuzz test confirms conservation.\n- **Time boundaries.** The 7-day distribute window, 24-hour job cooldown and lock, and the 30-day airdrop window all use consistent strict-less-than checks. Claim closes and burn opens at the same instant. The existing tests already exercise these exact boundaries.\n- **Airdrop pro rata.** The balance-at-least-total-weight check in `bind` guarantees every listed weight maps to at least one raw unit, and the sum of floored shares can never exceed the snapshot.\n- **External boundaries.** Every external call was walked for no-code receivers, non-standard tokens, zero inputs and return handling. The bounded-gas burn and native push cannot block the split, and `decimals()` above 71 is rejected before `10 ** decimals` could overflow.\n- **Static analysis leads.** All slither math lines are false positives here: the modulo is the carry remainder, the divide-then-multiply is the intended carry with dust retained, and the strict equalities are zero fast paths.\n\n**One info-level finding recorded**\n\nFloor dust from `streamed / totalStaked` and the per-account `mulDiv` floor stays in `reserved` forever and is never re-streamed, unlike the rate-division remainder. A scratch run showed a 1-wei deposit against 2e18 staked pays nobody, and that wei remains after a later full deposit streams normally. At realistic magnitudes this is wei-level, and the brief's \"no other way for IMD to leave\" makes it intentional, so it carries no proof and needs no fix.\n\n**Not reached**\n\nThe 457-wallet dataset was not supplied, so I could not verify that the weights sum to the committed total or that wallets are unique. The coverage record states this on the `claim` row. Fork tests against live state are outside my area and were not run.","treeHash":null,"usage":{"cachedInputTokens":602332,"cost":{"source":"runtime","usdMicros":3919488},"inputTokens":226,"model":"claude-fable-5-1","outputTokens":33613,"runtime":"claude","turns":25,"wallClockMs":565787}}],"verification":[{"checks":[{"durationMs":7003,"exitCode":0,"name":"build","output":"Compiling 57 files with Solc 0.8.26\nSolc 0.8.26 finished in 6.86s\nCompiler run successful!\n","passed":true},{"durationMs":23274,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Deployment.t.sol:DeploymentTest\n[PASS] test_factoryConstructionLinksFourBoundedContractsWithoutEscapeOpcodesOrOwners() (gas: 17289762)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 342.92ms (342.70ms CPU time)\n\nRan 9 tests for test/SimdAirdrop.t.sol:SimdAirdropTest\n[PASS] testFuzz_proRataFloorsWithoutOverflow(uint256,uint256) (runs: 1000, μ: 234815, ~: 235073)\nLogs:\n  Bound result 79895563879568054401524832053613480145\n  Bound result 56160834850897184852356969\n\n[PASS] test_bindOnlyVaultOnceAndRequiresFundedRemainder() (gas: 379767)\n[PASS] test_claimAndBurnBeforeBindingRejected() (gas: 67028)\n[PASS] test_failedBurnCanBeRetried() (gas: 538119)\n[PASS] test_fixtureClaimWindowAndBurnExactBoundaryAndLateDonation() (gas: 1657889)\n[PASS] test_fixtureClaimsProRataOnlySenderOnceBadProofAndUnlistedGetNothing() (gas: 1665802)\n[PASS] test_fixtureFailedTransferDoesNotConsumeClaimAndReentryCannotDoubleClaim() (gas: 1665239)\n[PASS] test_oldLaunchForeignRequesterAndNonIMDPoolRejected() (gas: 500215)\n[PASS] test_productionRootListSizeWeightAndUnknownWalletRejection() (gas: 402652)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 1.35s (1.36s CPU time)\n\nRan 3 tests for test/RewardProperties.t.sol:RewardPropertiesTest\n[PASS] testFuzz_departingStakerGetsOnlyTheirTimeWeightedShare(uint96,uint96,uint96,uint32) (runs: 1000, μ: 1181596, ~: 1187421)\nLogs:\n  Bound result 226620743276\n  Bound result 695514037296186686768602\n  Bound result 1\n\n[PASS] testFuzz_emptyPeriodsConserveScaledCarryAndDoNotPayTheNextStakerRetroactively(uint96,uint96) (runs: 1000, μ: 1002340, ~: 1002368)\nLogs:\n  Bound result 3906604288209\n  Bound result 682312556822861625842791914\n\n[PASS] testFuzz_midStreamTopupCannotErasePreviouslyEarnedOrFutureRewards(uint96,uint96,uint32) (runs: 1000, μ: 812601, ~: 813388)\nLogs:\n  Bound result 1514\n  Bound result 3028\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 1.35s (3.96s CPU time)\n\nRan 7 tests for test/JobsVault.t.sol:JobsVaultTest\n[PASS] test_belowMillionWholeTokensRejectedWalletBalanceDoesNotQualify() (gas: 1098530)\n[PASS] test_bodyBoundsAndUnconfiguredToken() (gas: 3338014)\n[PASS] test_failureRollsBackCooldownLockAndApproval() (gas: 1333389)\n[PASS] test_impossibleDecimalsRejectBindingAtomically() (gas: 1080129)\n[PASS] test_intakeReentryCannotBuyExtraJob() (gas: 1187456)\n[PASS] test_onlyOneJobPerDayPerWalletLockKeepsEarning() (gas: 1967350)\n[PASS] test_thresholdUsesSixDecimalsAndOnlyVaultSetsOnce() (gas: 1814293)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 1.35s (44.94ms CPU time)\n\nRan 6 tests for test/FailureAtomicity.t.sol:FailureAtomicityTest\n[PASS] testFuzz_jobThresholdUsesActualDecimalsAtBothSidesOfBoundary(uint8) (runs: 1000, μ: 2023849, ~: 2023670)\nLogs:\n  Bound result 9\n\n[PASS] test_failedJobAfterAnExpiredLockDoesNotExtendThatLock() (gas: 1062392)\n[PASS] test_failedJobsShareRevertsFeeCollectionNotificationAndCooldownTogether() (gas: 1599715)\n[PASS] test_failedRewardPaymentDuringExitRestoresPrincipalAndAccruedRewards() (gas: 1148566)\n[PASS] test_uninitializedChildrenRejectUnauthorizedSettersAndInvalidDependencies() (gas: 2513084)\n[PASS] test_unsoldJobDoesNotLockAndCanRetryAtChangedLivePrice() (gas: 1060259)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 1.35s (1.28s CPU time)\n\nRan 19 tests for test/SIMDVault.t.sol:SIMDVaultTest\n[PASS] testFuzz_splitConservesEveryWei(uint256) (runs: 1000, μ: 600888, ~: 608331)\nLogs:\n  Bound result 240617004706660\n\n[PASS] test_badTokenBalanceCannotBlockIMDOrNativePayments() (gas: 627492)\n[PASS] test_bindAcceptsReverseCurrencyOrder() (gas: 373366)\n[PASS] test_bindRejectsForeignRequesterWrongTokenAndEveryBadPair() (gas: 523469)\n[PASS] test_bindingOnlyDevOnceAndBindsFundedAirdropAtomically() (gas: 449465)\n[PASS] test_constructorLinksChildrenWithNoExternalDependencies() (gas: 4615312)\n[PASS] test_distributionBeforeBindAndBeforeSevenDaysFailsExactBoundaryWorks() (gas: 454466)\n[PASS] test_failedAirdropBindDoesNotBlockAndAnyoneRetries() (gas: 492295)\n[PASS] test_failedFeeCollectionIncludingExhaustedGasStillSplitsAndBurns() (gas: 2048794)\n[PASS] test_feeCallbackCannotDistributeTwice() (gas: 632123)\n[PASS] test_firstDistributionImmediateClaimsFeesSplitsAndBurns() (gas: 974176)\n[PASS] test_individualDevMarketingSharesAndDustAreReportedSeparately() (gas: 567485)\n[PASS] test_launchOnlyDevOnceExactPaymentAndReset() (gas: 595533)\n[PASS] test_nativeRejectionAndExhaustedStipendNeverBlockBurnOrSplit() (gas: 1359004)\n[PASS] test_receiveNative() (gas: 36832)\n[PASS] test_rejectedLaunchRollsBackAndCanRetryAtNewPrice() (gas: 558610)\n[PASS] test_resetEvenIfIntakeDoesNotSpendFullApproval() (gas: 388758)\n[PASS] test_revertingFalseNoReturnOutOfGasMalformedBurnDoesNotBlockSplit() (gas: 2104547)\n[PASS] test_unsoldActionAndInsufficientFundingFailWithoutConsumingLaunch() (gas: 380275)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 1.35s (1.36s CPU time)\n\nRan 16 tests for test/StakeRewards.t.sol:StakeRewardsTest\n[PASS] testFuzz_sequencesNeverOverpayAndAlwaysCoverReserved(bytes32) (runs: 1000, μ: 14733673, ~: 14759257)\n[PASS] test_accessControlAndSettersCannotBeReused() (gas: 157706)\n[PASS] test_directDonationOnlyStartsEarningAfterVaultNotification() (gas: 648515)\n[PASS] test_emptyNotifyDoesNotExtendOrRecycleReservedAndClaimDoesNotBecomeDeposit() (gas: 722603)\n[PASS] test_failedClaimAndWithdrawalRestoreAllAccounting() (gas: 1092171)\n[PASS] test_feeOnTransferStakeCountsOnlyReceived() (gas: 450685)\n[PASS] test_lockOnlyExtendsDoesNotStopClaimingAndExactExpiryAllowsExit() (gas: 913883)\n[PASS] test_midStreamDepositKeepsUnstreamedRemainder() (gas: 743803)\n[PASS] test_noStakersDuringPartOfStreamIsCarriedWithoutRetroactiveRewards() (gas: 1259784)\n[PASS] test_nobodyStakedCarriesIntoNextDeposit() (gas: 756832)\n[PASS] test_rateDivisionDustIsKeptAsScaledCarry() (gas: 381233)\n[PASS] test_soleStakerEarnsWholeDepositOverSevenDays() (gas: 785061)\n[PASS] test_stakingJustBeforeDepositAndExitingAfterHourGetsOne168thOfShare() (gas: 978501)\n[PASS] test_tokenCallbacksCannotReenterStakeWithdrawClaimOrExit() (gas: 2243724)\n[PASS] test_twoStakersSplitByStake() (gas: 903788)\n[PASS] test_zeroStakeZeroReceivedAndOverWithdrawalRejected() (gas: 551846)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 1.39s (1.40s CPU time)\n\nRan 1 test for test/invariant/Airdrop.invariant.t.sol:AirdropCommitmentInvariantTest\n[PASS] invariant_airdropConservesTokensAndCannotReopenOrReallocate() (runs: 256, calls: 24576, reverts: 0)\n\n╭----------------+----------+-------+---------+----------╮\n| Contract       | Selector | Calls | Reverts | Discards |\n+========================================================+\n| AirdropHandler | advance  | 4941  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| AirdropHandler | bind     | 4887  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| AirdropHandler | burn     | 4926  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| AirdropHandler | claim    | 4965  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| AirdropHandler | donate   | 4857  | 0       | 0        |\n╰----------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 633028988023500000000000000\n  Bound result 1000000000000\n  Bound result 856245493\n  Bound result 273838\n  Bound result 12\n  Bound result 2\n  Bound result 243\n  Bound result 647765913871469760742998635486\n  Bound result 3\n  Bound result 5\n  Bound result 10291\n  Bound result 7309\n  Bound result 79228162514264337593543950336\n  Bound result 24576\n  Bound result 3657\n  Bound result 1161277\n  Bound result 1000000000000000001\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 10.50s (10.43s CPU time)\n\nRan 2 tests for test/invariant/Airdrop.invariant.t.sol:AirdropFixtureInvariantTest\n[PASS] invariant_airdropConservesTokensAndCannotReopenOrReallocate() (runs: 256, calls: 24576, reverts: 0)\n\n╭----------------+----------+-------+---------+----------╮\n| Contract       | Selector | Calls | Reverts | Discards |\n+========================================================+\n| AirdropHandler | advance  | 4941  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| AirdropHandler | bind     | 4887  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| AirdropHandler | burn     | 4926  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| AirdropHandler | claim    | 4965  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| AirdropHandler | donate   | 4857  | 0       | 0        |\n╰----------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 633028988023500000000000000\n  Bound result 1000000000000\n  Bound result 856245493\n  Bound result 273838\n  Bound result 12\n  Bound result 2\n  Bound result 243\n  Bound result 647765913871469760742998635486\n  Bound result 3\n  Bound result 5\n  Bound result 14742\n  Bound result 8164\n  Bound result 79228162514264337593543950336\n  Bound result 24576\n  Bound result 3800\n  Bound result 1161277\n  Bound result 1000000000000000001\n\n[PASS] test_handlerExercisesClaimFailureRetryDuplicateAndLateDonation() (gas: 2887818)\nLogs:\n  Bound result 633028988023500000000000000\n  Bound result 7\n  Bound result 11\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 11.24s (11.17s CPU time)\n\nRan 2 tests for test/invariant/VaultSystem.invariant.t.sol:VaultSystemInvariantTest\n[PASS] invariant_allFundsAndLiabilitiesRemainAccountedFor() (runs: 256, calls: 24576, reverts: 0)\n\n╭--------------------+-------------------+-------+---------+----------╮\n| Contract           | Selector          | Calls | Reverts | Discards |\n+=====================================================================+\n| VaultSystemHandler | advance           | 2247  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | claim             | 2183  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | distribute        | 2258  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | donateOtherAssets | 2261  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | fund              | 2209  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | launch            | 2245  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | notify            | 2229  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | requestJob        | 2172  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | stake             | 2288  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | unauthorized      | 2326  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | withdraw          | 2158  | 0       | 0        |\n╰--------------------+-------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 10000000000000000000000\n  Bound result 1000000000000000000000000\n  Bound result 2000000000000000000000000\n  Bound result 3000000000000000000\n  Bound result 500000000000000000\n  Bound result 3137556246661158954407997\n  Bound result 603000000000000000000000\n  Bound result 91\n  Bound result 345600\n  Bound result 259200\n  Bound result 3963\n  Bound result 9999999999999999999900001\n  Bound result 172800000000000000000000\n  Bound result 4\n  Bound result 1266\n  Bound result 345600000000000000000000\n  Bound result 1800000000\n  Bound result 1037\n  Bound result 1123200000000000000000000\n  Bound result 5679\n  Bound result 9614\n  Bound result 5144\n  Bound result 2412021544436743606165512081\n  Bound result 120\n  Bound result 3047922972\n  Bound result 2937486683195299395\n  Bound result 7688\n  Bound result 682209532\n  Bound result 253\n  Bound result 0\n  Bound result 6829123945816807190506786592\n  Bound result 3902527445965400231948674\n  Bound result 3889\n  Bound result 3346\n\n[PASS] test_handlerExercisesSuccessfulAndRejectedTransitions() (gas: 4677647)\nLogs:\n  Bound result 10000000000000000000000\n  Bound result 1000000000000000000000000\n  Bound result 2000000000000000000000000\n  Bound result 1\n  Bound result 100000000000000000000\n  Bound result 13000000000000000000\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000\n  Bound result 500000000000000000\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 15.52s (15.51s CPU time)\n\nRan 16 tests for test/fork/RobinhoodFork.t.sol:RobinhoodForkTest\n[PASS] testFork_airdropFixtureProofsDeadlineAndBurn() (gas: 1541989)\n[PASS] testFork_bindOnlyDevOnceRejectsForeignRequesterAndBindsRemainderInSameCall() (gas: 460751)\n[PASS] testFork_bindingWrongTokenRollsBackAndUnfundedAirdropCanBeRetriedByAnyone() (gas: 872934)\n[PASS] testFork_blocksAloneCannotAdvanceWeeklyCooldown() (gas: 535975)\n[PASS] testFork_jobsRealIntakeThresholdCooldownAndEarningWhileLocked() (gas: 2156806)\n[PASS] testFork_midStreamDepositKeepsRemainder() (gas: 1264252)\n[PASS] testFork_noStakersCarryIntoNextDeposit() (gas: 1281845)\n[PASS] testFork_oneHourStakerGetsOne168thOfShare() (gas: 1526647)\n[PASS] testFork_originalAirdropCommitmentRejectsForgedClaimAndBurnsAtExpiry() (gas: 496234)\n[PASS] testFork_realIntakeFailedJobRestoresStakeLockCooldownBudgetAndAllowance() (gas: 1500028)\n[PASS] testFork_realIntakeLaunchOnlyDevOnceAndApprovalReset() (gas: 444959)\n[PASS] testFork_realIntakeRejectedLaunchPreservesFundsAllowanceAndOneTimeFlag() (gas: 724854)\n[PASS] testFork_realSwapFeesSplitBurnAndWeeklyBoundary() (gas: 2652767)\n[PASS] testFork_soleStakerGetsWholeWeek() (gas: 1116646)\n[PASS] testFork_twoStakersSplitByStake() (gas: 1603716)\n[PASS] testFuzz_forkNeverOverpaysAndBalanceCoversReserved(uint128,uint128,uint32) (runs: 256, μ: 1874972, ~: 1890607)\nLogs:\n  Bound result 241\n  Bound result 60\n\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 23.10s (34.68s CPU time)\n\nRan 11 test suites in 23.11s (68.85s CPU time): 82 tests passed, 0 failed, 0 skipped (82 total tests)\n","passed":true},{"durationMs":97,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"JobsVault.requestJob(bytes)\",\"JobsVault.setToken(address)\",\"SIMDVault.bindAirdrop()\",\"SIMDVault.bindLaunch(uint64,address)\",\"SIMDVault.distribute()\",\"SIMDVault.launch(bytes)\",\"SIMDVault.receive()\",\"SimdAirdrop.bind(uint64)\",\"SimdAirdrop.burnUnclaimed()\",\"SimdAirdrop.claim(uint256,bytes32[])\",\"StakeRewards.claim()\",\"StakeRewards.exit()\",\"StakeRewards.lock(address,uint256)\",\"StakeRewards.notifyDeposit()\",\"StakeRewards.setJobsVault(address)\",\"StakeRewards.setToken(address)\",\"StakeRewards.stake(uint256)\",\"StakeRewards.withdraw(uint256)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":14,\"README.md\":100,\"foundry.toml\":13,\"remappings.txt\":2,\"scripts/verify-sourcify.sh\":61,\"src/JobsVault.sol\":64,\"src/Network.sol\":13,\"src/SIMDVault.sol\":155,\"src/SimdAirdrop.sol\":86,\"src/StakeRewards.sol\":165,\"src/interfaces/IIntake.sol\":15,\"src/interfaces/ILaunchFactory.sol\":19,\"src/libraries/IntakePayment.sol\":22,\"src/libraries/LaunchPool.sol\":18,\"test/Deployment.t.sol\":45,\"test/FailureAtomicity.t.sol\":157,\"test/JobsVault.t.sol\":142,\"test/README.md\":50,\"test/RewardProperties.t.sol\":102,\"test/SIMDVault.t.sol\":281,\"test/SimdAirdrop.t.sol\":164,\"test/StakeRewards.t.sol\":300,\"test/fork/LiveFactory.sol\":110,\"test/fork/RobinhoodFork.t.sol\":474,\"test/helpers/MerkleFixture.sol\":41,\"test/helpers/VaultTestBase.sol\":83,\"test/invariant/Airdrop.invariant.t.sol\":112,\"test/invariant/AirdropHandler.sol\":168,\"test/invariant/VaultSystem.invariant.t.sol\":116,\"test/invariant/VaultSystemHandler.sol\":270,\"test/mocks/MockERC20.sol\":83,\"test/mocks/MockFactory.sol\":73,\"test/mocks/MockIntake.sol\":70},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"2294b37b524be9a30d6673b59e5bda188d61f40218bc8dfb84fe43a17a6b09ac","verifiedTreeHash":"afa21b3df61efcd6380cb4f4f9d4ec9409c7ffaf","verifierVersion":"0.1.0+15e8bc6b"},{"checks":[{"durationMs":3936,"exitCode":0,"name":"build","output":"Compiling 51 files with Solc 0.8.26\nSolc 0.8.26 finished in 3.80s\nCompiler run successful!\n","passed":true},{"durationMs":1294,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/fork/RobinhoodFork.t.sol:RobinhoodForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 4.02ms (0.00ns CPU time)\n\nRan 1 test for test/Deployment.t.sol:DeploymentTest\n[PASS] test_factoryConstructionLinksFourBoundedContractsWithoutEscapeOpcodesOrOwners() (gas: 17289762)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 70.81ms (70.55ms CPU time)\n\nRan 7 tests for test/JobsVault.t.sol:JobsVaultTest\n[PASS] test_belowMillionWholeTokensRejectedWalletBalanceDoesNotQualify() (gas: 1098530)\n[PASS] test_bodyBoundsAndUnconfiguredToken() (gas: 3338014)\n[PASS] test_failureRollsBackCooldownLockAndApproval() (gas: 1333389)\n[PASS] test_impossibleDecimalsRejectBindingAtomically() (gas: 1080129)\n[PASS] test_intakeReentryCannotBuyExtraJob() (gas: 1187456)\n[PASS] test_onlyOneJobPerDayPerWalletLockKeepsEarning() (gas: 1967350)\n[PASS] test_thresholdUsesSixDecimalsAndOnlyVaultSetsOnce() (gas: 1814293)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 1.02s (10.01ms CPU time)\n\nRan 9 tests for test/SimdAirdrop.t.sol:SimdAirdropTest\n[PASS] testFuzz_proRataFloorsWithoutOverflow(uint256,uint256) (runs: 1000, μ: 234817, ~: 235073)\nLogs:\n  Bound result 391272724786703838856281725603017546474232664435569707606221242\n  Bound result 433645289600182858029615232\n\n[PASS] test_bindOnlyVaultOnceAndRequiresFundedRemainder() (gas: 379767)\n[PASS] test_claimAndBurnBeforeBindingRejected() (gas: 67028)\n[PASS] test_failedBurnCanBeRetried() (gas: 538119)\n[PASS] test_fixtureClaimWindowAndBurnExactBoundaryAndLateDonation() (gas: 1657889)\n[PASS] test_fixtureClaimsProRataOnlySenderOnceBadProofAndUnlistedGetNothing() (gas: 1665802)\n[PASS] test_fixtureFailedTransferDoesNotConsumeClaimAndReentryCannotDoubleClaim() (gas: 1665239)\n[PASS] test_oldLaunchForeignRequesterAndNonIMDPoolRejected() (gas: 500215)\n[PASS] test_productionRootListSizeWeightAndUnknownWalletRejection() (gas: 402652)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 1.07s (1.08s CPU time)\n\nRan 19 tests for test/SIMDVault.t.sol:SIMDVaultTest\n[PASS] testFuzz_splitConservesEveryWei(uint256) (runs: 1000, μ: 601157, ~: 608343)\nLogs:\n  Bound result 7202142220949169409177636813522220\n\n[PASS] test_badTokenBalanceCannotBlockIMDOrNativePayments() (gas: 627492)\n[PASS] test_bindAcceptsReverseCurrencyOrder() (gas: 373366)\n[PASS] test_bindRejectsForeignRequesterWrongTokenAndEveryBadPair() (gas: 523469)\n[PASS] test_bindingOnlyDevOnceAndBindsFundedAirdropAtomically() (gas: 449465)\n[PASS] test_constructorLinksChildrenWithNoExternalDependencies() (gas: 4615312)\n[PASS] test_distributionBeforeBindAndBeforeSevenDaysFailsExactBoundaryWorks() (gas: 454466)\n[PASS] test_failedAirdropBindDoesNotBlockAndAnyoneRetries() (gas: 492295)\n[PASS] test_failedFeeCollectionIncludingExhaustedGasStillSplitsAndBurns() (gas: 2048794)\n[PASS] test_feeCallbackCannotDistributeTwice() (gas: 632123)\n[PASS] test_firstDistributionImmediateClaimsFeesSplitsAndBurns() (gas: 974176)\n[PASS] test_individualDevMarketingSharesAndDustAreReportedSeparately() (gas: 567485)\n[PASS] test_launchOnlyDevOnceExactPaymentAndReset() (gas: 595533)\n[PASS] test_nativeRejectionAndExhaustedStipendNeverBlockBurnOrSplit() (gas: 1359004)\n[PASS] test_receiveNative() (gas: 36832)\n[PASS] test_rejectedLaunchRollsBackAndCanRetryAtNewPrice() (gas: 558610)\n[PASS] test_resetEvenIfIntakeDoesNotSpendFullApproval() (gas: 388758)\n[PASS] test_revertingFalseNoReturnOutOfGasMalformedBurnDoesNotBlockSplit() (gas: 2104547)\n[PASS] test_unsoldActionAndInsufficientFundingFailWithoutConsumingLaunch() (gas: 380275)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 1.09s (1.11s CPU time)\n\nRan 16 tests for test/StakeRewards.t.sol:StakeRewardsTest\n[PASS] testFuzz_sequencesNeverOverpayAndAlwaysCoverReserved(bytes32) (runs: 1000, μ: 14741679, ~: 14759257)\n[PASS] test_accessControlAndSettersCannotBeReused() (gas: 157706)\n[PASS] test_directDonationOnlyStartsEarningAfterVaultNotification() (gas: 648515)\n[PASS] test_emptyNotifyDoesNotExtendOrRecycleReservedAndClaimDoesNotBecomeDeposit() (gas: 722603)\n[PASS] test_failedClaimAndWithdrawalRestoreAllAccounting() (gas: 1092171)\n[PASS] test_feeOnTransferStakeCountsOnlyReceived() (gas: 450685)\n[PASS] test_lockOnlyExtendsDoesNotStopClaimingAndExactExpiryAllowsExit() (gas: 913883)\n[PASS] test_midStreamDepositKeepsUnstreamedRemainder() (gas: 743803)\n[PASS] test_noStakersDuringPartOfStreamIsCarriedWithoutRetroactiveRewards() (gas: 1259784)\n[PASS] test_nobodyStakedCarriesIntoNextDeposit() (gas: 756832)\n[PASS] test_rateDivisionDustIsKeptAsScaledCarry() (gas: 381233)\n[PASS] test_soleStakerEarnsWholeDepositOverSevenDays() (gas: 785061)\n[PASS] test_stakingJustBeforeDepositAndExitingAfterHourGetsOne168thOfShare() (gas: 978501)\n[PASS] test_tokenCallbacksCannotReenterStakeWithdrawClaimOrExit() (gas: 2243724)\n[PASS] test_twoStakersSplitByStake() (gas: 903788)\n[PASS] test_zeroStakeZeroReceivedAndOverWithdrawalRejected() (gas: 551846)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 1.13s (1.15s CPU time)\n\nRan 6 test suites in 1.13s (4.39s CPU time): 52 tests passed, 0 failed, 1 skipped (53 total tests)\n","passed":true},{"durationMs":80,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"JobsVault.requestJob(bytes)\",\"JobsVault.setToken(address)\",\"SIMDVault.bindAirdrop()\",\"SIMDVault.bindLaunch(uint64,address)\",\"SIMDVault.distribute()\",\"SIMDVault.launch(bytes)\",\"SIMDVault.receive()\",\"SimdAirdrop.bind(uint64)\",\"SimdAirdrop.burnUnclaimed()\",\"SimdAirdrop.claim(uint256,bytes32[])\",\"StakeRewards.claim()\",\"StakeRewards.exit()\",\"StakeRewards.lock(address,uint256)\",\"StakeRewards.notifyDeposit()\",\"StakeRewards.setJobsVault(address)\",\"StakeRewards.setToken(address)\",\"StakeRewards.stake(uint256)\",\"StakeRewards.withdraw(uint256)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":14,\"README.md\":100,\"foundry.toml\":13,\"remappings.txt\":2,\"scripts/verify-sourcify.sh\":61,\"src/JobsVault.sol\":64,\"src/Network.sol\":13,\"src/SIMDVault.sol\":155,\"src/SimdAirdrop.sol\":86,\"src/StakeRewards.sol\":165,\"src/interfaces/IIntake.sol\":15,\"src/interfaces/ILaunchFactory.sol\":19,\"src/libraries/IntakePayment.sol\":22,\"src/libraries/LaunchPool.sol\":18,\"test/Deployment.t.sol\":45,\"test/JobsVault.t.sol\":142,\"test/SIMDVault.t.sol\":281,\"test/SimdAirdrop.t.sol\":164,\"test/StakeRewards.t.sol\":300,\"test/fork/LiveFactory.sol\":110,\"test/fork/RobinhoodFork.t.sol\":349,\"test/helpers/MerkleFixture.sol\":41,\"test/helpers/VaultTestBase.sol\":83,\"test/mocks/MockERC20.sol\":83,\"test/mocks/MockFactory.sol\":73,\"test/mocks/MockIntake.sol\":70},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":2333,"exitCode":0,"name":"slither","output":"[high/medium] weak-prng at src/StakeRewards.sol:92: StakeRewards.notifyDeposit() (src/StakeRewards.sol#92-104) uses a weak PRNG: \"carryScaled = total % DURATION (src/StakeRewards.sol#100)\"\n[high/medium] reentrancy-balance at src/StakeRewards.sol:106: Reentrancy in StakeRewards.stake(uint256) (src/StakeRewards.sol#106-117):\n[medium/medium] divide-before-multiply at src/StakeRewards.sol:92: StakeRewards.notifyDeposit() (src/StakeRewards.sol#92-104) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/StakeRewards.sol:106: StakeRewards.stake(uint256) (src/StakeRewards.sol#106-117) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/StakeRewards.sol:82: StakeRewards.rewardPerToken() (src/StakeRewards.sol#82-86) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/StakeRewards.sol:157: StakeRewards._claim() (src/StakeRewards.sol#157-164) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/StakeRewards.sol:92: StakeRewards.notifyDeposit() (src/StakeRewards.sol#92-104) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/StakeRewards.sol:133: StakeRewards._update(address) (src/StakeRewards.sol#133-145) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/StakeRewards.sol:106: Reentrancy in StakeRewards.stake(uint256) (src/StakeRewards.sol#106-117):\n[medium/medium] unused-return at src/libraries/LaunchPool.sol:10: LaunchPool.tokenOf(address,uint64,address,address) (src/libraries/LaunchPool.sol#10-17) ignores return value by (key,None,None,requester) = ILaunchFactory(factory).positionOf(number) (src/libraries/LaunchPool.sol#11)\n[medium/medium] unused-return at src/SIMDVault.sol:92: SIMDVault.distribute() (src/SIMDVault.sol#92-123) ignores return value by () = ILaunchFactory(FACTORY).claimFees{gas: CLAIM_GAS}(launchNumber) (src/SIMDVault.sol#96-100)\n[low/medium] missing-zero-check at src/SimdAirdrop.sol:52: SimdAirdrop.bind(uint64).launchedToken (src/SimdAirdrop.sol#52) lacks a zero-check on :\n[low/medium] timestamp at src/JobsVault.sol:52: JobsVault.requestJob(bytes) (src/JobsVault.sol#52-63) uses timestamp for comparisons\n[low/medium] timestamp at src/StakeRewards.sol:82: StakeRewards.rewardPerToken() (src/StakeRewards.sol#82-86) uses timestamp for comparisons\n[low/medium] timestamp at src/SIMDVault.sol:92: SIMDVault.distribute() (src/SIMDVault.sol#92-123) uses timestamp for comparisons\n[low/medium] timestamp at src/StakeRewards.sol:147: StakeRewards._withdraw(uint256) (src/StakeRewards.sol#147-155) uses timestamp for comparisons\n[low/medium] timestamp at src/StakeRewards.sol:92: StakeRewards.notifyDeposit() (src/StakeRewards.sol#92-104) uses timestamp for comparisons\n[low/medium] timestamp at src/SimdAirdrop.sol:79: SimdAirdrop.burnUnclaimed() (src/SimdAirdrop.sol#79-85) uses timestamp for comparisons\n[low/medium] timestamp at src/StakeRewards.sol:133: StakeRewards._update(address) (src/StakeRewards.sol#133-145) uses timestamp for comparisons\n[low/medium] timestamp at src/SimdAirdrop.sol:66: SimdAirdrop.claim(uint256,bytes32[]) (src/SimdAirdrop.sol#66-76) uses timestamp for comparisons\n[low/medium] timestamp at src/StakeRewards.sol:157: StakeRewards._claim() (src/StakeRewards.sol#157-164) uses timestamp for comparisons","passed":true},{"durationMs":807,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/JobsVault.sol:44: Reentrancy: State change after external call (6 places)\n[low] large-numeric-literal at src/JobsVault.sol:27: Large Numeric Literal (10 places)\n[low] literal-instead-of-constant at src/SIMDVault.sol:104: Literal Instead of Constant (6 places)\n[low] non-reentrant-not-first at src/SIMDVault.sol:63: `nonReentrant` is Not the First Modifier (3 places)\n[low] unsafe-erc20-operation at src/SIMDVault.sol:143: Unsafe ERC20 Operation","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"6702a9d2a6a4956416fc8e533d7b418780a082bcb9bf584e3cbfa3882d27b4e0","verifiedTreeHash":"362ef4f2b10f34eb1176aa5c39f6c15a9b73d2c2","verifierVersion":"0.1.0+15e8bc6b"},{"checks":[{"durationMs":6817,"exitCode":0,"name":"build","output":"Compiling 57 files with Solc 0.8.26\nSolc 0.8.26 finished in 6.67s\nCompiler run successful!\n","passed":true},{"durationMs":14549,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/fork/RobinhoodFork.t.sol:RobinhoodForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 862.86µs (0.00ns CPU time)\n\nRan 1 test for test/Deployment.t.sol:DeploymentTest\n[PASS] test_factoryConstructionLinksFourBoundedContractsWithoutEscapeOpcodesOrOwners() (gas: 17289762)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 44.82ms (44.51ms CPU time)\n\nRan 9 tests for test/SimdAirdrop.t.sol:SimdAirdropTest\n[PASS] testFuzz_proRataFloorsWithoutOverflow(uint256,uint256) (runs: 1000, μ: 234815, ~: 235073)\nLogs:\n  Bound result 79895563879568054401524832053613480145\n  Bound result 56160834850897184852356969\n\n[PASS] test_bindOnlyVaultOnceAndRequiresFundedRemainder() (gas: 379767)\n[PASS] test_claimAndBurnBeforeBindingRejected() (gas: 67028)\n[PASS] test_failedBurnCanBeRetried() (gas: 538119)\n[PASS] test_fixtureClaimWindowAndBurnExactBoundaryAndLateDonation() (gas: 1657889)\n[PASS] test_fixtureClaimsProRataOnlySenderOnceBadProofAndUnlistedGetNothing() (gas: 1665802)\n[PASS] test_fixtureFailedTransferDoesNotConsumeClaimAndReentryCannotDoubleClaim() (gas: 1665239)\n[PASS] test_oldLaunchForeignRequesterAndNonIMDPoolRejected() (gas: 500215)\n[PASS] test_productionRootListSizeWeightAndUnknownWalletRejection() (gas: 402652)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 232.93ms (245.88ms CPU time)\n\nRan 19 tests for test/SIMDVault.t.sol:SIMDVaultTest\n[PASS] testFuzz_splitConservesEveryWei(uint256) (runs: 1000, μ: 600888, ~: 608331)\nLogs:\n  Bound result 1314478251\n\n[PASS] test_badTokenBalanceCannotBlockIMDOrNativePayments() (gas: 627492)\n[PASS] test_bindAcceptsReverseCurrencyOrder() (gas: 373366)\n[PASS] test_bindRejectsForeignRequesterWrongTokenAndEveryBadPair() (gas: 523469)\n[PASS] test_bindingOnlyDevOnceAndBindsFundedAirdropAtomically() (gas: 449465)\n[PASS] test_constructorLinksChildrenWithNoExternalDependencies() (gas: 4615312)\n[PASS] test_distributionBeforeBindAndBeforeSevenDaysFailsExactBoundaryWorks() (gas: 454466)\n[PASS] test_failedAirdropBindDoesNotBlockAndAnyoneRetries() (gas: 492295)\n[PASS] test_failedFeeCollectionIncludingExhaustedGasStillSplitsAndBurns() (gas: 2048794)\n[PASS] test_feeCallbackCannotDistributeTwice() (gas: 632123)\n[PASS] test_firstDistributionImmediateClaimsFeesSplitsAndBurns() (gas: 974176)\n[PASS] test_individualDevMarketingSharesAndDustAreReportedSeparately() (gas: 567485)\n[PASS] test_launchOnlyDevOnceExactPaymentAndReset() (gas: 595533)\n[PASS] test_nativeRejectionAndExhaustedStipendNeverBlockBurnOrSplit() (gas: 1359004)\n[PASS] test_receiveNative() (gas: 36832)\n[PASS] test_rejectedLaunchRollsBackAndCanRetryAtNewPrice() (gas: 558610)\n[PASS] test_resetEvenIfIntakeDoesNotSpendFullApproval() (gas: 388758)\n[PASS] test_revertingFalseNoReturnOutOfGasMalformedBurnDoesNotBlockSplit() (gas: 2104547)\n[PASS] test_unsoldActionAndInsufficientFundingFailWithoutConsumingLaunch() (gas: 380275)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 1.13s (1.05s CPU time)\n\nRan 7 tests for test/JobsVault.t.sol:JobsVaultTest\n[PASS] test_belowMillionWholeTokensRejectedWalletBalanceDoesNotQualify() (gas: 1098530)\n[PASS] test_bodyBoundsAndUnconfiguredToken() (gas: 3338014)\n[PASS] test_failureRollsBackCooldownLockAndApproval() (gas: 1333389)\n[PASS] test_impossibleDecimalsRejectBindingAtomically() (gas: 1080129)\n[PASS] test_intakeReentryCannotBuyExtraJob() (gas: 1187456)\n[PASS] test_onlyOneJobPerDayPerWalletLockKeepsEarning() (gas: 1967350)\n[PASS] test_thresholdUsesSixDecimalsAndOnlyVaultSetsOnce() (gas: 1814293)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 1.13s (6.51ms CPU time)\n\nRan 6 tests for test/FailureAtomicity.t.sol:FailureAtomicityTest\n[PASS] testFuzz_jobThresholdUsesActualDecimalsAtBothSidesOfBoundary(uint8) (runs: 1000, μ: 2023849, ~: 2023670)\nLogs:\n  Bound result 0\n\n[PASS] test_failedJobAfterAnExpiredLockDoesNotExtendThatLock() (gas: 1062392)\n[PASS] test_failedJobsShareRevertsFeeCollectionNotificationAndCooldownTogether() (gas: 1599715)\n[PASS] test_failedRewardPaymentDuringExitRestoresPrincipalAndAccruedRewards() (gas: 1148566)\n[PASS] test_uninitializedChildrenRejectUnauthorizedSettersAndInvalidDependencies() (gas: 2513084)\n[PASS] test_unsoldJobDoesNotLockAndCanRetryAtChangedLivePrice() (gas: 1060259)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 1.15s (1.15s CPU time)\n\nRan 3 tests for test/RewardProperties.t.sol:RewardPropertiesTest\n[PASS] testFuzz_departingStakerGetsOnlyTheirTimeWeightedShare(uint96,uint96,uint96,uint32) (runs: 1000, μ: 1181596, ~: 1187421)\nLogs:\n  Bound result 560\n  Bound result 3439\n  Bound result 2912\n\n[PASS] testFuzz_emptyPeriodsConserveScaledCarryAndDoNotPayTheNextStakerRetroactively(uint96,uint96) (runs: 1000, μ: 1002340, ~: 1002368)\nLogs:\n  Bound result 504069574261145170747834\n  Bound result 756404174359217516617546\n\n[PASS] testFuzz_midStreamTopupCannotErasePreviouslyEarnedOrFutureRewards(uint96,uint96,uint32) (runs: 1000, μ: 812601, ~: 813388)\nLogs:\n  Bound result 509740592477780991166\n  Bound result 112044292186730063318110223\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 1.15s (3.40s CPU time)\n\nRan 16 tests for test/StakeRewards.t.sol:StakeRewardsTest\n[PASS] testFuzz_sequencesNeverOverpayAndAlwaysCoverReserved(bytes32) (runs: 1000, μ: 14733673, ~: 14759257)\n[PASS] test_accessControlAndSettersCannotBeReused() (gas: 157706)\n[PASS] test_directDonationOnlyStartsEarningAfterVaultNotification() (gas: 648515)\n[PASS] test_emptyNotifyDoesNotExtendOrRecycleReservedAndClaimDoesNotBecomeDeposit() (gas: 722603)\n[PASS] test_failedClaimAndWithdrawalRestoreAllAccounting() (gas: 1092171)\n[PASS] test_feeOnTransferStakeCountsOnlyReceived() (gas: 450685)\n[PASS] test_lockOnlyExtendsDoesNotStopClaimingAndExactExpiryAllowsExit() (gas: 913883)\n[PASS] test_midStreamDepositKeepsUnstreamedRemainder() (gas: 743803)\n[PASS] test_noStakersDuringPartOfStreamIsCarriedWithoutRetroactiveRewards() (gas: 1259784)\n[PASS] test_nobodyStakedCarriesIntoNextDeposit() (gas: 756832)\n[PASS] test_rateDivisionDustIsKeptAsScaledCarry() (gas: 381233)\n[PASS] test_soleStakerEarnsWholeDepositOverSevenDays() (gas: 785061)\n[PASS] test_stakingJustBeforeDepositAndExitingAfterHourGetsOne168thOfShare() (gas: 978501)\n[PASS] test_tokenCallbacksCannotReenterStakeWithdrawClaimOrExit() (gas: 2243724)\n[PASS] test_twoStakersSplitByStake() (gas: 903788)\n[PASS] test_zeroStakeZeroReceivedAndOverWithdrawalRejected() (gas: 551846)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 1.15s (1.15s CPU time)\n\nRan 1 test for test/invariant/Airdrop.invariant.t.sol:AirdropCommitmentInvariantTest\n[PASS] invariant_airdropConservesTokensAndCannotReopenOrReallocate() (runs: 256, calls: 24576, reverts: 0)\n\n╭----------------+----------+-------+---------+----------╮\n| Contract       | Selector | Calls | Reverts | Discards |\n+========================================================+\n| AirdropHandler | advance  | 4941  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| AirdropHandler | bind     | 4887  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| AirdropHandler | burn     | 4926  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| AirdropHandler | claim    | 4965  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| AirdropHandler | donate   | 4857  | 0       | 0        |\n╰----------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 633028988023500000000000000\n  Bound result 1000000000000\n  Bound result 856245493\n  Bound result 273838\n  Bound result 12\n  Bound result 2\n  Bound result 243\n  Bound result 647765913871469760742998635486\n  Bound result 3\n  Bound result 5\n  Bound result 10291\n  Bound result 7309\n  Bound result 79228162514264337593543950336\n  Bound result 24576\n  Bound result 3657\n  Bound result 1161277\n  Bound result 1000000000000000001\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 10.21s (10.20s CPU time)\n\nRan 2 tests for test/invariant/Airdrop.invariant.t.sol:AirdropFixtureInvariantTest\n[PASS] invariant_airdropConservesTokensAndCannotReopenOrReallocate() (runs: 256, calls: 24576, reverts: 0)\n\n╭----------------+----------+-------+---------+----------╮\n| Contract       | Selector | Calls | Reverts | Discards |\n+========================================================+\n| AirdropHandler | advance  | 4941  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| AirdropHandler | bind     | 4887  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| AirdropHandler | burn     | 4926  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| AirdropHandler | claim    | 4965  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| AirdropHandler | donate   | 4857  | 0       | 0        |\n╰----------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 633028988023500000000000000\n  Bound result 1000000000000\n  Bound result 856245493\n  Bound result 273838\n  Bound result 12\n  Bound result 2\n  Bound result 243\n  Bound result 647765913871469760742998635486\n  Bound result 3\n  Bound result 5\n  Bound result 14742\n  Bound result 8164\n  Bound result 79228162514264337593543950336\n  Bound result 24576\n  Bound result 3800\n  Bound result 1161277\n  Bound result 1000000000000000001\n\n[PASS] test_handlerExercisesClaimFailureRetryDuplicateAndLateDonation() (gas: 2887818)\nLogs:\n  Bound result 633028988023500000000000000\n  Bound result 7\n  Bound result 11\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 10.44s (10.44s CPU time)\n\nRan 2 tests for test/invariant/VaultSystem.invariant.t.sol:VaultSystemInvariantTest\n[PASS] invariant_allFundsAndLiabilitiesRemainAccountedFor() (runs: 256, calls: 24576, reverts: 0)\n\n╭--------------------+-------------------+-------+---------+----------╮\n| Contract           | Selector          | Calls | Reverts | Discards |\n+=====================================================================+\n| VaultSystemHandler | advance           | 2247  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | claim             | 2183  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | distribute        | 2258  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | donateOtherAssets | 2261  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | fund              | 2209  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | launch            | 2245  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | notify            | 2229  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | requestJob        | 2172  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | stake             | 2288  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | unauthorized      | 2326  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| VaultSystemHandler | withdraw          | 2158  | 0       | 0        |\n╰--------------------+-------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 10000000000000000000000\n  Bound result 1000000000000000000000000\n  Bound result 2000000000000000000000000\n  Bound result 3000000000000000000\n  Bound result 500000000000000000\n  Bound result 3137556246661158954407997\n  Bound result 603000000000000000000000\n  Bound result 91\n  Bound result 345600\n  Bound result 259200\n  Bound result 3963\n  Bound result 9999999999999999999900001\n  Bound result 172800000000000000000000\n  Bound result 4\n  Bound result 1266\n  Bound result 345600000000000000000000\n  Bound result 1800000000\n  Bound result 1037\n  Bound result 1123200000000000000000000\n  Bound result 5679\n  Bound result 9614\n  Bound result 5144\n  Bound result 2412021544436743606165512081\n  Bound result 120\n  Bound result 3047922972\n  Bound result 2937486683195299395\n  Bound result 7688\n  Bound result 682209532\n  Bound result 253\n  Bound result 0\n  Bound result 6829123945816807190506786592\n  Bound result 3902527445965400231948674\n  Bound result 3889\n  Bound result 3346\n\n[PASS] test_handlerExercisesSuccessfulAndRejectedTransitions() (gas: 4677647)\nLogs:\n  Bound result 10000000000000000000000\n  Bound result 1000000000000000000000000\n  Bound result 2000000000000000000000000\n  Bound result 1\n  Bound result 100000000000000000000\n  Bound result 13000000000000000000\n  Bound result 1000000000000000000\n  Bound result 1000000000000000000\n  Bound result 500000000000000000\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 14.38s (14.38s CPU time)\n\nRan 11 test suites in 14.39s (41.02s CPU time): 66 tests passed, 0 failed, 1 skipped (67 total tests)\n","passed":true},{"durationMs":89,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"JobsVault.requestJob(bytes)\",\"JobsVault.setToken(address)\",\"SIMDVault.bindAirdrop()\",\"SIMDVault.bindLaunch(uint64,address)\",\"SIMDVault.distribute()\",\"SIMDVault.launch(bytes)\",\"SIMDVault.receive()\",\"SimdAirdrop.bind(uint64)\",\"SimdAirdrop.burnUnclaimed()\",\"SimdAirdrop.claim(uint256,bytes32[])\",\"StakeRewards.claim()\",\"StakeRewards.exit()\",\"StakeRewards.lock(address,uint256)\",\"StakeRewards.notifyDeposit()\",\"StakeRewards.setJobsVault(address)\",\"StakeRewards.setToken(address)\",\"StakeRewards.stake(uint256)\",\"StakeRewards.withdraw(uint256)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":14,\"README.md\":100,\"foundry.toml\":13,\"launch.json\":10,\"remappings.txt\":2,\"scripts/verify-sourcify.sh\":61,\"src/JobsVault.sol\":64,\"src/Network.sol\":13,\"src/SIMDVault.sol\":155,\"src/SimdAirdrop.sol\":86,\"src/StakeRewards.sol\":165,\"src/interfaces/IIntake.sol\":15,\"src/interfaces/ILaunchFactory.sol\":19,\"src/libraries/IntakePayment.sol\":22,\"src/libraries/LaunchPool.sol\":18,\"test/Deployment.t.sol\":45,\"test/FailureAtomicity.t.sol\":157,\"test/JobsVault.t.sol\":142,\"test/README.md\":50,\"test/RewardProperties.t.sol\":102,\"test/SIMDVault.t.sol\":281,\"test/SimdAirdrop.t.sol\":164,\"test/StakeRewards.t.sol\":300,\"test/fork/LiveFactory.sol\":110,\"test/fork/RobinhoodFork.t.sol\":474,\"test/helpers/MerkleFixture.sol\":41,\"test/helpers/VaultTestBase.sol\":83,\"test/invariant/Airdrop.invariant.t.sol\":112,\"test/invariant/AirdropHandler.sol\":168,\"test/invariant/VaultSystem.invariant.t.sol\":116,\"test/invariant/VaultSystemHandler.sol\":270,\"test/mocks/MockERC20.sol\":83,\"test/mocks/MockFactory.sol\":73,\"test/mocks/MockIntake.sol\":70},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"83d9be53c5e98a4d76adac3177d7722ca31bbfd3560e5d909e130a3e6b100d53","verifiedTreeHash":"b9f1c7053d138d62b65c6c88fd68c1f82d74104e","verifierVersion":"0.1.0+15e8bc6b"}]}