{"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":"250fe599-ee1d-420b-9044-bcd6f63ea0b0","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"49d746dd94fbe811505a4184c950cf881c562de92a2031013899b9e0d851cab0","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":"f9b35dfc26089124f57a546350802779a677179546d378f14e88b7ded06235fd","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":"14ed04be95a6f353f34aa4f4b237a37694aec8657085a2de0d95b221015a79e5","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":"bb363a2c41c0d9860a9aaeb90b0ab79e3e81bb723d673d70e55bbf4a3095fcbd","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":"9ed4a7de8571431b8963848603ceb5b178ff46316f7d744186a2dc6175c89ad5","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":"1fddc306a3f6cd9d92c1edde175dbbd8da1c22158a135bb67f587b0322a54cd8","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":"db8bcf071b886e64b787efd696308385a6148d93df49dbe3076a480e175d9301","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"f1368cc61d8db36ae160217f1489d2224f1dfeb3034fe29f35fb8984b6da112c","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: Muttualy Assured Destruction (MAD).\nToken name: Muttualy Assured Destruction\nToken symbol: MAD\nToken supply: 1,000,000,000 with 18 decimals, all minted once to the deployer in the constructor.","parentJobId":null,"planHash":"26ff5396df053d65b3ef639504727e2c6516f25ce47673b182bd56bcc3cf6209","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"250fe599-ee1d-420b-9044-bcd6f63ea0b0","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2472-muttualy-assured-destruction"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51168","feedbackHash":"2532bbcfc6a20067012cf71ce2e89c7e6bf2f911f9b34f0950e7d47e70f7ca23","nodeKey":"audit_economics","submissionHash":"49d746dd94fbe811505a4184c950cf881c562de92a2031013899b9e0d851cab0","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52585","feedbackHash":"c2b322eae31950762cd3fce84e37fa4feb9a9271ff554cf97b5043b9ef2a494c","nodeKey":"audit_flow","submissionHash":"f9b35dfc26089124f57a546350802779a677179546d378f14e88b7ded06235fd","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51427","feedbackHash":"fd73e89692ddd01a11a646ee36793e6a322a32ea2286d591e0e03e321da29994","nodeKey":"audit_judge","submissionHash":"14ed04be95a6f353f34aa4f4b237a37694aec8657085a2de0d95b221015a79e5","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52256","feedbackHash":"912a65b7eee57c1dfb5b632c0b512dea4c26623efdd9c62e6b84ff2f9c656f51","nodeKey":"audit_math","submissionHash":"bb363a2c41c0d9860a9aaeb90b0ab79e3e81bb723d673d70e55bbf4a3095fcbd","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52136","feedbackHash":"b219bc7bfc3c5550d97213dd998337cf62c25c79839b5d7b71a3f41b556fa4b4","nodeKey":"audit_permissions","submissionHash":"9ed4a7de8571431b8963848603ceb5b178ff46316f7d744186a2dc6175c89ad5","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52673","feedbackHash":"d64e2731e6c67aa404662ec4ee9ea530f4ee0d7f2f406ad192fad251e7daaf43","nodeKey":"build_contract_project","submissionHash":"1fddc306a3f6cd9d92c1edde175dbbd8da1c22158a135bb67f587b0322a54cd8","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52171","feedbackHash":"6a16c56eb16da8aaee02b88e07681d61e01c0dec17dd6937de74da2e8ae3b5af","nodeKey":"manifest","submissionHash":"db8bcf071b886e64b787efd696308385a6148d93df49dbe3076a480e175d9301","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52677","feedbackHash":"9353605729f4acea4d15a2aa2ca59e98e52bb7044a9b76ffd1a28a974171afdf","nodeKey":"write_foundry_tests","submissionHash":"f1368cc61d8db36ae160217f1489d2224f1dfeb3034fe29f35fb8984b6da112c","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"d3f98f01960e3b9a6803886f071173b6d30c30320e6583686f249d1170ac9272","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"5b9c505a673e1a8a","findings":[{"citation":"resolved","description":"Merged from audit_flow and audit_economics (same root cause). _transfer rejects to == address(0) but neither transferFrom nor _transfer rejects from == address(0). With value 0 the allowance check (allowance[0][caller] = 0, 0 < 0 is false) and the balance check in _transfer (balanceOf[0] = 0, 0 < 0 is false) both pass, so any unprivileged caller can make the contract emit Transfer(from=0x0, to=<any>, value=0), the same shape the constructor's mint emits. No balance, allowance or totalSupply changes, so there is no loss of funds and the launch flows are unaffected; the only effect is on off-chain consumers (indexers, explorers) that read a Transfer from the zero address as a mint, on a token whose README states the supply is minted once. A non-zero value from address(0) reverts (InsufficientAllowance first, then InsufficientBalance). OpenZeppelin v5 rejects this case with ERC20InvalidSender. Not a defect under the launch floor's rules; recorded as information. Minimal optional fix preserving behaviour: revert in _transfer when from == address(0).","line":43,"path":"src/MADToken.sol","reproduction":"Reproduced with a Foundry test (test/scratch, not kept). State: fresh deployment, factory = 0xFAC holds 1_000_000_000e18, bob = 0xB0B holds nothing and has no allowance from anyone. Call: vm.prank(bob); token.transferFrom(address(0), bob, 0). Expected (ERC-20 reference behaviour): revert, the zero address is not a valid sender. Actual: returns true and emits Transfer(address(0), 0xB0B, 0) (vm.expectEmit matched); totalSupply stays 1_000_000_000e18, balanceOf(bob) = 0, balanceOf(factory) = 1_000_000_000e18. Control: vm.prank(bob); token.transferFrom(address(0), bob, 1) reverts with InsufficientAllowance(0xB0B, 0, 1).","severity":"info","snippet":"    function transferFrom(address from, address to, uint256 value) external returns (bool) {\n        uint256 current = allowance[from][msg.sender];\n        if (current != type(uint256).max) {\n            if (current < value) revert InsufficientAllowance(msg.sender, current, value);","title":"transferFrom accepts from == address(0) with value 0 and emits a mint-shaped Transfer(address(0), to, 0) event"},{"citation":"resolved","description":"From audit_permissions. approve writes allowance[msg.sender][spender] = value as an absolute overwrite and transferFrom is the only decrementing reader. There is no increaseAllowance/decreaseAllowance or permit, so an owner lowering a non-zero allowance has no atomic way to do so: a spender who sees the pending approve(lower) can spend the old allowance first and then the new one. This is inherent to ERC-20 as specified (OpenZeppelin v5 ERC20 behaves identically); the spender never moves more than the sum of what the owner explicitly approved, and the owner can mitigate by approving 0 first. No privileged role exists in this contract. Recorded as information, not a defect requiring a change to the agreed design.","line":38,"path":"src/MADToken.sol","reproduction":"Reproduced with a Foundry test (test/scratch, not kept). State: factory = 0xFAC holds 1_000_000_000e18. Sequence: vm.prank(factory) approve(alice, 100); vm.prank(alice) transferFrom(factory, alice, 100) (allowance 100 -> 0); vm.prank(factory) approve(alice, 50) (intended to lower the outstanding allowance to 50); vm.prank(alice) transferFrom(factory, alice, 50). Expected under the owner's intent: alice can take at most 100 in total. Actual: balanceOf(alice) = 150 and every call succeeded.","severity":"info","snippet":"        allowance[msg.sender][spender] = value;","title":"approve() overwrites the allowance absolutely; the standard ERC-20 approve front-running race applies (no increase/decrease path)"}],"hash":"14ed04be95a6f353f34aa4f4b237a37694aec8657085a2de0d95b221015a79e5","nodeId":"2b2c499f-24f9-45e8-bea2-c53ab3d289e4","outcome":"completed","summary":"Review complete. The findings file is written and validated, the scratch tests are removed, and the tree is otherwise unchanged.\n\n**Outcome:** the MAD token has no defect that would block the launch. Both specialist observations reproduced and are kept at info severity. Nothing new surfaced in my own pass.\n\n**What I verified against the code**\n\n- **Zero-address sender in transferFrom.** Reported by two specialists, merged into one finding. A caller with no allowance can call `transferFrom(address(0), anyone, 0)` and the contract emits a mint-shaped zero-value Transfer event. Balances and supply never change, so this is cosmetic for indexers only. Reproduced with a Foundry test using expectEmit.\n- **Approve front-running race.** Standard ERC-20 behaviour, identical to OpenZeppelin v5. Reproduced the exact 100 then 50 sequence yielding 150 spent. Kept as info, not a defect in the agreed design.\n- **Dropped nothing** except the aderyn large-literal lead, which is the supply constant and not a defect.\n\n**Coverage:** all four entry points answered. Transfer and burn hold. Approve and transferFrom carry the two info findings. I also recorded two invariant rows: supply conservation with no mint path, and deployability under the factory's CREATE2 model, where the no-argument constructor, manifest supply, name, symbol, decimals and code size all match.\n\nThe existing 30 project tests pass on this code. No critical or high findings, so no proof files were attached.","treeHash":null,"usage":{"cachedInputTokens":346398,"inputTokens":226,"model":"claude-fable-5-1","outputTokens":6882,"runtime":"claude","turns":8,"wallClockMs":160314}},{"artifacts":[],"attempt":1,"bundleHash":"3848a6bd9c76c5e236152ded697553a4d59dd8c434b036338ec810f84781cbe9","device":"476778ae63dff113","findings":[],"hash":"1fddc306a3f6cd9d92c1edde175dbbd8da1c22158a135bb67f587b0322a54cd8","nodeId":"2703eef5-a214-41dc-9a7e-d139e83f1406","outcome":"completed","summary":"The MAD token is built. `forge build`, `forge test` (12 tests, run with the verifier's fuzz seed) and `forge fmt --check` all pass. I didn't make the commit: git stopped it because this machine has no author identity set, and I didn't set one for you. All the files are staged, so they're ready for the harness's upload or for you to commit.\n\n- **Contract:** `src/MADToken.sol` (contract `MADToken`), a plain ERC-20 written from scratch with no outside libraries. It returns name \"Muttualy Assured Destruction\", symbol MAD and 18 decimals.\n- **Supply:** all 1,000,000,000 tokens (1e27 in the smallest unit) are minted once, in the constructor, to the deployer (the launch factory). It takes no constructor arguments.\n- **No admin powers:** there is no owner, minting, pausing, blacklist or fee. Every transfer moves exactly the amount sent, so the launch transfers to the distributor and pool arrive in full. The only way the supply changes is a holder burning their own tokens with `burn`, and it can only shrink.\n- **Small choices of mine:** a maximum allowance counts as unlimited and is never reduced, and sending to the zero address is refused (use `burn` instead).\n- **Tests:** `test/MADToken.t.sol` checks the name, symbol, supply, transfers and allowances. It also checks failures: spending more than a balance or allowance, sending to the zero address, approving the zero address and calling a mint function that doesn't exist. A fuzz test checks the total supply never changes on transfer. I didn't run the protected launch checks from the read list here, because they need the launch's own settings and contracts that aren't in this repository. Passing tests are not a security audit.\n- **Config:** `foundry.toml` pins solc 0.8.26 with `bytecode_hash = \"none\"` and the optimizer on. forge-std is copied into `lib/forge-std` as plain files, not a git submodule.\n- **Docs:** `README.md` covers the assumptions and deployment details. Its \"After launch\" section says there is nothing to configure.","treeHash":"218306b9a7972ebd987ceddaf295e49c65409d24","usage":{"cachedInputTokens":237995,"inputTokens":12,"model":"claude-opus-5-5","outputTokens":5163,"runtime":"claude","turns":6,"wallClockMs":60882}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"6208734cdf5317a1","findings":[{"citation":"resolved","description":"Neither transferFrom nor _transfer rejects a zero `from` address. With value 0 the allowance check (0 < 0) and the balance check (0 < 0) both pass, so any unprivileged caller can make the token emit `Transfer(address(0), anyAddress, 0)` at will after launch. No balance or totalSupply changes, so there is no economic loss and every conservation invariant still holds (verified by a 128,000-call invariant run over transfer/transferFrom/burn/approve). The only effect is on off-chain consumers: indexers and explorers treat a Transfer from the zero address as a mint, so a stranger can spam post-launch 'mint' events on a token whose README promises no mint exists. OpenZeppelin v5 reverts this case with ERC20InvalidSender. Minimal fix that preserves intended behaviour: `if (from == address(0)) revert InvalidSender(from);` at the top of _transfer (or in transferFrom).","line":43,"path":"src/MADToken.sol","reproduction":"State: fresh deployment, bob = 0xB0B holds nothing and has no allowance. Call: vm.prank(bob); token.transferFrom(address(0), bob, 0). Expected (ERC-20 reference behaviour): revert, zero address is not a valid sender. Actual: returns true and emits Transfer(from=0x0, to=0xB0B, value=0); totalSupply stays 1_000_000_000e18 and every balance is unchanged. Confirmed with the scratch test test_zeroValueTransferFromZeroAddressSucceeds (expectEmit of Transfer(address(0), bob, 0) passes).","severity":"info","snippet":"    function transferFrom(address from, address to, uint256 value) external returns (bool) {\n        uint256 current = allowance[from][msg.sender];\n        if (current != type(uint256).max) {\n            if (current < value) revert InsufficientAllowance(msg.sender, current, value);","title":"transferFrom accepts from == address(0) for value 0 and emits a mint-shaped Transfer event"}],"hash":"49d746dd94fbe811505a4184c950cf881c562de92a2031013899b9e0d851cab0","nodeId":"53e9b2a3-ff47-42ea-b8ca-8b26dcfd762a","outcome":"completed","summary":"The review is complete and the findings file is written. No repository files were changed (git status is clean apart from the ignored findings file and the scratch test directory, which is not kept).\n\n**Result: no economic defect found.** The token is a 76-line fixed-supply ERC-20 with no fees, exemptions, owner or mint path, so the Economic Security, Invariant and Flow Gap failure modes mostly have nothing to attach to. I checked each one against the code and confirmed with tests rather than by reading alone.\n\n**What I verified**\n\n- **Conservation invariant.** A Foundry invariant run over transfer, transferFrom, burn and approve with four actors never broke sum of balances equals totalSupply, nor totalSupply at most the initial supply. Numbers below.\n- **Exact launch flows.** Fuzzed transfer round-trips deliver exactly the stated amount, so the factory to distributor, distributor to claimant and pool flows arrive whole. Self-transfer and self-transferFrom conserve balances because the add re-reads storage after the subtract.\n- **Unchecked arithmetic.** Every unchecked subtraction is guarded by a prior bound check, and the unchecked add cannot overflow because balances sum to at most 1e27.\n- **Supply growth and privileged hands.** No mint, owner, pause, blacklist, burnFrom, delegatecall or selfdestruct exists. The constructor mints the whole supply to msg.sender with no arguments, matching the job's name, symbol, decimals and supply.\n- **Project checks.** forge build, forge test and forge fmt --check all pass on the delivered files.\n\n| Check | Result |\n|---|---|\n| Invariant calls | 128,000 across 256 runs, 0 reverts, 0 violations |\n| Existing suite | 12 passed |\n| Scratch edge tests | 6 passed |\n\n**One info-level finding.** transferFrom does not reject a zero `from` address. With value 0, both the allowance and balance checks pass, so any caller can emit a Transfer from the zero address, which indexers display as a mint. No balance or supply changes, so there is no economic impact. The suggested fix is a one-line zero-sender revert in the internal transfer, which OpenZeppelin v5 also does.\n\n**Coverage.** All four entry points have rows: approve, burn and transfer hold, transferFrom carries the info finding. Two extra rows record the conservation invariant and the launch-flow constructor check. Nothing in the assigned area was left unreached.","treeHash":null,"usage":{"cachedInputTokens":530632,"inputTokens":258,"model":"claude-fable-5-1","outputTokens":10157,"runtime":"claude","turns":18,"wallClockMs":165686}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"00920b27421b9a80","findings":[{"citation":"resolved","description":"Asymmetry lens (set <-> pull pair): approve() writes allowance[msg.sender][spender] = value as an absolute overwrite, and transferFrom() is the only reader that decrements it. There is no increaseAllowance/decreaseAllowance or permit path, so an owner who wants to lower a non-zero allowance has no atomic way to do so: a spender who observes the pending approve(lower) in the mempool can spend the old allowance first and then the new one. This is the well-known ERC-20 approve race inherent to the standard (OpenZeppelin v5 ERC20 behaves identically) and not a permission bypass: the spender never moves more than the sum of what the owner explicitly approved, and the owner can mitigate by approving 0 first. It is recorded as information for the author, not as a defect requiring a change to the agreed design. No privileged role exists in this contract, so no Access Control or Trust Gap finding arises; this is the single observation from the three assigned guides.","line":36,"path":"src/MADToken.sol","reproduction":"State: factory (holds 1e27) calls approve(alice, 100). Then factory submits approve(alice, 50) intending to lower the allowance. Alice front-runs it with transferFrom(factory, alice, 100) (allowance 100 -> 0, alice +100). The factory's approve(alice, 50) then lands (allowance = 50). Alice calls transferFrom(factory, alice, 50). Result: alice holds 150 while the owner intended a maximum of 100 outstanding. Verified with a Foundry test in test/scratch (test_approveRaceIsPossible, passes, i.e. the sequence succeeds on this code). Expected under the owner's intent: at most 100 spendable; actual: 150. Standard-inherent; mitigation is to approve(spender, 0) before setting a new non-zero value, or to add increaseAllowance/decreaseAllowance if the author wants it.","severity":"info","snippet":"    function approve(address spender, uint256 value) external returns (bool) {\n        if (spender == address(0)) revert InvalidSpender(spender);\n        allowance[msg.sender][spender] = value;","title":"approve() sets an absolute allowance with no increase/decrease path (standard ERC-20 approve race)"}],"hash":"9ed4a7de8571431b8963848603ceb5b178ff46316f7d744186a2dc6175c89ad5","nodeId":"c5b7b027-3a5d-4775-8972-ea570f78cd16","outcome":"completed","summary":"The review is complete and the findings file is written. The working tree holds only the findings file and build artifacts, with no source changes.\n\n**Outcome: no defects found in the assigned area.** The token at `src/MADToken.sol` has no owner, roles, modifiers, initializer, proxy, fee, pause or blacklist. Its four state-changing entry points each act only on the caller's own balance or on an allowance the owner granted, and no path can increase supply.\n\n**What I checked, against the Access Control, Trust Gap and Asymmetry guides:**\n- Permission map: all writers to balanceOf and allowance were traced. Every write is keyed by msg.sender or guarded by a balance or allowance check that precedes the unchecked arithmetic.\n- Asymmetry pairs: constructor mint versus burn mirror both totalSupply and balanceOf. transfer and transferFrom share one private path. Self-transfer keeps the balance intact because the sender write happens before the receiver read.\n- Trust Gap seams: with no privileged actor, no settable parameter and no price formula, no seam exists.\n- Static-analysis lead: the large numeric literal on line 11 equals exactly 1e27, the manifest supply. Not a defect.\n\n**Verification run:** the existing test suite passed, and a scratch Foundry file with seven tests passed, including a 2000-run fuzz of the sum-of-balances equals totalSupply invariant across transfer, infinite-allowance transferFrom and burn. The scratch directory was removed afterwards.\n\n**Reported:** one informational note on the standard ERC-20 approve race, with a concrete call sequence where a spender realises 150 tokens against an owner who intended at most 100. It is inherent to the standard, not a permission bypass, and needs no design change.\n\n**Coverage record:** all four listed entry points plus four invariant rows are answered in `.imd-findings.json`. approve is marked as carrying the info note. burn, transfer and transferFrom hold.","treeHash":null,"usage":{"cachedInputTokens":577162,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":12108,"runtime":"claude","turns":25,"wallClockMs":180264}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"789312fc56d3f446","findings":[],"hash":"b66a0da6460d9042fe3127eec0dbb49397dfc602477f6fb11cf57796ec6ad4c4","nodeId":"53e9b2a3-ff47-42ea-b8ca-8b26dcfd762a","outcome":"failed","summary":"Failed to authenticate: OAuth session expired and could not be refreshed","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"claude","turns":1,"wallClockMs":2642}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"72f49cf84b9ab056","findings":[],"hash":"bb363a2c41c0d9860a9aaeb90b0ab79e3e81bb723d673d70e55bbf4a3095fcbd","nodeId":"d40f0b32-4d8a-4d7b-b442-a404553bb60d","outcome":"completed","summary":"The review is complete and the findings file is written. Nothing further is needed from the tree or the user.\n\n**Result: no defects found in the Math Precision, Boundary and Numerical Gap areas.** The findings file at the repository root holds an empty findings list and seven coverage rows, one for each of the four entry points plus three invariant and boundary rows.\n\n**What I checked and how**\n\n- **Arithmetic surface.** The token has no division, no multiplication at runtime, no scaling and no casts. The only arithmetic is the compile-time supply constant and the subtractions and additions inside the unchecked blocks of transfer, transferFrom and burn. Each unchecked subtraction is preceded by the matching `<` check, and the unchecked addition cannot overflow because the sum of balances never exceeds the initial supply.\n- **Boundaries.** The contract makes no external calls, has no payable function, decodes no bytes, and has no receive or fallback. I disassembled the compiled runtime and found no CALL, DELEGATECALL, CALLCODE, CREATE or SELFDESTRUCT opcodes.\n- **Edges exercised in a scratch Foundry suite** (17 tests, 2000 fuzz runs, all passed, then deleted): zero value, exact balance, balance plus one, max uint, self-transfer of the full balance, allowance of max minus one, allowance spent exactly to zero, allowance sufficient but balance short, infinite allowance with no balance, burn to zero total supply, burn of max, and a randomized 24-step sequence of all four entry points keeping the sum of balances equal to total supply.\n- **Static analysis lead.** The aderyn large-literal note is stylistic. The literal evaluates to exactly 1e27, which matches the manifest supply.\n\n**One behaviour noted but not reported as a defect.** A zero-value transferFrom from any account succeeds without allowance and emits a Transfer event. This matches OpenZeppelin ERC-20 and moves nothing, so it is not a defect under the acceptance criteria.\n\n**Coverage.** All four entry points are marked `holds`. Nothing in my area was left unreached.","treeHash":null,"usage":{"cachedInputTokens":843360,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":11311,"runtime":"claude","turns":16,"wallClockMs":155527}},{"artifacts":[],"attempt":1,"bundleHash":"e92642455fd8e9417abd6ea027c7973e60242b814b6b96b5f14c563f9c7e4d34","device":"a6bc9bc7471a6711","findings":[],"hash":"db8bcf071b886e64b787efd696308385a6148d93df49dbe3076a480e175d9301","nodeId":"2691d988-17de-4e91-90d7-7ac6b540c978","outcome":"completed","summary":"Created [launch.json](/home/imd539/.identitymd/work/250fe599-ee1d-420b-9044-bcd6f63ea0b0/2691d988-17de-4e91-90d7-7ac6b540c978/launch.json) matching `MADToken`, its supply, empty constructor arguments, and the exact pool and economics values.\n\n`forge build` succeeded. All 12 tests passed with the supplied fuzz seed. Only `launch.json` is changed for submission.","treeHash":"c540972adf6c086a8b5cdf7b73db69c43c994d03","usage":{"cachedInputTokens":134784,"inputTokens":17408,"model":"gpt-6-astra","outputTokens":2094,"runtime":"codex","turns":3,"wallClockMs":69334}},{"artifacts":[],"attempt":1,"bundleHash":"72e64f0865fe2762212d8db68201cd115b8ec986214c5a9cf93c3f161056a05a","device":"e49531c471ac7158","findings":[],"hash":"f1368cc61d8db36ae160217f1489d2224f1dfeb3034fe29f35fb8984b6da112c","nodeId":"daaaea03-3992-42e1-ac41-e18989e9cf02","outcome":"completed","summary":"I added `test/MADTokenInvariant.t.sol` and committed it. With the verifier's fuzz seed, `forge test` passes all 30 tests, the 12 already in `test/MADToken.t.sol` included.\n\n**What the new file covers:**\n- **Invariant tests:** four actors make random sequences of transfer, approve, transferFrom and burn calls, plus calls that are meant to revert (spending more than the balance or more than the allowance). After every sequence the tests check three things:\n  - all balances add up to the total supply;\n  - the total supply plus everything burned still equals exactly 1,000,000,000 MAD, so the supply can never grow;\n  - every allowance matches what the test expects it to be.\n\n  This ran 128 runs of 64 calls each (8,192 calls), with every function exercised.\n- **Edge cases:**\n  - moving the full supply works, and the full supply plus 1 wei or the maximum value reverts;\n  - zero-amount transfers work, even from an empty account;\n  - transferring to yourself leaves your balance unchanged;\n  - a transferFrom that reverts (to the zero address, or from an account that is short) does not use up the allowance;\n  - spending the exact allowance leaves it at zero, and the next spend reverts;\n  - approve overwrites the old amount and emits its event;\n  - burning zero and burning the full supply both work;\n  - the deployment emits the mint event;\n  - there is no `burnFrom`, and the token refuses ETH.\n- **Fuzz tests:** transferFrom keeps balances and allowances correct for any approved amount, and burning more than you hold always reverts.\n\n**Findings:** none. I found no defect in `src/MADToken.sol`, so I did not write `.imd-findings.json`. The token has no owner, no mint function, no fees and no pause, and a holder can only burn their own tokens.\n\nGit had no identity set on this machine, so the commit failed at first. I made it with your name and email passed on that one command and did not change the git configuration.","treeHash":"91bad75024f9b4352653af31c165bfa4b385d41a","usage":{"cachedInputTokens":267351,"inputTokens":14,"model":"claude-opus-5-5","outputTokens":6189,"runtime":"claude","turns":7,"wallClockMs":67138}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"3c1226d190150ef0","findings":[{"citation":"resolved","description":"Sentinel-bypass check from the Execution Trace guide. _transfer rejects to == address(0), but neither transferFrom nor _transfer rejects from == address(0). With value 0, the allowance check passes because allowance[0][caller] is 0 and 0 < 0 is false. The balance check passes because balanceOf[0] is 0 and 0 < 0 is false. The function then emits Transfer(address(0), to, 0), the same event shape the constructor uses for the mint. No balances or totalSupply change, so funds are safe. Indexers and explorers that treat Transfer events from the zero address as mints will show fake zero-value mints to any address the caller picks, even though the README says the supply is minted once. OpenZeppelin v5 rejects this case with ERC20InvalidSender(address(0)). Fix: in _transfer, revert when from == address(0).","line":43,"path":"src/MADToken.sol","reproduction":"State: token freshly deployed. Any EOA E calls token.transferFrom(address(0), 0x000000000000000000000000000000000000dEaD, 0). Expected: revert, since the zero address has no holder and can grant no allowance. Actual: the call returns true and emits Transfer(from=0x0, to=0x…dEaD, value=0). totalSupply and every balance stay unchanged. E can repeat this for any recipient, so off-chain consumers record more than one mint event.","severity":"info","snippet":"    function transferFrom(address from, address to, uint256 value) external returns (bool) {\n        uint256 current = allowance[from][msg.sender];\n        if (current != type(uint256).max) {\n            if (current < value) revert InsufficientAllowance(msg.sender, current, value);","title":"transferFrom accepts from == address(0), letting anyone emit a zero-value Transfer(address(0), to, 0) that looks like a mint"}],"hash":"f9b35dfc26089124f57a546350802779a677179546d378f14e88b7ded06235fd","nodeId":"1200da77-bba0-459f-9f9a-72e99dd21968","outcome":"completed","summary":"I found no medium, high or critical defects in `MADToken`, and one info-level finding. The findings and coverage rows are in `.imd-findings.json`; I changed no tracked files.\n\n**Finding 1 (info), `src/MADToken.sol:43`, `transferFrom`:** anyone can call `transferFrom(address(0), anyAddress, 0)` and it succeeds.\n- **Why it gets through:** the allowance check passes because `0 < 0` is false, and the balance check passes the same way. Only `to == address(0)` is rejected, never `from`.\n- **Effect:** the call emits `Transfer(address(0), anyAddress, 0)`, which explorers and indexers display as a mint. No balance or the total supply changes.\n- **Fix:** reject `from == address(0)` in `_transfer`, as OpenZeppelin v5 does.\n\n**Coverage:**\n- `approve`: holds. It rejects a zero spender, sets the allowance and emits the event, and makes no external calls.\n- `burn`: holds. It checks the balance before the unchecked subtraction, so neither the balance nor the total supply can underflow, and both drop by the same amount.\n- `transfer`: holds. Sending to yourself is handled correctly, and the unchecked addition can't overflow because all balances together equal the total supply of at most 1e27. The exact amount always moves, so the factory → distributor → pool-manager launch flows arrive whole.\n- `transferFrom`: finding 1. The rest of its allowance handling is correct: an unlimited allowance is not reduced, and a revert undoes the partial write.\n- **Constructor and supply rule:** the constructor mints exactly 1,000,000,000 × 10^18 to the deployer and creates no roles. Nothing can raise the supply afterwards: there is no mint, owner, `DELEGATECALL` or `SELFDESTRUCT`.\n\n**Guides applied:**\n- **Execution Trace:** I checked parameter mismatches, special values such as the zero address and the unlimited allowance, half-finished state updates, and leftover allowances.\n- **Periphery:** the contract has no libraries and makes no external calls. Its only outside contacts are the pool manager's transfer-then-settle and the distributor's claims, both of which depend on exact transfers.\n- **First Principles:** I checked that balances and the total supply stay in step, and the zero, maximum and self-transfer edge cases.\n\n**Checks run:** the existing 12 Foundry tests pass. The aderyn \"large numeric literal\" note is the intended supply constant, not a defect. I wrote no proof test because nothing reached high or critical. I could not run the protected launch tests: they import `floor/OwnerChecks.sol` and `src/LaunchLiquidity.sol`, which are not in this tree.\n\nSeveral dotfiles I did not create (`.bashrc`, `.gitconfig`, `.mcp.json` and others) now show as untracked in the repository root. I left them alone, but they should be kept out of the submission.","treeHash":null,"usage":{"cachedInputTokens":313821,"inputTokens":14,"model":"claude-opus-5-5","outputTokens":5398,"runtime":"claude","turns":7,"wallClockMs":62138}}],"verification":[{"checks":[{"durationMs":941,"exitCode":0,"name":"build","output":"Compiling 21 files with Solc 0.8.26\nSolc 0.8.26 finished in 843.60ms\nCompiler run successful!\n","passed":true},{"durationMs":114,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 12 tests for test/MADToken.t.sol:MADTokenTest\n[PASS] testFuzz_transferConservesSupply(address,uint256) (runs: 256, μ: 85919, ~: 87343)\n[PASS] test_approveAndTransferFrom() (gas: 133694)\n[PASS] test_approveZeroSpenderReverts() (gas: 30311)\n[PASS] test_burnReducesSupply() (gas: 46662)\n[PASS] test_burnRevertsOverBalance() (gas: 34775)\n[PASS] test_infiniteAllowanceNotDecreased() (gas: 119184)\n[PASS] test_metadataAndSupply() (gas: 40911)\n[PASS] test_noMintEntryPoint() (gas: 31983)\n[PASS] test_transfer() (gas: 76549)\n[PASS] test_transferFromRevertsOverAllowance() (gas: 86847)\n[PASS] test_transferRevertsOnInsufficientBalance() (gas: 37432)\n[PASS] test_transferToZeroReverts() (gas: 32894)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 3.71ms (5.64ms CPU time)\n\nRan 1 test suite in 4.39ms (3.71ms CPU time): 12 tests passed, 0 failed, 0 skipped (12 total tests)\n","passed":true},{"durationMs":62,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"MADToken.approve(address,uint256)\",\"MADToken.burn(uint256)\",\"MADToken.transfer(address,uint256)\",\"MADToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":2,\"README.md\":27,\"foundry.toml\":9,\"remappings.txt\":1,\"src/MADToken.sol\":76,\"test/MADToken.t.sol\":101},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":475,"exitCode":0,"name":"slither","output":"slither: no results at low impact or above","passed":true},{"durationMs":172,"exitCode":0,"name":"aderyn","output":"[low] large-numeric-literal at src/MADToken.sol:11: Large Numeric Literal","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"1fddc306a3f6cd9d92c1edde175dbbd8da1c22158a135bb67f587b0322a54cd8","verifiedTreeHash":"218306b9a7972ebd987ceddaf295e49c65409d24","verifierVersion":"0.1.0+ead9012e"},{"checks":[{"durationMs":1028,"exitCode":0,"name":"build","output":"Compiling 21 files with Solc 0.8.26\nSolc 0.8.26 finished in 925.71ms\nCompiler run successful!\n","passed":true},{"durationMs":156,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 12 tests for test/MADToken.t.sol:MADTokenTest\n[PASS] testFuzz_transferConservesSupply(address,uint256) (runs: 256, μ: 85919, ~: 87343)\n[PASS] test_approveAndTransferFrom() (gas: 133694)\n[PASS] test_approveZeroSpenderReverts() (gas: 30311)\n[PASS] test_burnReducesSupply() (gas: 46662)\n[PASS] test_burnRevertsOverBalance() (gas: 34775)\n[PASS] test_infiniteAllowanceNotDecreased() (gas: 119184)\n[PASS] test_metadataAndSupply() (gas: 40911)\n[PASS] test_noMintEntryPoint() (gas: 31983)\n[PASS] test_transfer() (gas: 76549)\n[PASS] test_transferFromRevertsOverAllowance() (gas: 86847)\n[PASS] test_transferRevertsOnInsufficientBalance() (gas: 37432)\n[PASS] test_transferToZeroReverts() (gas: 32894)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 6.86ms (9.49ms CPU time)\n\nRan 1 test suite in 8.07ms (6.86ms CPU time): 12 tests passed, 0 failed, 0 skipped (12 total tests)\n","passed":true},{"durationMs":66,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"MADToken.approve(address,uint256)\",\"MADToken.burn(uint256)\",\"MADToken.transfer(address,uint256)\",\"MADToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":2,\"README.md\":27,\"foundry.toml\":9,\"launch.json\":20,\"remappings.txt\":1,\"src/MADToken.sol\":76,\"test/MADToken.t.sol\":101},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"db8bcf071b886e64b787efd696308385a6148d93df49dbe3076a480e175d9301","verifiedTreeHash":"c540972adf6c086a8b5cdf7b73db69c43c994d03","verifierVersion":"0.1.0+ead9012e"},{"checks":[{"durationMs":1896,"exitCode":0,"name":"build","output":"Compiling 22 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.78s\nCompiler run successful!\n","passed":true},{"durationMs":2409,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 12 tests for test/MADToken.t.sol:MADTokenTest\n[PASS] testFuzz_transferConservesSupply(address,uint256) (runs: 256, μ: 85783, ~: 87337)\n[PASS] test_approveAndTransferFrom() (gas: 133694)\n[PASS] test_approveZeroSpenderReverts() (gas: 30311)\n[PASS] test_burnReducesSupply() (gas: 46662)\n[PASS] test_burnRevertsOverBalance() (gas: 34775)\n[PASS] test_infiniteAllowanceNotDecreased() (gas: 119184)\n[PASS] test_metadataAndSupply() (gas: 40911)\n[PASS] test_noMintEntryPoint() (gas: 31983)\n[PASS] test_transfer() (gas: 76549)\n[PASS] test_transferFromRevertsOverAllowance() (gas: 86847)\n[PASS] test_transferRevertsOnInsufficientBalance() (gas: 37432)\n[PASS] test_transferToZeroReverts() (gas: 32894)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 27.67ms (17.90ms CPU time)\n\nRan 17 tests for test/MADTokenInvariant.t.sol:MADTokenEdgeTest\n[PASS] testFuzz_burnRevertsAboveBalance(uint256) (runs: 1000, μ: 35898, ~: 36227)\n[PASS] testFuzz_transferFromAccounting(uint256,uint256) (runs: 1000, μ: 141004, ~: 142838)\n[PASS] test_approveOverwritesAndEmits() (gas: 94261)\n[PASS] test_burnFromOthersNotPossible() (gas: 32279)\n[PASS] test_burnFullSupplyAndZero() (gas: 92726)\n[PASS] test_constructorEmitsMint() (gas: 5470)\n[PASS] test_initialSupplyConstant() (gas: 5621)\n[PASS] test_rejectsEther() (gas: 38411)\n[PASS] test_selfTransferKeepsBalance() (gas: 40078)\n[PASS] test_transferFromAllowanceOkButBalanceShort() (gas: 94770)\n[PASS] test_transferFromExactAllowanceZeroes() (gas: 156829)\n[PASS] test_transferFromToZeroReverts() (gas: 92292)\n[PASS] test_transferFromWithoutAllowanceReverts() (gas: 39884)\n[PASS] test_transferFullSupply() (gas: 71765)\n[PASS] test_transferMaxUintReverts() (gas: 37865)\n[PASS] test_transferOneOverFullSupplyReverts() (gas: 37815)\n[PASS] test_zeroTransferFromEmptyAccount() (gas: 46611)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 27.79ms (57.22ms CPU time)\n\nRan 1 test for test/MADTokenInvariant.t.sol:MADTokenInvariantTest\n[PASS]\nMADTokenInvariantTest invariants:\n[PASS] invariant_allowancesMatchGhost\n[PASS] invariant_sumOfBalancesEqualsSupply\n[PASS] invariant_supplyOnlyShrinksByBurns\n MADTokenInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭------------+---------------------------+-------+---------+----------╮\n| Contract   | Selector                  | Calls | Reverts | Discards |\n+=====================================================================+\n| MADHandler | approve                   | 1355  | 0       | 0        |\n|------------+---------------------------+-------+---------+----------|\n| MADHandler | burn                      | 1360  | 0       | 0        |\n|------------+---------------------------+-------+---------+----------|\n| MADHandler | transfer                  | 1403  | 0       | 0        |\n|------------+---------------------------+-------+---------+----------|\n| MADHandler | transferFrom              | 1349  | 0       | 0        |\n|------------+---------------------------+-------+---------+----------|\n| MADHandler | transferFromOverAllowance | 1365  | 0       | 0        |\n|------------+---------------------------+-------+---------+----------|\n| MADHandler | transferTooMuch           | 1360  | 0       | 0        |\n╰------------+---------------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.27s (2.26s CPU time)\n\nRan 3 test suites in 2.27s (2.32s CPU time): 30 tests passed, 0 failed, 0 skipped (30 total tests)\n","passed":true},{"durationMs":64,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"MADToken.approve(address,uint256)\",\"MADToken.burn(uint256)\",\"MADToken.transfer(address,uint256)\",\"MADToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":2,\"README.md\":27,\"foundry.toml\":9,\"remappings.txt\":1,\"src/MADToken.sol\":76,\"test/MADToken.t.sol\":101,\"test/MADTokenInvariant.t.sol\":284},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"f1368cc61d8db36ae160217f1489d2224f1dfeb3034fe29f35fb8984b6da112c","verifiedTreeHash":"91bad75024f9b4352653af31c165bfa4b385d41a","verifierVersion":"0.1.0+ead9012e"}]}