{"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":"f7b17bd6-1e82-427f-a758-605f6448810e","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"d15f04c5dc532438c8dcd255720a5389318e5d2d06739b672c2ec47b881b398b","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"7ee97481c2d24d97fea58760ac80b94dd8cef341fba3c3c3403f7499137a8027","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"797b207e558ff1f68fdc2f2079efca34be84f22b526f59e717d32aed173885b6","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"6c88a9b0a67b682c14ce7a7bd2076e5a0795c6f5434611b932bc39ca110a9c3b","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"b1c13455d258725150ec3043e6c449182900000f90e384760d58b40c93496d4d","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"2e3bd606d0b5ae235d91ff1b738539f91719e6680b0408f69e56392ee671500f","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"d59561da3b378461b7c749f6bd5084477d44993aa94972ddea90f542535751df","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"d59561da3b378461b7c749f6bd5084477d44993aa94972ddea90f542535751df","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"748057a8d58deab162f12eb022c2d27930295bcb7e46cf65f0eef67c72641d7c","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"980c62064f1f49ac43718ee001884c9f89c33a7e2a861e6119659a9c93ffa87d","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"4c76766b00591dfe982fe85d3de589fdb2f810e5137c301155d1c5e36a1d3b23","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"4c76766b00591dfe982fe85d3de589fdb2f810e5137c301155d1c5e36a1d3b23","skillId":"write-foundry-tests","state":"accepted"}],"objective":"A custom token: The mutual fun (TMF).\nToken name: The mutual fun\nToken symbol: TMF\nToken supply: 1,000,000,000 with 18 decimals, all minted once to the deployer in the constructor.","parentJobId":null,"planHash":"4e9f9f2c361ac55a5843dc939ac4403c145c2220db618a7a1e13e66828e94c0f","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"f7b17bd6-1e82-427f-a758-605f6448810e","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1994-the-mutual-fun"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51886","feedbackHash":"6cef6aa1d1470e8d27bdf54e782141b890c47228ef28f23530a4f6108bc0cefb","nodeKey":"audit_economics","submissionHash":"d15f04c5dc532438c8dcd255720a5389318e5d2d06739b672c2ec47b881b398b","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52135","feedbackHash":"6e0dcddb6755b29e5f3ff69b5e0abc3431e174024e5af43a8de8a56a0720dec9","nodeKey":"audit_flow","submissionHash":"7ee97481c2d24d97fea58760ac80b94dd8cef341fba3c3c3403f7499137a8027","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52678","feedbackHash":"4e1b27f537dfa3b129f10e4fac9f51ecaa9ace2729b50cbeff6742e1747d2527","nodeKey":"audit_judge","submissionHash":"797b207e558ff1f68fdc2f2079efca34be84f22b526f59e717d32aed173885b6","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51876","feedbackHash":"daa7974f32c87cf34a01895d9a7ff93640ca43e2e2d13c9b7dd139829d07b0ea","nodeKey":"audit_math","submissionHash":"6c88a9b0a67b682c14ce7a7bd2076e5a0795c6f5434611b932bc39ca110a9c3b","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51885","feedbackHash":"45bc7fa1c5b5931c83bd007efda738d2993b753b5c855de4b86df5841b35a26a","nodeKey":"audit_permissions","submissionHash":"b1c13455d258725150ec3043e6c449182900000f90e384760d58b40c93496d4d","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51465","feedbackHash":"feeba7565f30d7f18f9719f71f5036ae88ff2cb051ce6a64c7c13f59da564237","nodeKey":"build_contract_project","submissionHash":"2e3bd606d0b5ae235d91ff1b738539f91719e6680b0408f69e56392ee671500f","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52162","feedbackHash":"896f581b997a5e44fdbf9dc5998aef4272912acf44517497dcfabc752386cbe9","nodeKey":"manifest","submissionHash":"748057a8d58deab162f12eb022c2d27930295bcb7e46cf65f0eef67c72641d7c","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52169","feedbackHash":"5b3b0f58d1a9da1c8c665450ba893db30cb417a97859a1961a6f1d1a2b8ad628","nodeKey":"write_foundry_tests","submissionHash":"980c62064f1f49ac43718ee001884c9f89c33a7e2a861e6119659a9c93ffa87d","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"8101d408e1fab184d1d4bc85b23914617f6de205b269a10a7788629195b435f1","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"f94c72bae79ce987ebdbd45239b3fb798095f633148a42b35f47b6788f59c3bb","device":"f0c4ae5b8ada215c","findings":[],"hash":"2e3bd606d0b5ae235d91ff1b738539f91719e6680b0408f69e56392ee671500f","nodeId":"76ee0b3e-7c60-4fdd-8f1f-b8616b7e8246","outcome":"completed","summary":"The project is complete and verified.\n\n**What was built**\n\n- `src/MutualFunToken.sol` is the token. Contract identifier `MutualFunToken` (14 characters). It inherits OpenZeppelin ERC20 and its constructor mints the whole supply to `msg.sender`. Name \"The mutual fun\", symbol \"TMF\", 18 decimals, total supply 1,000,000,000 × 10^18. No owner, no mint, burn, pause, blacklist, fee, proxy or ether acceptance. The external surface is the thirteen standard ERC-20 functions plus five public constants.\n- `foundry.toml` pins solc 0.8.26, optimizer on, `bytecode_hash = \"none\"`, `cbor_metadata = false`, `ffi = false`, empty `fs_permissions`, and remappings for the two vendored libraries.\n- `lib/forge-std` (v1.11.0) and the five OpenZeppelin v5.4.0 files ERC20 needs are vendored as ordinary files. No `.git` directories or submodules remain.\n- `script/DeployMutualFunToken.s.sol` is a reference script whose `deploy()` function the tests call directly. It reads no environment.\n- `README.md` documents assumptions, manifest parameters, the 10/80/10 supply split performed by the factory, operational responsibilities, and states there is nothing to configure after launch.\n\n**Checks run**\n\n| Check | Result |\n|---|---|\n| `forge build` | passes |\n| `forge test` with the verifier's fuzz seed | 30 passed, 0 failed |\n| `forge test` with an empty environment | 30 passed |\n| `forge test` with seed 0x1 and 1000 fuzz runs | 30 passed |\n| `forge fmt --check` | clean |\n\n**Tests cover** metadata and constants, the single mint with its Transfer event, CREATE2 deployment from a factory stand-in landing the supply at the predicted address, exact-amount transfers and allowances, revert paths for insufficient balance, zero address, and missing allowance, the launch flows (swarm share, claim, remainder arriving whole), absence of mint/admin selectors, a runtime opcode scan for DELEGATECALL/CALLCODE/SELFDESTRUCT, and fuzz tests for supply conservation and random calls.\n\nThe one issue found while testing was a fuzz test of mine that double-counted when the fuzzer chose the deployer as recipient. The test was corrected. Nothing was left out, and no `launch.json` was written per the launch rules.","treeHash":"993c75e69c50ac74b2f6f83968511efc47995c1c","usage":{"cachedInputTokens":517999,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":18764,"runtime":"claude","turns":20,"wallClockMs":251755}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"d03fc84904b54d9e","findings":[],"hash":"6c88a9b0a67b682c14ce7a7bd2076e5a0795c6f5434611b932bc39ca110a9c3b","nodeId":"83d6fc5d-cd7c-4057-9062-22c04f8fc767","outcome":"completed","summary":"The review is complete. The findings file is written at the repository root with zero defects and nine coverage rows, and the working tree is otherwise untouched.\n\n**Outcome:** no math, boundary or numerical-gap defect in `src/MutualFunToken.sol`. The token adds one compile-time constant and one constructor mint to OpenZeppelin's ERC20, so every value-moving operation is the library's.\n\n**What I verified**\n\n- **Vendored library is genuine.** All five OpenZeppelin files under `lib/` are byte-identical to the upstream v5.4.0 tag, so the unchecked blocks in the transfer and allowance paths carry upstream's proven bounds.\n- **Supply constant is exact.** The expression evaluates to 10^27, divisible by 10^18 and by 10,000, so the 10/80/10 launch split leaves no dust. The aderyn large-literal lead is correct arithmetic, not a defect.\n- **Boundaries hold.** Transfers of the max uint, the whole balance, one above the balance, zero, and self-transfers of the whole balance all behave correctly. Allowance at max is not decremented and at max minus one is. The zero address cannot reach the mint branch through any public path.\n- **Conservation holds.** A multi-party fuzz over four actors with 2000 runs kept the sum of balances equal to the total supply throughout.\n- **No external boundaries exist.** The token has no external calls, no payable or fallback function, no bytes decoding, and no sentinel other than the library's zero-address guards.\n- **Seed magnitudes fit.** The 80 percent seed and the implied sqrt prices for either currency ordering stay inside Uniswap v4's integer bounds.\n\n**One note, not a finding.** The `decimals()` the chain sees is the library default rather than the token's own constant. Both are 18, so there is no failing state today. They agree by duplication rather than by wiring.\n\n**Coverage:** all three listed entry points are marked `holds`, with six additional invariant rows. Nothing in my area was left unreached.","treeHash":null,"usage":{"cachedInputTokens":759357,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":16808,"runtime":"claude","turns":26,"wallClockMs":238958}},{"artifacts":[],"attempt":1,"bundleHash":"e20dc639f4458eb54315e756851de39a857600c0a6631a38cc6b83c6c8e4df9e","device":"f9cb4fd544aa3c68","findings":[],"hash":"748057a8d58deab162f12eb022c2d27930295bcb7e46cf65f0eef67c72641d7c","nodeId":"72ca2029-e006-4f3d-87ee-e410d111fdd4","outcome":"completed","summary":"Done. The manifest is written and the only changed file is launch.json.\n\n**What the accepted tree contains.** One contract, `MutualFunToken`, an OpenZeppelin ERC20 with an empty constructor that mints the whole supply to msg.sender. The compiled ABI confirms the constructor takes no inputs. There are no application contracts.\n\n**What launch.json says.**\n- kind is custom_token, token contract MutualFunToken, name \"The mutual fun\", symbol TMF, 18 decimals, empty constructorArgs.\n- totalSupply is 1000000000000000000000000000, which is 1,000,000,000 tokens in minor units and exactly what the constructor mints.\n- contracts is an empty list.\n- pool is IMD at 0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127, fee 12500, tick spacing 60, initialPrice one to one, as the launch reference gives it.\n- economics is the job's object copied verbatim.\n- notes describe the token's fixed supply, lack of privileged functions and exact-amount transfers, under 800 characters.\n\n**Checks.** The build compiles cleanly and all 30 existing tests pass with the verifier's fuzz seed. A scripted check confirmed the manifest has only schema keys, the supply string, and the verbatim economics. Git status shows launch.json as the single new file.","treeHash":"fa2292b2a949ebbde25905e2f7f2d19075b4f753","usage":{"cachedInputTokens":186786,"inputTokens":130,"model":"claude-fable-5-1","outputTokens":2790,"runtime":"claude","turns":9,"wallClockMs":46349}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"96eeb8eb2acdbbf1","findings":[{"citation":"resolved","description":"Merged from audit_economics (its only finding; no other specialist raised it). The contract publishes TOKEN_DECIMALS and derives TOTAL_SUPPLY from it (line 29: `TOTAL_SUPPLY_TOKENS * 10 ** uint256(TOKEN_DECIMALS)`), but it does not override ERC20.decimals(), so decimals() is the value returned by lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol:77-78 (`return 18;`). Today both are 18, the manifest's token.decimals is 18 and token.totalSupply is 1e27, so no launch check or transfer is affected and this is not a defect in the deployed behaviour. It is a maintenance coupling: a future edit of TOKEN_DECIMALS alone would mint a supply that disagrees with decimals() while the constant-based tests still pass. Not blocking. Minimal fix that preserves behaviour: `function decimals() public pure override returns (uint8) { return TOKEN_DECIMALS; }` or remove the constant.","line":23,"path":"src/MutualFunToken.sol","reproduction":"Reproduced in test/scratch/Review.t.sol::test_decimalsAndSupplyAgree on the current tree: deploy `new MutualFunToken()`; `token.decimals()` returns 18 (from OZ ERC20.sol:78, no override exists in src/MutualFunToken.sol), `token.TOKEN_DECIMALS()` returns 18, `token.TOTAL_SUPPLY()` == 1_000_000_000 * 10**18 == `token.totalSupply()` and the deployer's balance equals it. Expected and actual agree today; the finding is that the two values are independent definitions, which the source confirms (no `decimals()` function in src/MutualFunToken.sol, `grep -n decimals src/MutualFunToken.sol` matches only the constant and its comment).","severity":"info","snippet":"    uint8 public constant TOKEN_DECIMALS = 18;","title":"decimals() is OpenZeppelin's hardcoded 18, not the published TOKEN_DECIMALS constant that sizes TOTAL_SUPPLY"},{"citation":"resolved","description":"Merged from audit_permissions (its only finding). MutualFunToken inherits OpenZeppelin ERC20 v5.4.0 unchanged: `approve` sets `_allowances[owner][spender] = value` absolutely (lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol:280) and `_spendAllowance` decrements (ERC20.sol:294). There is no increaseAllowance/decreaseAllowance or permit. This is the well-known property of the ERC-20 standard, present in every plain ERC-20, and not a permission bypass: only the owner can set their allowance and only the named spender can consume it (verified in test/scratch/Review.t.sol::test_approvePaths and ::test_transferFromPaths). Trust assumption to document for holders (set to 0 before changing a live allowance), not a code defect; changing it would widen the agreed token surface.","line":15,"path":"src/MutualFunToken.sol","reproduction":"Reproduced in test/scratch/Review.t.sol::test_approveOverwriteRace (passes on this code, i.e. the race is real). State: ALICE holds 1000e18 TMF. 1) ALICE: approve(BOB, 100e18). 2) BOB front-runs ALICE's pending reduction: transferFrom(ALICE, BOB, 100e18) succeeds, allowance 100e18 -> 0. 3) ALICE's approve(BOB, 50e18) executes, allowance = 50e18. 4) BOB: transferFrom(ALICE, BOB, 50e18) succeeds. Expected by ALICE: at most 50e18 spendable after the reduction. Actual: balanceOf(BOB) == 150e18.","severity":"info","snippet":"contract MutualFunToken is ERC20 {","title":"Standard ERC-20 approve overwrite race: lowering a non-zero allowance can be front-run so the spender takes old plus new amount"}],"hash":"797b207e558ff1f68fdc2f2079efca34be84f22b526f59e717d32aed173885b6","nodeId":"fd469da2-239d-4ad2-aa11-b46343f3f293","outcome":"completed","summary":"The review is complete. The findings file is valid JSON, both snippets match the tree exactly, and no tracked file changed.\n\n**Outcome:** no defects in the token. The code is OpenZeppelin ERC20 v5.4.0 with constants and a one-line constructor, and every launch-floor property holds.\n\n**Specialist findings, judged:**\n- The decimals coupling note from the economics specialist reproduced. `decimals()` is OpenZeppelin's hardcoded 18, independent of the `TOKEN_DECIMALS` constant that sizes the supply. Both are 18 today, so I kept it at info as a maintenance note, not a defect.\n- The approve overwrite race from the permissions specialist reproduced. It is the standard ERC-20 property and not a permission bypass, so it stays at info as a documented trust assumption.\n- The flow, math and test-writing specialists reported nothing, and I confirmed nothing was missed in their areas.\n- The aderyn large-numeric-literal lead is a style note with no behavioural effect and was dropped.\n\n**My own checks, all passing in a scratch test suite:** exact-amount transfers with correct revert paths, finite and infinite allowance handling, approve only touching the caller's allowance, mint of exactly 1e27 to the deployer under CREATE2 with no arguments, no admin or mint selectors, no ether acceptance, no DELEGATECALL, CALLCODE or SELFDESTRUCT in the runtime, and bytecode well under the size limits. The manifest matches the constructor, supply and decimals.\n\n**Coverage:** all three entry points answered as holds, plus three invariant rows. The protected floor tests could not be run locally because they import v4-core and floor files absent from this tree, but each property they assert was checked directly.","treeHash":null,"usage":{"cachedInputTokens":435967,"inputTokens":258,"model":"claude-fable-5-1","outputTokens":9860,"runtime":"claude","turns":13,"wallClockMs":224930}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"b97527597dd934a0","findings":[],"hash":"7ee97481c2d24d97fea58760ac80b94dd8cef341fba3c3c3403f7499137a8027","nodeId":"ece5acee-833d-46d9-9b8e-5792c1ce5507","outcome":"completed","summary":"The review is complete. `.imd-findings.json` is written at the repository root with an empty findings list and 11 coverage rows. No tracked file was changed and the scratch test directory is removed.\n\n**Result: no defects found.** The token is a 35-line wrapper over OpenZeppelin's ERC20 that adds only constants and a constructor minting the full supply to the deployer. Every control-flow path I could reach behaves exactly as the standard specifies.\n\n**What I verified**\n\n- **Vendored library integrity (Periphery).** All five files under the OpenZeppelin lib directory are byte-identical to upstream v5.4.0 by sha256. The message sender helper is plain, with no meta-transaction forwarder.\n- **Entry points (Execution Trace).** The compiled ABI has exactly the three state-changing functions the verifier listed, with no receive, fallback or payable surface. I traced approve, transfer and transferFrom through the inherited `_update`, `_approve` and `_spendAllowance` with concrete values: zero-address refusals, one-unit-above-balance revert, residual and infinite allowances, revocation, self-transferFrom without allowance, from equal to the zero address, and supply conservation under fuzzing. Ten scratch tests passed, plus the existing suite of 30.\n- **Deployment identity (First Principles).** The constructor mints to the creating contract under CREATE2, not the transaction origin, takes no arguments, and reads no chain state. The README's manifest values match the code.\n- **Launch flows.** With no transfer override, every hop moves exactly the requested amount, so the swarm share, claims, seed and swaps in the protected harness are satisfied by plain ERC-20 semantics.\n- **Static-analysis lead.** The aderyn large-literal note is the supply constant with digit separators; it equals the brief and cannot overflow.\n\n**Two observations recorded as coverage notes, not findings**, since neither has a failing input on this tree:\n\n- The approve race and allowance residual are inherent to ERC-20 and documented in the README.\n- The `TOKEN_DECIMALS` constant drives the supply calculation while `decimals()` is the inherited hardcoded 18. Both agree today. An edit to the constant would silently desynchronise them, which the author may want to guard by overriding `decimals()` to return the constant.\n\nNothing in my area was left unreached.","treeHash":null,"usage":{"cachedInputTokens":737927,"inputTokens":322,"model":"claude-fable-5-1","outputTokens":14540,"runtime":"claude","turns":29,"wallClockMs":210636}},{"artifacts":[],"attempt":1,"bundleHash":"ad78d8071c09c8eb23ce96daac4ea78f994d5129d14dcaed5c690fc0f26d7768","device":"e8816d4386532a66","findings":[],"hash":"980c62064f1f49ac43718ee001884c9f89c33a7e2a861e6119659a9c93ffa87d","nodeId":"37500112-bd0f-4429-aaab-4297fdad3986","outcome":"completed","summary":"Done. The suite now has 66 tests across three files, all passing with the verifier's fuzz seed and with a second seed.\n\n**What I added** (the existing `test/MutualFunToken.t.sol` is untouched):\n\n- `test/MutualFunToken.invariant.t.sol`: a handler with six actors (deployer, holders, a distributor stand-in, the remainderTo address) that drives transfer, transferAll, approve and transferFrom with bounded inputs, plus failure handlers that assert the exact revert for over-balance transfers, transfers to the zero address, pulls above allowance, pulls above balance with infinite allowance, zero-spender approvals, strangers pulling without allowance, ether sends, and undeclared selectors from any caller. Ghost balances and allowances are kept independently. Invariants: fixed total supply, actor balances sum to the supply with nothing at the zero address, token or handler, balances and allowances equal the ghosts, nobody holds more than ever moved, metadata immutable, no ether held. Runs at 256 runs by 64 depth with fail-on-revert on, via inline config.\n- `test/MutualFunToken.edges.t.sol`: 35 edge tests at 1000 fuzz runs. Same call twice for transfer, approve and transferFrom, approve overwrite and revoke, allowance one below infinity is decremented, no Approval event on spend, transferFrom from self needs an allowance, zero address cannot send or approve and cannot be pulled from, max uint256 and supply plus one revert with exact errors, tokens sent to the contract are stuck, short calldata reverts, and fuzzed round-trip, additivity, bystander isolation, per-spender allowances and stranger pulls.\n\n**Verification.** I ran a deliberately broken ghost copy in test/scratch and the balance invariant caught it, so the invariants are not vacuous. `forge fmt --check` passes on the new files.\n\n**No defects found.** The token is a thin wrapper over OpenZeppelin ERC20 v5.4 with constants and a constructor mint. Every failure path behaves as the standard requires, so no findings file was written.","treeHash":"3184f6351222a4a33c96acb81095489b621329c0","usage":{"cachedInputTokens":436318,"inputTokens":226,"model":"claude-fable-5-1","outputTokens":20840,"runtime":"claude","turns":17,"wallClockMs":344358}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"aca5d7170d77c721","findings":[{"citation":"resolved","description":"Asymmetry guide, approve <-> _spendAllowance pair. `approve` writes `_allowances[owner][spender] = value` as an absolute set (lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol:280) while `_spendAllowance` decrements it (ERC20.sol:301). The token inherits OpenZeppelin ERC20 v5.4.0 without override and adds no increaseAllowance/decreaseAllowance or permit. A holder who reduces an existing non-zero approval cannot do so atomically: a spender who sees the pending `approve` can spend the old allowance first and then the new one. This is the well-known ERC-20 approve race, inherent to the standard and present in every plain ERC20; the README documents infinite approvals but not this. It is not a permission bypass: nobody can raise an allowance but its owner, and nobody can consume it but the named spender (verified in test/scratch). Reported at info so the author can decide whether it is in scope. Fixing it would change the agreed design (adding `increaseAllowance`/`decreaseAllowance` or ERC20Permit-style helpers to the token's surface); the zero-cost alternative is operational: holders set an allowance to 0 before raising or lowering it, and the README should say so.","line":15,"path":"src/MutualFunToken.sol","reproduction":"State: ALICE holds 1000e18 TMF. 1) ALICE: approve(BOB, 100e18). 2) ALICE later broadcasts approve(BOB, 50e18) intending to lower it. 3) BOB front-runs step 2: transferFrom(ALICE, BOB, 100e18) succeeds (allowance 100e18 -> 0). 4) Step 2 executes: allowance set to 50e18. 5) BOB: transferFrom(ALICE, BOB, 50e18) succeeds. Expected by ALICE: BOB can take at most 50e18 after the reduction. Actual: BOB's balance is 150e18. Reproduced in test/scratch/AccessChecks.t.sol::test_approveOverwriteRace (passes on this code, i.e. the race is real).","severity":"info","snippet":"contract MutualFunToken is ERC20 {","title":"Allowance is an absolute overwrite: lowering an approval can be front-run so the spender obtains old plus new amount (standard ERC-20 property, inherited unchanged)"}],"hash":"b1c13455d258725150ec3043e6c449182900000f90e384760d58b40c93496d4d","nodeId":"976e9b9a-fb9d-4413-a80a-d498a46a5d26","outcome":"completed","summary":"The review is complete and the findings file is written. No tracked files were changed. The findings file and the scratch test are both gitignored.\n\n**Outcome: no defects in the assigned area.** The token is a 35-line wrapper over OpenZeppelin ERC20 v5.4.0 with no roles, modifiers, initializer, proxy, or admin surface. One info-level note was recorded.\n\nWhat I checked, against the Access Control, Trust Gap and Asymmetry guides:\n\n- **Permission model.** The compiled ABI exposes only approve, transfer, transferFrom, the view functions and five constant getters. Every storage writer traces to the caller's own balance or allowance. The deployer has no path over a holder's balance.\n- **Supply.** The constructor is the only reachable mint. Burn is unreachable because both transfer routes reject the zero receiver. I confirmed this with a scratch test.\n- **Vendored library.** All five OpenZeppelin files are byte-identical to the upstream v5.4.0 tag fetched from GitHub, so nothing hides in the library.\n- **Asymmetry pairs.** approve versus spend, transfer versus transferFrom, and the mint and burn branches of the update function are all mirror-consistent. Self-spend and third-party spend both require the exact allowance.\n- **Trust Gap seams.** No privileged actor, formula, settable recipient or caller-class branch exists, so no seam has a surface.\n- **Static-analysis lead.** The large-literal warning on line 26 is the brief's supply constant and evaluates to exactly 1e27.\n\nThe one recorded item is the standard ERC-20 approve overwrite race. Lowering an allowance from 100 to 50 lets a front-running spender take 150 in total. I reproduced it in a scratch test and filed it as info, since it is inherent to plain ERC-20 and fixing it would widen the agreed surface. The judge can decide whether a README note is wanted.\n\nCoverage rows answer all three listed entry points plus seven invariant rows. The existing 30-test suite passes.","treeHash":null,"usage":{"cachedInputTokens":506143,"inputTokens":226,"model":"claude-fable-5-1","outputTokens":12816,"runtime":"claude","turns":25,"wallClockMs":185396}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"dd3018ab6b18e7bc","findings":[{"citation":"resolved","description":"The contract publishes a TOKEN_DECIMALS constant and derives TOTAL_SUPPLY from it (line 29: TOTAL_SUPPLY_TOKENS * 10 ** uint256(TOKEN_DECIMALS)), but it never overrides ERC20.decimals(), which returns OpenZeppelin's hardcoded 18. On the code as it stands both are 18, so the manifest invariant TOTAL_SUPPLY == 1e9 * 10**decimals() holds and nothing fails; this is not an exploitable defect. It is a latent coupling gap: the public constant that integrators and the manifest step may read is not the value the token actually reports, so a future edit to one without the other would mint a supply that disagrees with decimals() while every test asserting the constants still passes. Minimal fix that preserves behaviour: add `function decimals() public pure override returns (uint8) { return TOKEN_DECIMALS; }`, or drop the constant.","line":23,"path":"src/MutualFunToken.sol","reproduction":"State that triggers it: TOKEN_DECIMALS changed to any value other than 18 (for example 6) without overriding decimals(). Then TOTAL_SUPPLY() == 1_000_000_000 * 10**6 and the constructor mints that, but decimals() still returns 18, so the manifest's token.decimals (read from decimals()) and token.totalSupply (the minted amount) would describe a 0.001 TMF supply. On the current tree the concrete call `token.decimals()` returns 18 and `token.TOKEN_DECIMALS()` returns 18, so no input fails today; verified with a scratch test asserting decimals() == TOKEN_DECIMALS and TOTAL_SUPPLY() == TOTAL_SUPPLY_TOKENS() * 10**decimals(), both of which pass.","severity":"info","snippet":"    uint8 public constant TOKEN_DECIMALS = 18;","title":"TOKEN_DECIMALS constant is not what decimals() returns; the two agree only by coincidence"}],"hash":"d15f04c5dc532438c8dcd255720a5389318e5d2d06739b672c2ec47b881b398b","nodeId":"1a12706a-e75e-4e37-bdb3-3ee38e38578c","outcome":"completed","summary":"The review is complete and `.imd-findings.json` is written in the repository root. No source file was changed and the scratch tests were removed.\n\n**Outcome: no exploitable defect in the economics of TMF.** The token is a 35-line wrapper over OpenZeppelin ERC20 with a single constructor mint, and every Economic Security, Invariant and Flow Gap failure mode I checked holds.\n\nWhat I verified:\n\n- **Library integrity.** All five vendored OpenZeppelin files diffed byte-identical to upstream v5.4.0, so there is no hidden fee, skim or allowance tamper in the code that actually runs. I also confirmed the vendored forge-std reports a deliberately failing assertion, so passing tests are meaningful.\n- **Conservation.** A stateful invariant run with random transfer, approve and transferFrom calls across eight actors kept the sum of balances equal to the 1e27 supply throughout. No mint, burn or burnFrom selector exists, so supply can neither grow nor shrink after the constructor.\n- **Launch flows.** Modelled the factory to distributor, distributor to claimant, factory to PoolManager seed, PoolManager to trader buy and sell, and factory to remainder flows using the v4 pull and push pattern. Every hop received exactly what was requested and the 80/10/10 split sums to supply.\n- **Privilege surface.** No owner, roles, pause, blacklist or transfer hook. The deployer cannot move a holder's balance without a standard approval. The runtime has no DELEGATECALL, CALLCODE or SELFDESTRUCT and refuses ether.\n- **Static analysis leads.** Slither reported nothing. The aderyn large-numeric-literal line is the readable underscore literal for the supply and is not a defect.\n\nOne item is recorded at info severity only. The contract publishes a `TOKEN_DECIMALS` constant and derives the supply from it, but never overrides `decimals()`, which returns OpenZeppelin's hardcoded 18. The two coincide today so nothing fails and no revision is needed. It is a latent coupling gap, and the minimal fix is a one-line override returning the constant.\n\nThe coverage record answers all three ABI entry points as `holds`, plus six invariant and dependency rows. Nothing in the assigned area was left unreached. The opening pool price derivation and the launch manifest itself were outside the token's code and were not reviewed beyond confirming the README's documented split and constructor arguments are consistent.","treeHash":null,"usage":{"cachedInputTokens":688253,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":12806,"runtime":"claude","turns":20,"wallClockMs":206552}}],"verification":[{"checks":[{"durationMs":848,"exitCode":0,"name":"build","output":"Compiling 28 files with Solc 0.8.26\nSolc 0.8.26 finished in 781.47ms\nCompiler run successful!\n","passed":true},{"durationMs":94,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 30 tests for test/MutualFunToken.t.sol:MutualFunTokenTest\n[PASS] testFuzz_randomCallCannotGrowSupply(bytes4,address,uint256) (runs: 256, μ: 33467, ~: 33478)\n[PASS] testFuzz_transferAboveBalanceReverts(uint256,uint256) (runs: 256, μ: 93140, ~: 93433)\nLogs:\n  Bound result 307298818332758923730984920\n  Bound result 1\n\n[PASS] testFuzz_transferConservesSupply(address,uint256) (runs: 256, μ: 91525, ~: 92091)\nLogs:\n  Bound result 717567601365224864953796400\n\n[PASS] testFuzz_transferFromWithinAllowance(uint256,uint256) (runs: 256, μ: 130347, ~: 131091)\nLogs:\n  Bound result 307298818332757614608652738\n  Bound result 0\n\n[PASS] test_approveAndTransferFrom() (gas: 196289)\n[PASS] test_approveZeroSpenderReverts() (gas: 30804)\n[PASS] test_constantsMatchBrief() (gas: 40821)\n[PASS] test_constructorTakesNoArgumentsAndNeedsNoOtherContract() (gas: 9327)\n[PASS] test_create2DeploymentMintsToTheFactory() (gas: 891769)\n[PASS] test_deployScriptFunctionMintsToCaller() (gas: 1544064)\n[PASS] test_deployerCannotMoveAHoldersBalance() (gas: 152380)\n[PASS] test_emitsTransferFromZeroOnMint() (gas: 5900)\n[PASS] test_infiniteAllowanceIsNotReduced() (gas: 160558)\n[PASS] test_launchFlowsMoveExactAmounts() (gas: 1077642)\n[PASS] test_metadata() (gas: 29402)\n[PASS] test_mintsWholeSupplyToDeployerOnce() (gas: 22898)\n[PASS] test_noEtherAccepted() (gas: 36581)\n[PASS] test_noMintOrAdminSelectorExists() (gas: 655737)\n[PASS] test_runtimeHasNoDelegatecallCallcodeOrSelfdestruct() (gas: 921759)\n[PASS] test_transferEmitsEvent() (gas: 61837)\n[PASS] test_transferFromRevertsAboveAllowance() (gas: 133105)\n[PASS] test_transferFromRevertsAboveBalanceEvenWithAllowance() (gas: 135834)\n[PASS] test_transferFromRevertsWithoutAllowance() (gas: 85837)\n[PASS] test_transferMovesExactAmount() (gas: 77976)\n[PASS] test_transferRevertsOnInsufficientBalance() (gas: 33229)\n[PASS] test_transferRevertsOneAboveBalance() (gas: 85690)\n[PASS] test_transferToSelfKeepsBalance() (gas: 38019)\n[PASS] test_transferToZeroAddressReverts() (gas: 30818)\n[PASS] test_transferWholeBalance() (gas: 65583)\n[PASS] test_transferZeroAmountSucceeds() (gas: 40014)\nSuite result: ok. 30 passed; 0 failed; 0 skipped; finished in 7.63ms (30.74ms CPU time)\n\nRan 1 test suite in 8.21ms (7.63ms CPU time): 30 tests passed, 0 failed, 0 skipped (30 total tests)\n","passed":true},{"durationMs":34,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"MutualFunToken.approve(address,uint256)\",\"MutualFunToken.transfer(address,uint256)\",\"MutualFunToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":138,\"foundry.toml\":34,\"script/DeployMutualFunToken.s.sol\":23,\"src/MutualFunToken.sol\":35,\"test/MutualFunToken.t.sol\":365},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":435,"exitCode":0,"name":"slither","output":"slither: no results at low impact or above","passed":true},{"durationMs":221,"exitCode":0,"name":"aderyn","output":"[low] large-numeric-literal at src/MutualFunToken.sol:26: Large Numeric Literal","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"2e3bd606d0b5ae235d91ff1b738539f91719e6680b0408f69e56392ee671500f","verifiedTreeHash":"993c75e69c50ac74b2f6f83968511efc47995c1c","verifierVersion":"0.1.0+d952fceb"},{"checks":[{"durationMs":806,"exitCode":0,"name":"build","output":"Compiling 28 files with Solc 0.8.26\nSolc 0.8.26 finished in 725.72ms\nCompiler run successful!\n","passed":true},{"durationMs":95,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 30 tests for test/MutualFunToken.t.sol:MutualFunTokenTest\n[PASS] testFuzz_randomCallCannotGrowSupply(bytes4,address,uint256) (runs: 256, μ: 33467, ~: 33478)\n[PASS] testFuzz_transferAboveBalanceReverts(uint256,uint256) (runs: 256, μ: 93140, ~: 93433)\nLogs:\n  Bound result 6589144835141328013616\n  Bound result 153893\n\n[PASS] testFuzz_transferConservesSupply(address,uint256) (runs: 256, μ: 91525, ~: 92091)\nLogs:\n  Bound result 717567601365224864953796400\n\n[PASS] testFuzz_transferFromWithinAllowance(uint256,uint256) (runs: 256, μ: 130347, ~: 131091)\nLogs:\n  Bound result 307298818332757614608652738\n  Bound result 0\n\n[PASS] test_approveAndTransferFrom() (gas: 196289)\n[PASS] test_approveZeroSpenderReverts() (gas: 30804)\n[PASS] test_constantsMatchBrief() (gas: 40821)\n[PASS] test_constructorTakesNoArgumentsAndNeedsNoOtherContract() (gas: 9327)\n[PASS] test_create2DeploymentMintsToTheFactory() (gas: 891769)\n[PASS] test_deployScriptFunctionMintsToCaller() (gas: 1544064)\n[PASS] test_deployerCannotMoveAHoldersBalance() (gas: 152380)\n[PASS] test_emitsTransferFromZeroOnMint() (gas: 5900)\n[PASS] test_infiniteAllowanceIsNotReduced() (gas: 160558)\n[PASS] test_launchFlowsMoveExactAmounts() (gas: 1077642)\n[PASS] test_metadata() (gas: 29402)\n[PASS] test_mintsWholeSupplyToDeployerOnce() (gas: 22898)\n[PASS] test_noEtherAccepted() (gas: 36581)\n[PASS] test_noMintOrAdminSelectorExists() (gas: 655737)\n[PASS] test_runtimeHasNoDelegatecallCallcodeOrSelfdestruct() (gas: 921759)\n[PASS] test_transferEmitsEvent() (gas: 61837)\n[PASS] test_transferFromRevertsAboveAllowance() (gas: 133105)\n[PASS] test_transferFromRevertsAboveBalanceEvenWithAllowance() (gas: 135834)\n[PASS] test_transferFromRevertsWithoutAllowance() (gas: 85837)\n[PASS] test_transferMovesExactAmount() (gas: 77976)\n[PASS] test_transferRevertsOnInsufficientBalance() (gas: 33229)\n[PASS] test_transferRevertsOneAboveBalance() (gas: 85690)\n[PASS] test_transferToSelfKeepsBalance() (gas: 38019)\n[PASS] test_transferToZeroAddressReverts() (gas: 30818)\n[PASS] test_transferWholeBalance() (gas: 65583)\n[PASS] test_transferZeroAmountSucceeds() (gas: 40014)\nSuite result: ok. 30 passed; 0 failed; 0 skipped; finished in 7.36ms (31.86ms CPU time)\n\nRan 1 test suite in 8.10ms (7.36ms CPU time): 30 tests passed, 0 failed, 0 skipped (30 total tests)\n","passed":true},{"durationMs":35,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"MutualFunToken.approve(address,uint256)\",\"MutualFunToken.transfer(address,uint256)\",\"MutualFunToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":138,\"foundry.toml\":34,\"launch.json\":24,\"script/DeployMutualFunToken.s.sol\":23,\"src/MutualFunToken.sol\":35,\"test/MutualFunToken.t.sol\":365},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"748057a8d58deab162f12eb022c2d27930295bcb7e46cf65f0eef67c72641d7c","verifiedTreeHash":"fa2292b2a949ebbde25905e2f7f2d19075b4f753","verifierVersion":"0.1.0+d952fceb"},{"checks":[{"durationMs":1428,"exitCode":0,"name":"build","output":"Compiling 30 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.36s\nCompiler run successful!\n","passed":true},{"durationMs":9936,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 30 tests for test/MutualFunToken.t.sol:MutualFunTokenTest\n[PASS] testFuzz_randomCallCannotGrowSupply(bytes4,address,uint256) (runs: 256, μ: 33479, ~: 33482)\n[PASS] testFuzz_transferAboveBalanceReverts(uint256,uint256) (runs: 256, μ: 93086, ~: 93457)\nLogs:\n  Bound result 130416137252838426122347254\n  Bound result 13\n\n[PASS] testFuzz_transferConservesSupply(address,uint256) (runs: 256, μ: 91671, ~: 92091)\nLogs:\n  Bound result 5\n\n[PASS] testFuzz_transferFromWithinAllowance(uint256,uint256) (runs: 256, μ: 130121, ~: 131117)\nLogs:\n  Bound result 377264151910430917162792730\n  Bound result 13\n\n[PASS] test_approveAndTransferFrom() (gas: 196289)\n[PASS] test_approveZeroSpenderReverts() (gas: 30804)\n[PASS] test_constantsMatchBrief() (gas: 40821)\n[PASS] test_constructorTakesNoArgumentsAndNeedsNoOtherContract() (gas: 9327)\n[PASS] test_create2DeploymentMintsToTheFactory() (gas: 891769)\n[PASS] test_deployScriptFunctionMintsToCaller() (gas: 1544064)\n[PASS] test_deployerCannotMoveAHoldersBalance() (gas: 152380)\n[PASS] test_emitsTransferFromZeroOnMint() (gas: 5900)\n[PASS] test_infiniteAllowanceIsNotReduced() (gas: 160558)\n[PASS] test_launchFlowsMoveExactAmounts() (gas: 1077642)\n[PASS] test_metadata() (gas: 29402)\n[PASS] test_mintsWholeSupplyToDeployerOnce() (gas: 22898)\n[PASS] test_noEtherAccepted() (gas: 36581)\n[PASS] test_noMintOrAdminSelectorExists() (gas: 655737)\n[PASS] test_runtimeHasNoDelegatecallCallcodeOrSelfdestruct() (gas: 921759)\n[PASS] test_transferEmitsEvent() (gas: 61837)\n[PASS] test_transferFromRevertsAboveAllowance() (gas: 133105)\n[PASS] test_transferFromRevertsAboveBalanceEvenWithAllowance() (gas: 135834)\n[PASS] test_transferFromRevertsWithoutAllowance() (gas: 85837)\n[PASS] test_transferMovesExactAmount() (gas: 77976)\n[PASS] test_transferRevertsOnInsufficientBalance() (gas: 33229)\n[PASS] test_transferRevertsOneAboveBalance() (gas: 85690)\n[PASS] test_transferToSelfKeepsBalance() (gas: 38019)\n[PASS] test_transferToZeroAddressReverts() (gas: 30818)\n[PASS] test_transferWholeBalance() (gas: 65583)\n[PASS] test_transferZeroAmountSucceeds() (gas: 40014)\nSuite result: ok. 30 passed; 0 failed; 0 skipped; finished in 48.59ms (97.04ms CPU time)\n\nRan 35 tests for test/MutualFunToken.edges.t.sol:MutualFunTokenEdgesTest\n[PASS] testFuzz_allowanceIsPerSpender(uint256) (runs: 1000, μ: 73402, ~: 73718)\n[PASS] testFuzz_anyNonZeroRecipientReceivesExactly(address,uint256) (runs: 1000, μ: 80713, ~: 81880)\nLogs:\n  Bound result 10\n\n[PASS] testFuzz_lastApproveWins(uint256,uint256) (runs: 1000, μ: 87935, ~: 88019)\n[PASS] testFuzz_shortCalldataReverts(uint8) (runs: 1000, μ: 41359, ~: 39582)\nLogs:\n  Bound result 10\n\n[PASS] testFuzz_strangerCannotPull(address,uint256) (runs: 1000, μ: 41705, ~: 41721)\nLogs:\n  Bound result 10\n\n[PASS] testFuzz_transferDoesNotTouchBystanders(uint256,uint256) (runs: 1000, μ: 206028, ~: 207805)\nLogs:\n  Bound result 54128289\n  Bound result 23743498\n\n[PASS] testFuzz_transferFromOneAboveAllowanceReverts(uint256) (runs: 1000, μ: 96084, ~: 96313)\nLogs:\n  Bound result 1000000000000000000000\n\n[PASS] testFuzz_transferIsAdditive(uint256,uint256) (runs: 1000, μ: 112724, ~: 112752)\nLogs:\n  Bound result 284860035838899100996463073\n  Bound result 80852510753188364345852944\n\n[PASS] testFuzz_transferRoundTripRestoresBalances(uint256) (runs: 1000, μ: 104331, ~: 104255)\nLogs:\n  Bound result 30\n\n[PASS] test_allowanceDoesNotFollowTokensToANewHolder() (gas: 143599)\n[PASS] test_allowanceOneBelowInfinityIsDecremented() (gas: 116505)\n[PASS] test_approveLowerOverwritesHigher() (gas: 88118)\n[PASS] test_approveZeroRevokes() (gas: 116239)\n[PASS] test_constantsAreNotSettable() (gas: 107029)\n[PASS] test_deployerIsTheOnlyHolderAfterConstruction() (gas: 30462)\n[PASS] test_finiteAllowanceSpendEmitsNoApprovalEvent() (gas: 119009)\n[PASS] test_infiniteAllowanceEmitsNoApprovalOnSpend() (gas: 112628)\n[PASS] test_nobodyCanPullFromTheZeroAddress() (gas: 33552)\n[PASS] test_oneWeiTransferRoundTrip() (gas: 100725)\n[PASS] test_sameApproveTwiceOverwritesNotAccumulates() (gas: 85100)\n[PASS] test_sameTransferFromTwiceWithinAllowance() (gas: 160012)\n[PASS] test_sameTransferTwiceMovesTwice() (gas: 105624)\n[PASS] test_selfApprovalAllowsSelfTransferFrom() (gas: 171193)\n[PASS] test_spendingWholeAllowanceLeavesZeroAndSecondPullFails() (gas: 145466)\n[PASS] test_supplyFitsComfortablyInUint256() (gas: 15178)\n[PASS] test_tokensSentToTheContractItselfAreStuck() (gas: 101642)\n[PASS] test_transferFromRequiresAllowanceEvenFromSelf() (gas: 85884)\n[PASS] test_transferFromToOwnerKeepsBalanceSpendsAllowance() (gas: 94692)\n[PASS] test_transferFromToZeroAddressReverts() (gas: 83816)\n[PASS] test_transferFromZeroAmountWithoutAllowanceSucceeds() (gas: 43051)\n[PASS] test_transferMaxUint256Reverts() (gas: 33560)\n[PASS] test_transferSupplyPlusOneReverts() (gas: 33563)\n[PASS] test_twoDeploymentsAreIndependent() (gas: 32523)\n[PASS] test_zeroAddressCannotApprove() (gas: 30826)\n[PASS] test_zeroAddressCannotSend() (gas: 30827)\nSuite result: ok. 35 passed; 0 failed; 0 skipped; finished in 48.65ms (427.68ms CPU time)\n\nRan 1 test for test/MutualFunToken.invariant.t.sol:MutualFunTokenInvariantTest\n[PASS]\nMutualFunTokenInvariantTest invariants:\n[PASS] invariant_allowancesMatchGhosts\n[PASS] invariant_balancesMatchGhosts\n[PASS] invariant_metadataIsImmutable\n[PASS] invariant_nobodyHoldsMoreThanWasMoved\n[PASS] invariant_sumOfBalancesEqualsSupply\n[PASS] invariant_tokenHoldsNoEther\n[PASS] invariant_totalSupplyIsFixed\n MutualFunTokenInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------------+---------------------------------------+-------+---------+----------╮\n| Contract              | Selector                              | Calls | Reverts | Discards |\n+============================================================================================+\n| MutualFunTokenHandler | approve                               | 1393  | 0       | 0        |\n|-----------------------+---------------------------------------+-------+---------+----------|\n| MutualFunTokenHandler | approveZeroSpender                    | 1426  | 0       | 0        |\n|-----------------------+---------------------------------------+-------+---------+----------|\n| MutualFunTokenHandler | sendEther                             | 1382  | 0       | 0        |\n|-----------------------+---------------------------------------+-------+---------+----------|\n| MutualFunTokenHandler | strangerPullsFromHolder               | 1334  | 0       | 0        |\n|-----------------------+---------------------------------------+-------+---------+----------|\n| MutualFunTokenHandler | transfer                              | 1369  | 0       | 0        |\n|-----------------------+---------------------------------------+-------+---------+----------|\n| MutualFunTokenHandler | transferAboveBalance                  | 1296  | 0       | 0        |\n|-----------------------+---------------------------------------+-------+---------+----------|\n| MutualFunTokenHandler | transferAll                           | 1363  | 0       | 0        |\n|-----------------------+---------------------------------------+-------+---------+----------|\n| MutualFunTokenHandler | transferFrom                          | 1333  | 0       | 0        |\n|-----------------------+---------------------------------------+-------+---------+----------|\n| MutualFunTokenHandler | transferFromAboveAllowance            | 1361  | 0       | 0        |\n|-----------------------+---------------------------------------+-------+---------+----------|\n| MutualFunTokenHandler | transferFromAboveBalanceWithAllowance | 1393  | 0       | 0        |\n|-----------------------+---------------------------------------+-------+---------+----------|\n| MutualFunTokenHandler | transferToZeroAddress                 | 1401  | 0       | 0        |\n|-----------------------+---------------------------------------+-------+---------+----------|\n| MutualFunTokenHandler | unknownSelector                       | 1333  | 0       | 0        |\n╰-----------------------+---------------------------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 2\n  Bound result 10000000000000000001\n  Bound result 1\n  Bound result 1\n  Bound result 1162\n  Bound result 0\n  Bound result 2\n  Bound result 3\n  Bound result 4\n  Bound result 973\n  Bound result 2\n  Bound result 5\n  Bound result 1\n  Bound result 10089\n  Bound result 3\n  Bound result 5\n  Bound result 2\n  Bound result 3\n  Bound result 1\n  Bound result 1695\n  Bound result 4\n  Bound result 1\n  Bound result 0\n  Bound result 5\n  Bound result 859664823105636794369\n  Bound result 3\n  Bound result 13094773204629704574474388177827470467290\n  Bound result 2\n  Bound result 4\n  Bound result 4\n  Bound result 5\n  Bound result 0\n  Bound result 2\n  Bound result 1\n  Bound result 5\n  Bound result 5000000000000000000\n  Bound result 1\n  Bound result 4\n  Bound result 1\n  Bound result 3000000000000000000\n  Bound result 3\n  Bound result 5\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 4\n  Bound result 5352\n  Bound result 5\n  Bound result 244\n  Bound result 1\n  Bound result 4\n  Bound result 3\n  Bound result 2\n  Bound result 4\n  Bound result 3\n  Bound result 0\n  Bound result 5\n  Bound result 519\n  Bound result 1\n  Bound result 4\n  Bound result 1\n  Bound result 0\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129639934\n  Bound result 5\n  Bound result 1\n  Bound result 1400\n  Bound result 0\n  Bound result 1\n  Bound result 1974181\n  Bound result 5\n  Bound result 965\n  Bound result 2\n  Bound result 5394\n  Bound result 4\n  Bound result 3\n  Bound result 3\n  Bound result 2\n  Bound result 2\n  Bound result 4\n  Bound result 4817681693740691892847019812951363492365938739090\n  Bound result 2\n  Bound result 2\n  Bound result 3\n  Bound result 3\n  Bound result 5\n  Bound result 0\n  Bound result 0\n  Bound result 418280402802\n  Bound result 5\n  Bound result 2\n  Bound result 4\n  Bound result 11151\n  Bound result 4\n  Bound result 4\n  Bound result 0\n  Bound result 0\n  Bound result 2\n  Bound result 11599\n  Bound result 1\n  Bound result 4\n  Bound result 1\n  Bound result 4\n  Bound result 1\n  Bound result 1\n  Bound result 5\n  Bound result 4\n  Bound result 5\n  Bound result 1\n  Bound result 5\n  Bound result 18291006601092733268\n  Bound result 0\n  Bound result 5\n  Bound result 1\n  Bound result 2827\n  Bound result 1\n  Bound result 5\n  Bound result 0\n  Bound result 12643\n  Bound result 0\n  Bound result 0\n  Bound result 2\n  Bound result 129334087412704566129\n  Bound result 2\n  Bound result 24576\n  Bound result 2\n  Bound result 3\n  Bound result 0\n  Bound result 18\n  Bound result 2\n  Bound result 4\n  Bound result 4863\n  Bound result 3\n  Bound result 7\n  Bound result 4\n  Bound result 4863\n  Bound result 4\n  Bound result 7962\n  Bound result 2\n  Bound result 3081\n  Bound result 2\n  Bound result 3617\n  Bound result 0\n  Bound result 1\n  Bound result 628\n  Bound result 4\n  Bound result 5\n  Bound result 1\n  Bound result 0\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 9.86s (9.86s CPU time)\n\nRan 3 test suites in 9.86s (9.96s CPU time): 66 tests passed, 0 failed, 0 skipped (66 total tests)\n","passed":true},{"durationMs":39,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"MutualFunToken.approve(address,uint256)\",\"MutualFunToken.transfer(address,uint256)\",\"MutualFunToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":138,\"foundry.toml\":34,\"script/DeployMutualFunToken.s.sol\":23,\"src/MutualFunToken.sol\":35,\"test/MutualFunToken.edges.t.sol\":382,\"test/MutualFunToken.invariant.t.sol\":320,\"test/MutualFunToken.t.sol\":365},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"980c62064f1f49ac43718ee001884c9f89c33a7e2a861e6119659a9c93ffa87d","verifiedTreeHash":"3184f6351222a4a33c96acb81095489b621329c0","verifierVersion":"0.1.0+d952fceb"}]}