{"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":"f1be67f1-de5b-4c15-a985-b60434b34773","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"d7cf6e308c7b082584dc02110fcc9e6192f688589fdef7957027c0b8ab273e71","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":"38ba9577a36206260dcb1c4489271aca8c5490f3184103100e8436ddf526b4ca","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":"7005b0419d6f946051b20f1974cf757f22299324312910fced6459d2818a2ecb","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":"fa6c8da8423340398ef76bcec9e633fca65f68025ee57e1621204c0daee1686d","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":"64e7292c3d3afca308db6b0c2ce0d3031138159f0912d3cd800a51700c1fb7c9","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":"599d4c7f5a82992aa13c34e05d332279461932881fe9f8acd962b9731552617f","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":"61a7a4c66fed7640aca3e547fadbb8c7d13f7016a50284588fbd0759c0114545","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"f8d5fdaf39e7f329ab58b6909974abad473e6220584c8866c26b840f60c15ca9","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: State Encryption (STATE).\nToken name: State Encryption\nToken symbol: STATE\nToken supply: 1,000,000 with 18 decimals, all minted once to the deployer in the constructor.","parentJobId":null,"planHash":"0b89d90f29851f4fe8db4480fcf1b1c2915dc1f75899db24ba8391a5ac38be60","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"f1be67f1-de5b-4c15-a985-b60434b34773","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1969-state-encryption"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51064","feedbackHash":"52dbe0585ef411589f4570745d25cba1c4078e355574b8fa15b98818db522b96","nodeKey":"audit_economics","submissionHash":"d7cf6e308c7b082584dc02110fcc9e6192f688589fdef7957027c0b8ab273e71","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51071","feedbackHash":"d6548e7a362f0ada02a165b592491fd38f48d28ba4780ed63e1595402e636a7b","nodeKey":"audit_flow","submissionHash":"38ba9577a36206260dcb1c4489271aca8c5490f3184103100e8436ddf526b4ca","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52266","feedbackHash":"56362fbb33d85bc7e499d81d44ceeb0be47b233f1f7c09a523952535895aaa6a","nodeKey":"audit_judge","submissionHash":"7005b0419d6f946051b20f1974cf757f22299324312910fced6459d2818a2ecb","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51488","feedbackHash":"e7f999f755aa5508a18fb407b4a5d9535fc1f67f4f9fab6b86e8beec5e0c7290","nodeKey":"audit_math","submissionHash":"fa6c8da8423340398ef76bcec9e633fca65f68025ee57e1621204c0daee1686d","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51035","feedbackHash":"bd19203f751097650de17a760163b06c965ec6c75d92892a9912d98b3eaa4378","nodeKey":"audit_permissions","submissionHash":"64e7292c3d3afca308db6b0c2ce0d3031138159f0912d3cd800a51700c1fb7c9","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51429","feedbackHash":"e460df992a6160d08f346d724a6155206df5bbb850fbb37bda07b60979381f1a","nodeKey":"build_contract_project","submissionHash":"599d4c7f5a82992aa13c34e05d332279461932881fe9f8acd962b9731552617f","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52604","feedbackHash":"1931497b9ec5ac1ae3881b6e3d7255c266ff9c939cd4163c25005780cdacdc36","nodeKey":"manifest","submissionHash":"61a7a4c66fed7640aca3e547fadbb8c7d13f7016a50284588fbd0759c0114545","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52608","feedbackHash":"12f3bf311954a3292b27ab4e180bc48e5152cfcca966fbc5fa1c22077904c8c9","nodeKey":"write_foundry_tests","submissionHash":"f8d5fdaf39e7f329ab58b6909974abad473e6220584c8866c26b840f60c15ca9","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"b0ddcfc5d6c1d709603b773b5c48f1c10708272e06f577856eea86a4b30b3b6e","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"39da99ded7f125c8","findings":[{"citation":"resolved","description":"Execution trace / sentinel bypass. `_transfer` (src/StateToken.sol:152-162) validates `to` but never `from`, and `transferFrom` (src/StateToken.sol:142-146) passes a caller-chosen `from` straight into `_spendAllowance` and `_transfer`. For `amount == 0` both guards are vacuous: `_allowances[address(0)][caller]` is 0 and `0 < 0` is false, `_balances[address(0)]` is 0 and `0 < 0` is false. So an arbitrary caller with no balance and no allowance makes `transferFrom(address(0), to, 0)` succeed, which writes `_allowances[address(0)][caller] = 0`, emits `Approval(address(0), caller, 0)` and emits `Transfer(address(0), to, 0)`. The README and NatSpec state that the constructor's `Transfer(address(0), msg.sender, supply)` is 'the only mint that ever happens'; after deployment any account can append further zero-value mint-shaped Transfer events at will, and the Approval event shows the zero address granting allowances. No balance or supply changes (the amount is forced to 0 by the balance check), so there is no loss of funds; the harm is to off-chain consumers that classify `Transfer` events with `from == address(0)` as mints (explorers' holder lists, indexers that count mint events, supply reconstruction from logs) and to the documented 'only one mint' guarantee. OpenZeppelin v5's `_transfer` reverts with `ERC20InvalidSender(address(0))` for exactly this reason. Minimal fix that keeps the design: add `if (from == address(0)) revert ZeroAddress();` beside the existing `to` check in `_transfer` (the constructor does not go through `_transfer`, so the launch mint is unaffected and the protected launch-flow tests still pass). Non-zero amounts from address(0) are correctly refused: `transferFrom(address(0), x, 1)` reverts `InsufficientAllowance(caller, 0, 1)`.","line":153,"path":"src/StateToken.sol","reproduction":"State: freshly deployed StateToken (constructor mints 1,000,000e18 to the deployer). Caller: any address, e.g. 0xBAD, holding 0 STATE and no allowance from anyone. Call: `vm.prank(0xBAD); token.transferFrom(address(0), 0xCAFE, 0)`. Expected: revert (nobody can act on behalf of the zero address; the only Transfer from address(0) should be the constructor mint). Actual: returns true and emits `Approval(address(0), 0xBAD, 0)` then `Transfer(address(0), 0xCAFE, 0)`; balanceOf(0xCAFE) stays 0, totalSupply stays 1e24. Verified with a Foundry test using vm.expectEmit on both events (test passed on the current code, i.e. the events are emitted). Control: `vm.prank(0xBAD); token.transferFrom(address(0), 0xCAFE, 1)` reverts with InsufficientAllowance(0xBAD, 0, 1), so no value moves.","severity":"low","snippet":"        if (to == address(0)) revert ZeroAddress();\n        uint256 fromBalance = _balances[from];\n        if (fromBalance < amount) revert InsufficientBalance(from, fromBalance, amount);","title":"transferFrom accepts address(0) as `from`: anyone can emit a mint-shaped Transfer(address(0), to, 0) and Approval(address(0), caller, 0) after deployment"}],"hash":"38ba9577a36206260dcb1c4489271aca8c5490f3184103100e8436ddf526b4ca","nodeId":"55eef7b9-3c98-4f12-b8a4-8fbb515e5829","outcome":"completed","summary":"Review complete. The findings file is written, validated, and the snippet matches the tree. The working tree has no other changes.\n\n**Result: one low finding, no critical, high or medium.** The token is a self-contained fixed-supply ERC-20 with no owner, mint, burn, pause, fee, external call or fallback, and all five entry points were traced in full against the Execution Trace, Periphery and First Principles guides.\n\n**Finding 1 (low):** `transferFrom` never validates `from`, so for a zero amount both the allowance and balance guards are vacuous. Any caller with no balance and no allowance can run `transferFrom(address(0), to, 0)` and it succeeds, emitting a mint-shaped `Transfer(address(0), to, 0)` plus `Approval(address(0), caller, 0)`. No funds or supply move, but the documented \"only one mint ever\" guarantee breaks for log consumers that treat transfers from the zero address as mints. Anchored at `src/StateToken.sol:153`. Minimal fix is a `from != address(0)` check in `_transfer`, which leaves the constructor mint and the launch flows untouched. I confirmed the behaviour with a scratch Foundry test using `expectEmit`, then removed the scratch file.\n\n**Coverage recorded** for all five listed entry points plus the fixed-supply invariant, the launch flows and the periphery. `approve`, `increaseAllowance`, `decreaseAllowance` and `transfer` hold. `transferFrom` carries the finding. The project's 33 tests pass, and the protected custom-token floor expectations are met by construction since nothing is deducted on any transfer.\n\nNothing in my area was left unreached.","treeHash":null,"usage":{"cachedInputTokens":411317,"inputTokens":194,"model":"claude-fable-5-1","outputTokens":11834,"runtime":"claude","turns":13,"wallClockMs":180377}},{"artifacts":[],"attempt":1,"bundleHash":"c96b5ceeef970d7d46558ff861f6fe717cb95a5b60ed48d6301de44290bd9f7c","device":"3987a51ff810f3b9","findings":[],"hash":"599d4c7f5a82992aa13c34e05d332279461932881fe9f8acd962b9731552617f","nodeId":"f4d8f13b-9a8a-49bf-9158-28f524d482c2","outcome":"completed","summary":"The State Encryption (STATE) token is implemented, tested, documented and committed on `main` as commit b8e90f5.\n\n**What was built**\n\n- `src/StateToken.sol`: a self-contained ERC-20 named `StateToken`. The constructor takes no arguments and mints 1,000,000 STATE with 18 decimals once to the deployer, which is the factory at launch. It has no owner, mint, burn, pause, blocklist, fee or upgrade path, so the supply cannot grow and no privileged party exists. Allowances of the maximum value are treated as unlimited, and zero-address transfers and approvals revert with custom errors.\n- `script/DeployStateToken.s.sol`: a deploy script whose `deploy()` function is called directly by the tests. It reads no environment variables.\n- `test/StateToken.t.sol`: 33 tests covering metadata, constructor minting, transfer and transferFrom success and failure paths, allowance adjustment, exact-amount launch flows, rejection of every mint, pause, freeze and seize selector from the protected floor test, a runtime bytecode scan for forbidden opcodes, and four fuzz tests for supply conservation and exactness.\n- `foundry.toml` pins solc 0.8.26, sets `bytecode_hash = \"none\"`, and leaves ffi and filesystem access off. forge-std v1.9.7 is vendored under `lib/` as plain files with no submodule.\n- `README.md` documents the token parameters, assumptions, manifest values for the launch, the empty \"After launch\" section, operational responsibilities and security notes.\n\n**Verification**\n\n| Check | Result |\n|---|---|\n| forge build | exit 0, lint warnings only |\n| forge test with the verifier's fuzz seed, 256 and 2000 runs | 33 passed, 0 failed |\n| forge test with an empty environment | 33 passed |\n| forge fmt --check | clean |\n\nSlither and Mythril were not available here and did not run. The deployment itself, explorer verification and supply distribution belong to the IdentityMD deployer and the manifest step, which this task did not perform.","treeHash":"db271a1ce91b6e2641e88824695e629289630460","usage":{"cachedInputTokens":538996,"inputTokens":322,"model":"claude-fable-5-1","outputTokens":19508,"runtime":"claude","turns":21,"wallClockMs":260112}},{"artifacts":[],"attempt":1,"bundleHash":"35a45bcd34e3d04a50a2b07829d9ce6579cc054322fd3e18be8d14372553aff8","device":"9e17b97b8ac7c97a","findings":[],"hash":"61a7a4c66fed7640aca3e547fadbb8c7d13f7016a50284588fbd0759c0114545","nodeId":"b949090e-58cb-4e54-a7ba-45ec9c98f85e","outcome":"completed","summary":"I wrote launch.json for the State Encryption launch, and it is the only file changed.\n\n**What the manifest says**\n\n- Token contract StateToken, name \"State Encryption\", symbol \"STATE\", 18 decimals, empty constructorArgs. The accepted constructor takes no parameters and mints the fixed supply to msg.sender.\n- totalSupply is \"1000000000000000000000000\", which equals the contract's TOTAL_SUPPLY constant of 1,000,000 × 10^18.\n- contracts is an empty list. The accepted tree has no application contracts, only the token and a deploy script.\n- pool and economics are copied exactly from the launch reference: IMD pair address, fee 12500, tick spacing 60, one-to-one initialPrice, and the requester's economics object verbatim.\n- notes describe only StateToken's actual behaviour and run to about 1,300 characters.\n\n**Checks run**\n\n- A Python shape check confirmed only schema keys are present, strings for uint256 and address fields, numbers for fee, tickSpacing and decimals, lowercase addresses, and the supply matching the constructor.\n- `forge build` compiled cleanly.\n- `forge test` with the verifier's fuzz seed passed all 33 existing tests.\n- `git status` shows launch.json as the only new or changed file.","treeHash":"785c21e382803d358278afbcd5014fbfa5c004d7","usage":{"cachedInputTokens":173852,"inputTokens":130,"model":"claude-fable-5-1","outputTokens":3103,"runtime":"claude","turns":9,"wallClockMs":62557}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"8472a2af9a9cf974","findings":[],"hash":"64e7292c3d3afca308db6b0c2ce0d3031138159f0912d3cd800a51700c1fb7c9","nodeId":"7c2a06bc-78a4-47b3-a8fc-66c7df8d4b27","outcome":"completed","summary":"Review complete. No defects found in the assigned area, and the findings file is written.\n\n**Result:** `.imd-findings.json` holds an empty findings list and nine coverage rows, one for each of the five state-changing entry points plus four invariants. Every entry point is marked `holds`.\n\n**What I checked** in `src/StateToken.sol` against the Access Control, Trust Gap and Asymmetry guides, after the Senior Auditor's Mindset:\n\n- **Access control.** The constructor assigns no role and takes no argument. There is no owner, modifier, initializer, proxy, receive or fallback. Every state-changing function writes only storage keyed by msg.sender, or spends an allowance msg.sender was explicitly granted. No slot is written by two functions with different guards.\n- **Asymmetry.** The approve/transferFrom, increase/decrease allowance and transfer/transferFrom pairs were diffed side by side. Both unchecked blocks are guarded by the comparison directly above them. The unlimited-allowance sentinel behaves identically whether reached by approve or by increaseAllowance. Zero-spender handling is symmetric across all three allowance functions. Self-transfer and self-transferFrom preserve balances.\n- **Trust gap.** The token has no fee, price, recipient setter or admin parameter, so there is no seam between authorization and value flow.\n- **Launch floor.** Single transfer path with no caller-dependent branch, so factory, distributor and pool flows move exact amounts. Supply is minted once to msg.sender, which is the factory. The existing 33-test suite passes, and the runtime has no DELEGATECALL, CALLCODE or SELFDESTRUCT.\n\n**Verification.** I ran the project suite and a nine-case scratch probe covering the edge paths above, including a 256-run conservation fuzz. All passed. The scratch tests were removed afterwards, and the only file left in the tree beyond the original is `.imd-findings.json`. Both static-analysis lines (large numeric literal, unused public functions) are noise with no reachable effect.\n\n**Not a defect, noted for the author:** tokens sent to the token's own address are unrecoverable, which is standard ERC-20 behaviour and already documented in the README. The approve overwrite race is likewise inherent and documented.","treeHash":null,"usage":{"cachedInputTokens":539164,"inputTokens":258,"model":"claude-fable-5-1","outputTokens":11544,"runtime":"claude","turns":23,"wallClockMs":192005}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"644eb561f6d70a85","findings":[{"citation":"resolved","description":"Merged from write_foundry_tests (info), audit_flow (low), audit_economics (low) and audit_math (info): same root cause, one finding. `_transfer` (src/StateToken.sol:152-162) rejects a zero recipient but never a zero sender, and `transferFrom` (line 142-146) forwards a caller-chosen `from` into `_spendAllowance` and `_transfer`. With amount == 0 both guards are vacuous: `_allowances[address(0)][caller]` is 0 and `0 < 0` is false; `_balances[address(0)]` is 0 and `0 < 0` is false. So an unprivileged caller with no balance and no allowance makes `transferFrom(address(0), to, 0)` succeed, which writes `_allowances[address(0)][caller] = 0`, emits `Approval(address(0), caller, 0)` and emits `Transfer(address(0), to, 0)`. The constructor's `Transfer(address(0), msg.sender, TOTAL_SUPPLY)` is documented (NatSpec line 55, README) as the only mint that ever happens; after deployment anyone can append further zero-value mint-shaped logs for any recipient. No balance, allowance or supply changes (totalSupply is a compile-time constant and a non-zero amount reverts InsufficientAllowance), so there is no loss of funds and the launch flows are unaffected; the impact is on explorers, indexers and supply/holder reconstruction from logs that treat from == address(0) as a mint, and on the 'only one mint' guarantee. OpenZeppelin v5's `_transfer` reverts with ERC20InvalidSender(address(0)) for exactly this case. Same root-cause family, recorded here rather than as a separate finding: `transferFrom(owner, to, 0)` from a stranger with no allowance also succeeds and emits `Approval(owner, stranger, 0)` (line 177) for an owner who never approved anyone; it writes nothing. Minimal fix that preserves the intended design: in `_transfer` add `if (from == address(0)) revert ZeroAddress();` beside the existing `to` check (the constructor writes `_balances` directly and does not go through `_transfer`, so the launch mint is unaffected). Optionally drop the Approval emission in `_spendAllowance`, as OpenZeppelin v5 did, to close the stranger-Approval variant; do not return early on amount == 0, since EIP-20 requires zero-value transfers to be treated as normal transfers.","line":153,"path":"src/StateToken.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {StateToken} from \"src/StateToken.sol\";\n\n/// @notice Fails on the current code: anyone can call transferFrom(address(0), to, 0) with no\n///         allowance and make the token emit Transfer(address(0), to, 0), the shape of a mint.\n///         Passes once _transfer (or transferFrom) rejects from == address(0).\ncontract ZeroSenderEventTest is Test {\n    StateToken token;\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n\n    function setUp() public {\n        token = new StateToken();\n    }\n\n    function test_transferFromZeroSenderReverts() public {\n        vm.prank(alice);\n        (bool ok,) = address(token).call(abi.encodeCall(StateToken.transferFrom, (address(0), bob, 0)));\n        assertFalse(ok, \"transferFrom(address(0), to, 0) succeeded and emitted a mint-shaped Transfer event\");\n    }\n}","reproduction":"State: freshly deployed StateToken (constructor minted 1,000,000e18 to the deployer). Caller: any address with 0 STATE and no allowance, e.g. alice = makeAddr(\"alice\"). Call: vm.prank(alice); token.transferFrom(address(0), bob, 0). Expected: revert (address(0) is not a holder and granted no allowance; the only Transfer from address(0) should be the constructor mint). Actual: returns true and emits Approval(address(0), alice, 0) then Transfer(address(0), bob, 0); balanceOf(bob) stays 0, totalSupply stays 1e24. Control: vm.prank(alice); token.transferFrom(address(0), bob, 1) reverts InsufficientAllowance(alice, 0, 1). Reproduced by running both specialist proofs (.imd/reads/proofs/Proof_4da71c5445c4.t.sol and Proof_92ec7a8a44a6.t.sol) copied into test/scratch/: both fail on the current code for the stated reason. Related variant reproduced in test/scratch/ReviewChecks.t.sol: after token.transfer(alice, 1 ether), vm.prank(bob); token.transferFrom(alice, carol, 0) succeeds and emits two logs (Approval(alice, bob, 0), Transfer(alice, carol, 0)) with allowance(alice, bob) still 0.","severity":"low","snippet":"        if (to == address(0)) revert ZeroAddress();","title":"transferFrom accepts from == address(0): any caller can emit a mint-shaped Transfer(address(0), to, 0) and Approval(address(0), caller, 0) after launch"}],"hash":"7005b0419d6f946051b20f1974cf757f22299324312910fced6459d2818a2ecb","nodeId":"676322d0-2ad8-4aea-bf3e-22ce8e78780a","outcome":"completed","summary":"Review complete. The findings file is written at the repository root with one merged finding and a nine-row coverage record.\n\n**Outcome**\n\n- **One finding kept, severity low.** All four specialists reported the same root cause: `_transfer` at src/StateToken.sol:153 rejects a zero recipient but never a zero sender, so any caller can run a zero-amount transferFrom from address(0) and make the token emit a mint-shaped Transfer plus a spurious Approval from the zero address. I merged the four reports into one finding and folded in the related variant where a stranger emits a zero-value Approval on behalf of a real holder.\n- **Reproduced.** Both attached specialist proofs fail on the current code for the stated reason. I confirmed the control case: a non-zero amount from address(0) reverts with InsufficientAllowance, so no value moves and supply is untouched. Severity stays low because the harm is limited to off-chain indexers and the documented \"only one mint\" guarantee.\n- **Nothing the specialists missed.** My own scratch tests covered every entry point, the CREATE2 deployment with no constructor arguments, creation and runtime size limits, an opcode scan for DELEGATECALL, CALLCODE and SELFDESTRUCT, and admin-shaped selectors. All held. The project's own 68 tests pass. The manifest matches the code on contract name, metadata, supply and constructor arguments.\n\n**Not run:** the protected launch-flow tests import Uniswap v4 core and the floor's helper contracts, which are not vendored in this tree, so I verified their assertions by equivalent scratch tests instead.\n\n**Suggested fix for the author:** add a `from == address(0)` check beside the existing recipient check in `_transfer`. The constructor writes balances directly, so the launch mint is unaffected.","treeHash":null,"usage":{"cachedInputTokens":461807,"inputTokens":258,"model":"claude-fable-5-1","outputTokens":9359,"runtime":"claude","turns":9,"wallClockMs":162506}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"63274cc86a9400de","findings":[{"citation":"resolved","description":"_transfer rejects a zero recipient but not a zero sender. transferFrom(address(0), to, 0) passes _spendAllowance (allowance of address(0) is 0, and 0 < 0 is false) and _transfer (balance of address(0) is 0, and 0 < 0 is false), so the call succeeds and emits Transfer(address(0), to, 0) and Approval(address(0), caller, 0). No balance or supply changes, so there is no loss of funds and the conservation invariant sum(balances) == totalSupply still holds (checked with a fuzz invariant run). The harm is to the token's post-launch guarantee that 'the only mint that ever happens' is the constructor's: explorers, indexers and the launch's own monitoring treat Transfer from address(0) as a mint signal, and an unprivileged caller can forge that signal for any recipient at will. OpenZeppelin's ERC20 rejects this case (ERC20InvalidSender). Minimal fix that preserves the intended behaviour: in _transfer, `if (from == address(0) || to == address(0)) revert ZeroAddress();` (or check `from` in transferFrom). Related note (info, same root cause family): transferFrom(owner, to, 0) from a caller with no allowance also succeeds and emits Approval(owner, caller, 0) for an owner who never approved anyone; it writes nothing, but lets a stranger emit Approval logs on behalf of any address. OpenZeppelin v4 had the same behaviour and v5 removed the Approval emission from _spendAllowance; dropping that emit (or returning early when amount == 0 is not EIP-20 compliant, so prefer dropping the emit) closes it.","line":153,"path":"src/StateToken.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test, Vm} from \"forge-std/Test.sol\";\nimport {StateToken} from \"src/StateToken.sol\";\n\n/// @notice transferFrom(address(0), to, 0) must revert: address(0) is not a holder, and a\n///         Transfer(address(0), to, v) log is the mint signal indexers and explorers key on.\n///         Fails on the current code (the call succeeds and emits Transfer(0x0, to, 0) and\n///         Approval(0x0, caller, 0)); passes once _transfer rejects from == address(0).\ncontract ZeroFromEventTest is Test {\n    StateToken token;\n    address attacker = address(0xA77A);\n    address target = address(0xBEEF);\n\n    function setUp() public {\n        token = new StateToken();\n    }\n\n    function test_transferFromZeroAddressSenderReverts() public {\n        vm.recordLogs();\n        vm.prank(attacker);\n        (bool ok,) =\n            address(token).call(abi.encodeWithSelector(StateToken.transferFrom.selector, address(0), target, 0));\n        Vm.Log[] memory logs = vm.getRecordedLogs();\n        bool mintShaped;\n        for (uint256 i; i < logs.length; ++i) {\n            if (\n                logs[i].topics[0] == keccak256(\"Transfer(address,address,uint256)\")\n                    && logs[i].topics[1] == bytes32(0)\n            ) mintShaped = true;\n        }\n        assertFalse(mintShaped, \"a stranger emitted Transfer(address(0), to, 0): a mint-shaped log after launch\");\n        assertFalse(ok, \"transferFrom with from == address(0) succeeded\");\n    }\n}","reproduction":"State: freshly deployed StateToken (supply at the factory). Call from any EOA (e.g. 0xA77A): token.transferFrom(address(0), 0xBEEF, 0). Expected: revert (address(0) is not a holder; nothing should ever emit Transfer with from == address(0) after the constructor). Actual: returns true and emits Transfer(0x0000000000000000000000000000000000000000, 0xBEEF, 0) and Approval(0x0000000000000000000000000000000000000000, 0xA77A, 0). Balances and totalSupply are unchanged. Verified with test/scratch/ZeroFromEvent.t.sol: fails on the current code, passes after adding the from == address(0) check at src/StateToken.sol:153.","severity":"low","snippet":"        if (to == address(0)) revert ZeroAddress();\n        uint256 fromBalance = _balances[from];\n        if (fromBalance < amount) revert InsufficientBalance(from, fromBalance, amount);","title":"transferFrom accepts from == address(0): anyone can emit a mint-shaped Transfer(address(0), to, 0) log after launch"}],"hash":"d7cf6e308c7b082584dc02110fcc9e6192f688589fdef7957027c0b8ab273e71","nodeId":"0b1d9398-3c5b-4a45-97c8-3911484ee1f3","outcome":"completed","summary":"Review complete. The findings file is written and no tracked file changed.\n\n**Result**\n\nStateToken is a bare fixed-supply ERC-20 with no economic logic of its own, and its economics hold. One low-severity defect was found and substantiated.\n\n- **Low: `transferFrom` accepts a zero-address sender.** `_transfer` at `src/StateToken.sol:153` rejects a zero recipient but not a zero sender. Any caller can run `transferFrom(address(0), to, 0)`, which succeeds and emits a mint-shaped `Transfer(address(0), to, 0)` log plus `Approval(address(0), caller, 0)`. Balances and supply are unchanged, so no funds are at risk, but an unprivileged party can forge the mint signal explorers and indexers key on. The same family lets a stranger emit `Approval(owner, caller, 0)` for any owner via a zero-amount `transferFrom`, noted as info inside the finding. A proof test is attached: it fails on the current code and passes once `from == address(0)` is rejected, which I verified on a throwaway copy before restoring the source.\n\n**What held**\n\n- Conservation: a StdInvariant fuzz run over random `transfer`, `approve` and `transferFrom` sequences kept the sum of balances equal to the constant supply.\n- Supply cannot grow, and no privileged party exists to move or freeze a balance. The constructor mints everything to `msg.sender` once.\n- Launch flows are exact: no fee, tax or exemption logic, so the swarm share, pool seed, remainder, distributor claims and Uniswap v4 settle/take all move exactly what they say.\n- Allowance arithmetic: checked increase panics rather than wraps, decrease reverts below zero, and the max-allowance sentinel is never decremented.\n\n**Coverage**\n\nAll five entry points have rows: four `holds`, `transferFrom` marked `finding` 1. Three extra rows record the invariants and launch economics checked. Nothing in my area was left unreached.\n\nStatic-analysis leads (large numeric literal, public functions not used internally) are style only and were not promoted.","treeHash":null,"usage":{"cachedInputTokens":691924,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":13209,"runtime":"claude","turns":20,"wallClockMs":222406}},{"artifacts":[],"attempt":1,"bundleHash":"fae41019739d179f99f02a59446e3098292302b34677cd2796c6611c7004ed16","device":"3c9b660ee1938912","findings":[{"description":"transferFrom(from, to, 0) is accepted from any caller with no allowance, because _spendAllowance passes when allowance (0) >= amount (0) and _transfer only checks `to`. With from == address(0) this emits Transfer(address(0), to, 0), which indexers that treat a Transfer from the zero address as a mint will record as a zero-value mint, and also an Approval(address(0), caller, 0). No tokens move and totalSupply is a compile-time constant, so nothing is at risk; EIP-20 requires zero-value transfers to be treated as normal transfers, so this is standard-conforming. Recorded so the behaviour is reported rather than silently blessed; the covering test (testFuzz_zeroValueTransferFromNeedsNoAllowance in test/StateToken.properties.t.sol) asserts it as observed behaviour with a comment, not as a guarantee. A guard `if (from == address(0)) revert ZeroAddress();` in _transfer would remove the zero-address event at no cost if the requester prefers.","line":142,"path":"src/StateToken.sol","reproduction":"Deploy StateToken. From any account X call transferFrom(address(0), Y, 0). Expected by a strict reader: revert (no allowance from address(0)). Actual: returns true, emits Approval(0x0, X, 0) then Transfer(0x0, Y, 0); balances and totalSupply unchanged.","severity":"info","title":"Zero-value transferFrom succeeds for any `from`, including address(0), and emits Transfer(0x0, to, 0)"}],"hash":"f8d5fdaf39e7f329ab58b6909974abad473e6220584c8866c26b840f60c15ca9","nodeId":"363b3fc6-5613-45b6-8b4d-44d5391edb6a","outcome":"completed","summary":"The findings file is in place and is intentionally excluded from git by the platform's own exclude rules, so the working tree is exactly as it should be.\n\n**Outcome.** The token is a plain fixed-supply ERC-20 with no owner, mint, burn, pause or fee, and I found no defect in it. I added two test files alongside the existing unit suite, and the full run is green under the verifier's fuzz seed and two other seeds.\n\n| Suite | Tests | Result |\n|---|---|---|\n| test/StateToken.t.sol (existing) | 33 | pass |\n| test/StateToken.properties.t.sol (new) | 34 | pass |\n| test/StateToken.invariant.t.sol (new) | 7 invariants, 256 runs x depth 64 | pass |\n\n**Invariant suite.** A handler drives the token from nine actors, including the deploying factory stub and three empty \"strangers\", with bounded inputs and ghost accounting. It runs with fail-on-revert on, so every handler path either succeeds or proves the exact custom error it expected. Paths covered:\n\n- Clamped transfer, approve, increase, decrease and transferFrom, each asserting exact balance and allowance deltas.\n- Expected-revert paths for over-balance, over-allowance, zero address, allowance overflow and underflow, and the factory pulling without allowance.\n- Unclamped transfer and transferFrom with raw amounts, where the outcome is observed and matched against the error that should have fired.\n- Privileged selectors such as mint, burnFrom, pause, freeze and seize, which must never answer.\n\nInvariants checked after every call: supply is constant, actor balances sum to the supply, nothing strands at the zero address, token or handler, every balance is explained by recorded flows, every allowance matches the ghost model, and metadata never drifts.\n\n**Property suite.** Stateless fuzz at 1000 runs over transfer commutativity, additivity, round trips, multi-hop conservation, self-transfer, the exact width of the unlimited-allowance sentinel, allowance checked before balance, owner as its own spender, from equal to to, revocation, exact exhaustion, event shape on every path, and deployment independence.\n\n**Reported, not blessed.** One info-level observation went into the findings file: a zero-value transferFrom succeeds for any from address with no allowance, including address(0), and emits a zero-value Transfer from the zero address. This is EIP-20 conforming and moves nothing, so I covered it as observed behaviour with a comment rather than as a guarantee, and noted the one-line guard that would remove it if the requester prefers.\n\nNothing was installed and no configuration file was touched. The two new files are untracked in the working tree, ready for the platform's commit.","treeHash":"d2a5b88436e3306173d54062d3081705e19bd3b1","usage":{"cachedInputTokens":1159404,"inputTokens":482,"model":"claude-fable-5-1","outputTokens":35435,"runtime":"claude","turns":28,"wallClockMs":614653}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"5835e48821d8827d","findings":[{"citation":"resolved","description":"Boundary x invariant seam (zero-input fast path). _transfer rejects to == address(0) but never rejects from == address(0), and _spendAllowance's guard `current < amount` is false when both are 0. So any unprivileged caller can run transferFrom(address(0), to, 0) with no allowance: the call succeeds, writes _allowances[0][caller] = 0, and emits Approval(address(0), caller, 0) followed by Transfer(address(0), to, 0). The only Transfer with from == address(0) is supposed to be the constructor's mint (README: 'There is no mint'); this lets anyone produce arbitrary mint-shaped events after launch. No balance, allowance or supply changes (value is 0 and totalSupply() is a constant), so there is no loss of funds; the impact is on indexers, explorers and off-chain supply/holder trackers that treat from == address(0) as a mint and may create spurious mint rows or an address(0) holder entry. The OpenZeppelin reference reverts here with ERC20InvalidSender(address(0)). Fix: in _transfer add `if (from == address(0)) revert ZeroAddress();` (or check it in transferFrom); intended behaviour is unchanged because address(0) can never hold or move tokens.","line":153,"path":"src/StateToken.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {StateToken} from \"src/StateToken.sol\";\n\n/// @notice Fails on the current code: anyone can call transferFrom(address(0), to, 0) with no\n///         allowance and make the token emit Transfer(address(0), to, 0), the shape of a mint.\n///         Passes once _transfer (or transferFrom) rejects from == address(0).\ncontract ZeroSenderEventTest is Test {\n    StateToken token;\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n\n    function setUp() public {\n        token = new StateToken();\n    }\n\n    function test_transferFromZeroSenderReverts() public {\n        vm.prank(alice);\n        (bool ok,) = address(token).call(abi.encodeCall(StateToken.transferFrom, (address(0), bob, 0)));\n        assertFalse(ok, \"transferFrom(address(0), to, 0) succeeded and emitted a mint-shaped Transfer event\");\n    }\n}","reproduction":"State: fresh StateToken, caller alice has no tokens and no allowance. Call: vm.prank(alice); token.transferFrom(address(0), bob, 0). Expected: revert (no allowance from address(0), and address(0) is not a valid sender; OZ reverts ERC20InvalidSender). Actual: returns true and emits Approval(address(0), alice, 0) and Transfer(address(0), bob, 0); balances and totalSupply unchanged (1e24). With amount 1 the call correctly reverts InsufficientAllowance(alice, 0, 1), so only the zero-amount edge is affected. Verified with test/scratch/ZeroSenderEvent.t.sol (fails on current code).","severity":"info","snippet":"        if (to == address(0)) revert ZeroAddress();\n        uint256 fromBalance = _balances[from];\n        if (fromBalance < amount) revert InsufficientBalance(from, fromBalance, amount);","title":"transferFrom accepts from == address(0) with amount 0, letting anyone emit a mint-shaped Transfer(address(0), to, 0) event"}],"hash":"fa6c8da8423340398ef76bcec9e633fca65f68025ee57e1621204c0daee1686d","nodeId":"e403dce0-0ebd-48bf-8f9a-22be116d7e0b","outcome":"completed","summary":"Review complete. The findings file is written and validated.\n\n**Outcome:** the StateToken math holds. One informational defect found, no medium or higher.\n\n**What I checked** against the Math Precision, Boundary and Numerical Gap guides:\n- **Math Precision:** the contract has no division, no fixed-point scaling, no downcasts and no `a*b/c` chains. Supply constant equals 1e24, matching the manifest string in the README. Both `unchecked` blocks sit behind an explicit comparison, and the one checked addition in `increaseAllowance` panics rather than wraps at the uint256 ceiling.\n- **Boundary:** no external calls, payable functions, receive or fallback, or `abi.decode`, so most steps are vacuous. I walked both sentinel branches, the zero-amount and max-amount inputs, the self-transfer path and the unlimited-allowance sentinel at max minus one and max.\n- **Numerical Gap:** the conservation invariant (sum of balances equals the constant total supply) was fuzzed over arbitrary transfer chains and holds.\n\n**Finding 1 (info):** `transferFrom(address(0), to, 0)` succeeds for any caller with no allowance, because the zero-amount input passes both the allowance and balance guards and `_transfer` only rejects a zero `to`. The call emits a mint-shaped `Transfer(address(0), to, 0)` event after launch. No balances, allowances or supply change, so the impact is limited to indexers and explorers. The OpenZeppelin reference reverts here. A proof test under `test/scratch/ZeroSenderEvent.t.sol` fails on the current code and is included in the finding.\n\n**Coverage:** all five entry points have rows. Four hold, `transferFrom` carries the finding, and three invariant rows record the supply, literal and boundary checks. The project's 33 tests pass. The protected launch test could not be run here because it needs Uniswap v4 sources and launch environment variables that are not in this tree.","treeHash":null,"usage":{"cachedInputTokens":600040,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":11605,"runtime":"claude","turns":18,"wallClockMs":255195}}],"verification":[{"checks":[{"durationMs":923,"exitCode":0,"name":"build","output":"Compiling 23 files with Solc 0.8.26\nSolc 0.8.26 finished in 855.00ms\nCompiler run successful!\n","passed":true},{"durationMs":98,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 33 tests for test/StateToken.t.sol:StateTokenTest\n[PASS] testFuzz_approveThenAllowanceRoundTrips(address,uint256) (runs: 256, μ: 63245, ~: 63584)\n[PASS] testFuzz_transferAboveBalanceReverts(uint256,uint256) (runs: 256, μ: 104335, ~: 104515)\nLogs:\n  Bound result 3420733110\n  Bound result 80355205707534630125713401553324080415363249670746602042086327345\n\n[PASS] testFuzz_transferConservesSupply(address,uint256,uint256) (runs: 256, μ: 189191, ~: 190082)\nLogs:\n  Bound result 998233021252814897168819\n  Bound result 244354580005226027253330\n\n[PASS] testFuzz_transferFromSpendsExactly(uint256,uint256,uint256) (runs: 256, μ: 223063, ~: 223734)\nLogs:\n  Bound result 263481170763917516607\n  Bound result 402936818\n  Bound result 209602739\n\n[PASS] test_RevertWhen_approveZeroAddress() (gas: 32542)\n[PASS] test_RevertWhen_decreaseAllowanceBelowZero() (gas: 86010)\n[PASS] test_RevertWhen_increaseAllowanceOverflows() (gas: 85691)\n[PASS] test_RevertWhen_transferExceedsBalance() (gas: 97350)\n[PASS] test_RevertWhen_transferFromEmptyAccount() (gas: 37738)\n[PASS] test_RevertWhen_transferFromExceedsAllowance() (gas: 147383)\n[PASS] test_RevertWhen_transferFromExceedsBalanceDespiteAllowance() (gas: 162423)\n[PASS] test_RevertWhen_transferFromToZeroAddress() (gas: 149381)\n[PASS] test_RevertWhen_transferFromWithoutAllowance() (gas: 99881)\n[PASS] test_RevertWhen_transferToZeroAddress() (gas: 92145)\n[PASS] test_approveDoesNotRequireBalance() (gas: 75624)\n[PASS] test_approveSetsAllowanceAndEmits() (gas: 113309)\n[PASS] test_constructorEmitsMintTransfer() (gas: 14115)\n[PASS] test_constructorMintsWholeSupplyToDeployer() (gas: 49226)\n[PASS] test_deployScriptMintsToTheBroadcaster() (gas: 1254019)\n[PASS] test_deploymentDoesNotDependOnChainState() (gas: 15897)\n[PASS] test_increaseAndDecreaseAllowance() (gas: 197770)\n[PASS] test_launchFlowsArriveWhole() (gas: 301476)\n[PASS] test_metadata() (gas: 34632)\n[PASS] test_noCallIncreasesSupplyOrMintsToCaller() (gas: 683899)\n[PASS] test_noPrivilegedCallMovesOrFreezesAHolder() (gas: 469647)\n[PASS] test_runtimeHasNoDelegatecallCallcodeOrSelfdestruct() (gas: 2598371)\n[PASS] test_transferFromSpendsAllowance() (gas: 232544)\n[PASS] test_transferFromWithUnlimitedAllowanceKeepsIt() (gas: 195068)\n[PASS] test_transferMovesExactAmount() (gas: 179508)\n[PASS] test_transferToSelfKeepsBalance() (gas: 102765)\n[PASS] test_transferWholeBalance() (gas: 137229)\n[PASS] test_transferZeroAmountSucceeds() (gas: 47577)\n[PASS] test_unknownSelectorAndPlainEtherAreRejected() (gas: 59138)\nSuite result: ok. 33 passed; 0 failed; 0 skipped; finished in 12.37ms (49.35ms CPU time)\n\nRan 1 test suite in 13.18ms (12.37ms CPU time): 33 tests passed, 0 failed, 0 skipped (33 total tests)\n","passed":true},{"durationMs":32,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"StateToken.approve(address,uint256)\",\"StateToken.decreaseAllowance(address,uint256)\",\"StateToken.increaseAllowance(address,uint256)\",\"StateToken.transfer(address,uint256)\",\"StateToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":6,\"README.md\":117,\"foundry.toml\":21,\"remappings.txt\":1,\"script/DeployStateToken.s.sol\":26,\"src/StateToken.sol\":179,\"test/StateToken.t.sol\":441},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":360,"exitCode":0,"name":"slither","output":"slither: no results at low impact or above","passed":true},{"durationMs":269,"exitCode":0,"name":"aderyn","output":"[low] large-numeric-literal at src/StateToken.sol:20: Large Numeric Literal\n[low] unused-public-function at src/StateToken.sol:67: Public Function Not Used Internally (11 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"599d4c7f5a82992aa13c34e05d332279461932881fe9f8acd962b9731552617f","verifiedTreeHash":"db271a1ce91b6e2641e88824695e629289630460","verifierVersion":"0.1.0+d952fceb"},{"checks":[{"durationMs":874,"exitCode":0,"name":"build","output":"Compiling 23 files with Solc 0.8.26\nSolc 0.8.26 finished in 792.07ms\nCompiler run successful!\n","passed":true},{"durationMs":106,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 33 tests for test/StateToken.t.sol:StateTokenTest\n[PASS] testFuzz_approveThenAllowanceRoundTrips(address,uint256) (runs: 256, μ: 63245, ~: 63584)\n[PASS] testFuzz_transferAboveBalanceReverts(uint256,uint256) (runs: 256, μ: 104335, ~: 104515)\nLogs:\n  Bound result 584926809999183770940866\n  Bound result 1\n\n[PASS] testFuzz_transferConservesSupply(address,uint256,uint256) (runs: 256, μ: 189191, ~: 190082)\nLogs:\n  Bound result 3\n  Bound result 2\n\n[PASS] testFuzz_transferFromSpendsExactly(uint256,uint256,uint256) (runs: 256, μ: 223063, ~: 223734)\nLogs:\n  Bound result 209983171018400035773673\n  Bound result 8251930\n  Bound result 1543814\n\n[PASS] test_RevertWhen_approveZeroAddress() (gas: 32542)\n[PASS] test_RevertWhen_decreaseAllowanceBelowZero() (gas: 86010)\n[PASS] test_RevertWhen_increaseAllowanceOverflows() (gas: 85691)\n[PASS] test_RevertWhen_transferExceedsBalance() (gas: 97350)\n[PASS] test_RevertWhen_transferFromEmptyAccount() (gas: 37738)\n[PASS] test_RevertWhen_transferFromExceedsAllowance() (gas: 147383)\n[PASS] test_RevertWhen_transferFromExceedsBalanceDespiteAllowance() (gas: 162423)\n[PASS] test_RevertWhen_transferFromToZeroAddress() (gas: 149381)\n[PASS] test_RevertWhen_transferFromWithoutAllowance() (gas: 99881)\n[PASS] test_RevertWhen_transferToZeroAddress() (gas: 92145)\n[PASS] test_approveDoesNotRequireBalance() (gas: 75624)\n[PASS] test_approveSetsAllowanceAndEmits() (gas: 113309)\n[PASS] test_constructorEmitsMintTransfer() (gas: 14115)\n[PASS] test_constructorMintsWholeSupplyToDeployer() (gas: 49226)\n[PASS] test_deployScriptMintsToTheBroadcaster() (gas: 1254019)\n[PASS] test_deploymentDoesNotDependOnChainState() (gas: 15897)\n[PASS] test_increaseAndDecreaseAllowance() (gas: 197770)\n[PASS] test_launchFlowsArriveWhole() (gas: 301476)\n[PASS] test_metadata() (gas: 34632)\n[PASS] test_noCallIncreasesSupplyOrMintsToCaller() (gas: 683899)\n[PASS] test_noPrivilegedCallMovesOrFreezesAHolder() (gas: 469647)\n[PASS] test_runtimeHasNoDelegatecallCallcodeOrSelfdestruct() (gas: 2598371)\n[PASS] test_transferFromSpendsAllowance() (gas: 232544)\n[PASS] test_transferFromWithUnlimitedAllowanceKeepsIt() (gas: 195068)\n[PASS] test_transferMovesExactAmount() (gas: 179508)\n[PASS] test_transferToSelfKeepsBalance() (gas: 102765)\n[PASS] test_transferWholeBalance() (gas: 137229)\n[PASS] test_transferZeroAmountSucceeds() (gas: 47577)\n[PASS] test_unknownSelectorAndPlainEtherAreRejected() (gas: 59138)\nSuite result: ok. 33 passed; 0 failed; 0 skipped; finished in 16.28ms (61.89ms CPU time)\n\nRan 1 test suite in 17.07ms (16.28ms CPU time): 33 tests passed, 0 failed, 0 skipped (33 total tests)\n","passed":true},{"durationMs":34,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"StateToken.approve(address,uint256)\",\"StateToken.decreaseAllowance(address,uint256)\",\"StateToken.increaseAllowance(address,uint256)\",\"StateToken.transfer(address,uint256)\",\"StateToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":6,\"README.md\":117,\"foundry.toml\":21,\"launch.json\":24,\"remappings.txt\":1,\"script/DeployStateToken.s.sol\":26,\"src/StateToken.sol\":179,\"test/StateToken.t.sol\":441},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"61a7a4c66fed7640aca3e547fadbb8c7d13f7016a50284588fbd0759c0114545","verifiedTreeHash":"785c21e382803d358278afbcd5014fbfa5c004d7","verifierVersion":"0.1.0+d952fceb"},{"checks":[{"durationMs":1838,"exitCode":0,"name":"build","output":"Compiling 25 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.76s\nCompiler run successful!\n","passed":true},{"durationMs":24529,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 33 tests for test/StateToken.t.sol:StateTokenTest\n[PASS] testFuzz_approveThenAllowanceRoundTrips(address,uint256) (runs: 256, μ: 63320, ~: 63578)\n[PASS] testFuzz_transferAboveBalanceReverts(uint256,uint256) (runs: 256, μ: 104259, ~: 104509)\nLogs:\n  Bound result 1\n  Bound result 28073242\n\n[PASS] testFuzz_transferConservesSupply(address,uint256,uint256) (runs: 256, μ: 189417, ~: 190082)\nLogs:\n  Bound result 749817661527435201178234\n  Bound result 15701059641709840846362\n\n[PASS] testFuzz_transferFromSpendsExactly(uint256,uint256,uint256) (runs: 256, μ: 223022, ~: 223721)\nLogs:\n  Bound result 17\n  Bound result 33723198579568255448797842017539257392994856525765510017818913316908433\n  Bound result 17\n\n[PASS] test_RevertWhen_approveZeroAddress() (gas: 32542)\n[PASS] test_RevertWhen_decreaseAllowanceBelowZero() (gas: 86010)\n[PASS] test_RevertWhen_increaseAllowanceOverflows() (gas: 85691)\n[PASS] test_RevertWhen_transferExceedsBalance() (gas: 97350)\n[PASS] test_RevertWhen_transferFromEmptyAccount() (gas: 37738)\n[PASS] test_RevertWhen_transferFromExceedsAllowance() (gas: 147383)\n[PASS] test_RevertWhen_transferFromExceedsBalanceDespiteAllowance() (gas: 162423)\n[PASS] test_RevertWhen_transferFromToZeroAddress() (gas: 149381)\n[PASS] test_RevertWhen_transferFromWithoutAllowance() (gas: 99881)\n[PASS] test_RevertWhen_transferToZeroAddress() (gas: 92145)\n[PASS] test_approveDoesNotRequireBalance() (gas: 75624)\n[PASS] test_approveSetsAllowanceAndEmits() (gas: 113309)\n[PASS] test_constructorEmitsMintTransfer() (gas: 14115)\n[PASS] test_constructorMintsWholeSupplyToDeployer() (gas: 49226)\n[PASS] test_deployScriptMintsToTheBroadcaster() (gas: 1254019)\n[PASS] test_deploymentDoesNotDependOnChainState() (gas: 15897)\n[PASS] test_increaseAndDecreaseAllowance() (gas: 197770)\n[PASS] test_launchFlowsArriveWhole() (gas: 301476)\n[PASS] test_metadata() (gas: 34632)\n[PASS] test_noCallIncreasesSupplyOrMintsToCaller() (gas: 683899)\n[PASS] test_noPrivilegedCallMovesOrFreezesAHolder() (gas: 469647)\n[PASS] test_runtimeHasNoDelegatecallCallcodeOrSelfdestruct() (gas: 2598371)\n[PASS] test_transferFromSpendsAllowance() (gas: 232544)\n[PASS] test_transferFromWithUnlimitedAllowanceKeepsIt() (gas: 195068)\n[PASS] test_transferMovesExactAmount() (gas: 179508)\n[PASS] test_transferToSelfKeepsBalance() (gas: 102765)\n[PASS] test_transferWholeBalance() (gas: 137229)\n[PASS] test_transferZeroAmountSucceeds() (gas: 47577)\n[PASS] test_unknownSelectorAndPlainEtherAreRejected() (gas: 59138)\nSuite result: ok. 33 passed; 0 failed; 0 skipped; finished in 257.59ms (137.71ms CPU time)\n\nRan 34 tests for test/StateToken.properties.t.sol:StateTokenPropertiesTest\n[PASS] testFuzz_allowanceChangesEmitExactlyOneApproval(uint256,uint256) (runs: 1000, μ: 134525, ~: 134756)\nLogs:\n  Bound result 36122886030742045772\n  Bound result 252514889336021963500103050664522205082422547412140344305033508754\n\n[PASS] testFuzz_allowanceCheckedBeforeBalance(uint256,uint256,uint256) (runs: 1000, μ: 150518, ~: 151450)\nLogs:\n  Bound result 1\n  Bound result 2428830010\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007910753385839\n\n[PASS] testFuzz_allowanceExhaustsExactly(uint256,uint8) (runs: 1000, μ: 513515, ~: 445873)\nLogs:\n  Bound result 977\n  Bound result 14\n\n[PASS] testFuzz_allowanceOfUntouchedPairIsZero(address,address) (runs: 1000, μ: 11333, ~: 11333)\n[PASS] testFuzz_anyDeployerGetsTheWholeSupply(address) (runs: 1000, μ: 30110, ~: 30113)\n[PASS] testFuzz_approveIsIdempotentAndOverwrites(uint256,uint256) (runs: 1000, μ: 183295, ~: 183412)\n[PASS] testFuzz_balanceOfUntouchedAddressIsZero(address) (runs: 1000, μ: 13821, ~: 13821)\n[PASS] testFuzz_chainOfHopsConserves(uint256,uint8) (runs: 1000, μ: 760957, ~: 630861)\nLogs:\n  Bound result 137702644077899787687177\n  Bound result 24\n\n[PASS] testFuzz_decreaseAllowanceToExactlyZero(uint256) (runs: 1000, μ: 124668, ~: 124866)\n[PASS] testFuzz_increaseAllowanceToExactlyMax(uint256) (runs: 1000, μ: 137962, ~: 138009)\n[PASS] testFuzz_increaseFromMaxByZeroIsAllowed(uint256) (runs: 1000, μ: 132513, ~: 132455)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_increaseThenDecreaseRoundTrips(uint256,uint256) (runs: 1000, μ: 147916, ~: 148147)\nLogs:\n  Bound result 36122886030742045772\n  Bound result 252514889336021963500103050664522205082422547412140344305033508754\n\n[PASS] testFuzz_ownerAsSpenderNeedsSelfAllowance(uint256,uint256) (runs: 1000, μ: 233615, ~: 233974)\nLogs:\n  Bound result 2267179818740901046\n  Bound result 2148457836732808104\n\n[PASS] testFuzz_revocationIsImmediate(uint256) (runs: 1000, μ: 175922, ~: 175864)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_selfTransfer(uint256,uint256) (runs: 1000, μ: 94192, ~: 92490)\nLogs:\n  Bound result 36122886030742045772\n\n[PASS] testFuzz_sentinelBoundary(uint256,uint256) (runs: 1000, μ: 294463, ~: 294925)\nLogs:\n  Bound result 36122886030742045772\n  Bound result 14999959228191584834\n\n[PASS] testFuzz_spenderCanPullToItselfWithinAllowance(uint256,uint256,uint256) (runs: 1000, μ: 222510, ~: 226178)\nLogs:\n  Bound result 926588733138168744120442\n  Bound result 825207613328765797757748\n  Bound result 825207613328765797757748\n\n[PASS] testFuzz_transferEmitsExactlyOneEvent(uint256) (runs: 1000, μ: 70875, ~: 71083)\nLogs:\n  Bound result 0\n\n[PASS] testFuzz_transferFromEventShape(uint256,uint256,bool) (runs: 1000, μ: 186381, ~: 183268)\nLogs:\n  Bound result 109677739216779233369897\n  Bound result 109677739216779233369896\n\n[PASS] testFuzz_transferFromToSameAccountSpendsAllowanceOnly(uint256,uint256) (runs: 1000, μ: 174731, ~: 176063)\nLogs:\n  Bound result 36122886030742045772\n  Bound result 8472041336000319724\n\n[PASS] testFuzz_transferIsAdditive(uint256,uint256,uint256) (runs: 1000, μ: 249466, ~: 252703)\nLogs:\n  Bound result 547753990730945137435060\n  Bound result 427766545036652742710597\n  Bound result 92363716446313221651744\n\n[PASS] testFuzz_transferRoundTrip(uint256,uint256) (runs: 1000, μ: 171262, ~: 172876)\nLogs:\n  Bound result 36122886030742045772\n  Bound result 8472041336000319724\n\n[PASS] testFuzz_transfersCommute(uint256,uint256,uint256) (runs: 1000, μ: 340193, ~: 348936)\nLogs:\n  Bound result 1\n  Bound result 0\n  Bound result 0\n\n[PASS] testFuzz_unlimitedAllowanceStillNeedsBalance(uint256,uint256) (runs: 1000, μ: 169476, ~: 169895)\nLogs:\n  Bound result 36122886030742045772\n  Bound result 252514889336021963500103050664522205082422547412140344305033508754\n\n[PASS] testFuzz_zeroAddressBeatsBalanceCheck(uint256) (runs: 1000, μ: 42929, ~: 42876)\n[PASS] testFuzz_zeroValueTransferFromNeedsNoAllowance(address,address) (runs: 1000, μ: 106576, ~: 106686)\n[PASS] test_creationCodeTakesNoArguments() (gas: 579700)\n[PASS] test_decreaseAllowanceIsRefusedForUnapprovedPair() (gas: 73229)\n[PASS] test_decreaseAllowanceZeroSpenderReverts() (gas: 34766)\n[PASS] test_decreaseFromUnlimitedMakesItFinite() (gas: 225878)\n[PASS] test_failedCallsEmitNothing() (gas: 67986)\n[PASS] test_transferOfMaxUintRevertsEvenForTheWholeSupplyHolder() (gas: 69748)\n[PASS] test_transferOfOneWei() (gas: 113397)\n[PASS] test_twoDeploymentsDoNotShareState() (gas: 180314)\nSuite result: ok. 34 passed; 0 failed; 0 skipped; finished in 257.84ms (5.55s CPU time)\n\nRan 1 test for test/StateToken.invariant.t.sol:StateTokenInvariantTest\n[PASS]\nStateTokenInvariantTest invariants:\n[PASS] invariant_allowancesMatchModel\n[PASS] invariant_balancesSumToSupply\n[PASS] invariant_ghostFlowsExplainEveryBalance\n[PASS] invariant_metadataIsFixed\n[PASS] invariant_noTokensStrandedAtSpecialAddresses\n[PASS] invariant_receivedNeverExceedsVolume\n[PASS] invariant_supplyIsConstant\n StateTokenInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭-------------------+-----------------------------------+-------+---------+----------╮\n| Contract          | Selector                          | Calls | Reverts | Discards |\n+====================================================================================+\n| StateTokenHandler | approve                           | 1041  | 0       | 0        |\n|-------------------+-----------------------------------+-------+---------+----------|\n| StateTokenHandler | approveZeroSpenderReverts         | 983   | 0       | 0        |\n|-------------------+-----------------------------------+-------+---------+----------|\n| StateTokenHandler | decreaseAllowance                 | 1075  | 0       | 0        |\n|-------------------+-----------------------------------+-------+---------+----------|\n| StateTokenHandler | decreaseAllowanceBelowZeroReverts | 1057  | 0       | 0        |\n|-------------------+-----------------------------------+-------+---------+----------|\n| StateTokenHandler | factoryCannotPullWithoutAllowance | 1022  | 0       | 0        |\n|-------------------+-----------------------------------+-------+---------+----------|\n| StateTokenHandler | increaseAllowance                 | 1026  | 0       | 0        |\n|-------------------+-----------------------------------+-------+---------+----------|\n| StateTokenHandler | increaseAllowanceOverflowReverts  | 1058  | 0       | 0        |\n|-------------------+-----------------------------------+-------+---------+----------|\n| StateTokenHandler | privilegedCallsDoNothing          | 988   | 0       | 0        |\n|-------------------+-----------------------------------+-------+---------+----------|\n| StateTokenHandler | transfer                          | 1048  | 0       | 0        |\n|-------------------+-----------------------------------+-------+---------+----------|\n| StateTokenHandler | transferAboveBalanceReverts       | 1037  | 0       | 0        |\n|-------------------+-----------------------------------+-------+---------+----------|\n| StateTokenHandler | transferFrom                      | 978   | 0       | 0        |\n|-------------------+-----------------------------------+-------+---------+----------|\n| StateTokenHandler | transferFromAboveAllowanceReverts | 1025  | 0       | 0        |\n|-------------------+-----------------------------------+-------+---------+----------|\n| StateTokenHandler | transferFromAboveBalanceReverts   | 1040  | 0       | 0        |\n|-------------------+-----------------------------------+-------+---------+----------|\n| StateTokenHandler | transferFromUnclamped             | 1018  | 0       | 0        |\n|-------------------+-----------------------------------+-------+---------+----------|\n| StateTokenHandler | transferToZeroReverts             | 987   | 0       | 0        |\n|-------------------+-----------------------------------+-------+---------+----------|\n| StateTokenHandler | transferUnclamped                 | 1001  | 0       | 0        |\n╰-------------------+-----------------------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3\n  Bound result 4\n  Bound result 0\n  Bound result 4\n  Bound result 3\n  Bound result 0\n  Bound result 2\n  Bound result 6\n  Bound result 242\n  Bound result 1\n  Bound result 1\n  Bound result 5\n  Bound result 2\n  Bound result 1\n  Bound result 340282366920938463463374607431768211455\n  Bound result 3\n  Bound result 2\n  Bound result 8\n  Bound result 1000\n  Bound result 5\n  Bound result 1\n  Bound result 3\n  Bound result 21995\n  Bound result 8\n  Bound result 7\n  Bound result 5000000000000000000\n  Bound result 4\n  Bound result 0\n  Bound result 0\n  Bound result 5\n  Bound result 7\n  Bound result 661\n  Bound result 2\n  Bound result 4\n  Bound result 2\n  Bound result 9972\n  Bound result 8\n  Bound result 0\n  Bound result 0\n  Bound result 7\n  Bound result 2\n  Bound result 6\n  Bound result 11318227178895847773725875081477851752720215783639\n  Bound result 7\n  Bound result 2\n  Bound result 3\n  Bound result 0\n  Bound result 1\n  Bound result 8\n  Bound result 6\n  Bound result 1\n  Bound result 0\n  Bound result 3\n  Bound result 4\n  Bound result 8\n  Bound result 0\n  Bound result 3\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 6\n  Bound result 0\n  Bound result 1\n  Bound result 2\n  Bound result 2\n  Bound result 5\n  Bound result 3\n  Bound result 2\n  Bound result 0\n  Bound result 6\n  Bound result 3\n  Bound result 0\n  Bound result 2\n  Bound result 2\n  Bound result 0\n  Bound result 6\n  Bound result 7\n  Bound result 8\n  Bound result 1133301551691762696\n  Bound result 1\n  Bound result 6\n  Bound result 0\n  Bound result 4183\n  Bound result 1\n  Bound result 1\n  Bound result 3\n  Bound result 0\n  Bound result 0\n  Bound result 2\n  Bound result 1\n  Bound result 10744\n  Bound result 1\n  Bound result 0\n  Bound result 3\n  Bound result 2000000000000000000\n  Bound result 8\n  Bound result 1\n  Bound result 5\n  Bound result 2\n  Bound result 4\n  Bound result 2356239976144585233084798982139757751740\n  Bound result 1\n  Bound result 2\n  Bound result 0\n  Bound result 2\n  Bound result 8\n  Bound result 0\n  Bound result 7\n  Bound result 0\n  Bound result 6\n  Bound result 3\n  Bound result 7\n  Bound result 8\n  Bound result 0\n  Bound result 5\n  Bound result 8\n  Bound result 0\n  Bound result 1\n  Bound result 8\n  Bound result 0\n  Bound result 8\n  Bound result 5\n  Bound result 0\n  Bound result 1232\n  Bound result 4\n  Bound result 0\n  Bound result 0\n  Bound result 4\n  Bound result 6\n  Bound result 5\n  Bound result 6\n  Bound result 100000000000000000000\n  Bound result 3\n  Bound result 0\n  Bound result 6\n  Bound result 226704035831830284090003\n  Bound result 183338168807665226704036015168452897668\n  Bound result 1\n  Bound result 1\n  Bound result 5\n  Bound result 7\n  Bound result 13978\n  Bound result 7\n  Bound result 6\n  Bound result 0\n  Bound result 3\n  Bound result 8\n  Bound result 12990\n  Bound result 4\n  Bound result 0\n  Bound result 3\n  Bound result 0\n  Bound result 6\n  Bound result 8\n  Bound result 7\n  Bound result 8\n  Bound result 6\n  Bound result 1\n  Bound result 81\n  Bound result 7\n  Bound result 3\n  Bound result 3\n  Bound result 340282366920938463463374607431768211455\n  Bound result 1\n  Bound result 1\n  Bound result 40000000000000000000\n  Bound result 5\n  Bound result 2\n  Bound result 5\n  Bound result 7\n  Bound result 9473\n  Bound result 0\n  Bound result 5\n  Bound result 4\n  Bound result 3\n  Bound result 2\n  Bound result 632\n  transfer: 7\n  transferFrom: 5\n  approve: 6\n  increaseAllowance: 1\n  decreaseAllowance: 7\n  expected reverts: 34\n  unclamped: 4\n  unclamped succeeded: 1\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 24.43s (24.43s CPU time)\n\nRan 3 test suites in 24.44s (24.95s CPU time): 68 tests passed, 0 failed, 0 skipped (68 total tests)\n","passed":true},{"durationMs":31,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"StateToken.approve(address,uint256)\",\"StateToken.decreaseAllowance(address,uint256)\",\"StateToken.increaseAllowance(address,uint256)\",\"StateToken.transfer(address,uint256)\",\"StateToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":6,\"README.md\":117,\"foundry.toml\":21,\"remappings.txt\":1,\"script/DeployStateToken.s.sol\":26,\"src/StateToken.sol\":179,\"test/StateToken.invariant.t.sol\":537,\"test/StateToken.properties.t.sol\":557,\"test/StateToken.t.sol\":441},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"f8d5fdaf39e7f329ab58b6909974abad473e6220584c8866c26b840f60c15ca9","verifiedTreeHash":"d2a5b88436e3306173d54062d3081705e19bd3b1","verifierVersion":"0.1.0+d952fceb"}]}