{"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":"cf4e8326-5f4e-4e0f-bfff-b97ff9f833dc","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"a6a1fd669a4f33c9e179aa64637ae0b3f1e529b53ff068fdbf40ef96eca9b615","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":"9d983ddbc7f204d3e99ab957bb85a0afa32c5faf2625d8c0edd8efda419b1608","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":"6dc1f937dfbb7c51944341574bcddd0a5c31e6c62d920288366b64d74da57ead","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":"a91eab4abe442d33faadcd059b5f0a6b350d12390d0aec4b098f34538ed1e553","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":"71afa91a9a9f1a0b545503e57e7fd2a59dbd30d27c1fa0ba14989714aa833979","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":"f3eea77c8f6b4024ce8118d4ec42d0d1dbe246abc7f247c03ea7eb507e4d85f6","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":"35d494972a1ff32c53a0237867cef5274049d345b03d3dadcff1ccd55712e566","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"09ae2835e73992d2876b722ed7e34fdf1f2047be34443d77d791844b89f9400a","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: Quorum (QRM).\nToken name: Quorum\nToken symbol: QRM\nToken supply: 1,000,000,000 with 18 decimals, all minted once to the deployer in the constructor.","parentJobId":null,"planHash":"98d63ac02032a5291829ac2290aa54811a7a5d231a97025ab74317bb1768b863","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"cf4e8326-5f4e-4e0f-bfff-b97ff9f833dc","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1968-quorum"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"52622","feedbackHash":"093b9dcc709be4672f62d47149fa049604e0042212a705d4500be744271cdaac","nodeKey":"audit_economics","submissionHash":"a6a1fd669a4f33c9e179aa64637ae0b3f1e529b53ff068fdbf40ef96eca9b615","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52600","feedbackHash":"631c8419518681ebcdc65aedbd1705932b28932923b8d3e7053725c47c9389c9","nodeKey":"audit_flow","submissionHash":"9d983ddbc7f204d3e99ab957bb85a0afa32c5faf2625d8c0edd8efda419b1608","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51021","feedbackHash":"ad45b5d703232f4efbaf7169c0d83e1c27e0a5e22385da2debf8bc9f80209a71","nodeKey":"audit_judge","submissionHash":"6dc1f937dfbb7c51944341574bcddd0a5c31e6c62d920288366b64d74da57ead","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52614","feedbackHash":"f276784b9f28a65ad73a5173706596d24f3369a153ef7b643e75612c1e70d7bf","nodeKey":"audit_math","submissionHash":"a91eab4abe442d33faadcd059b5f0a6b350d12390d0aec4b098f34538ed1e553","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52613","feedbackHash":"6872bd34a7611aebeec195cf5517154d1c41371c97a8198dbf59d4842bae5121","nodeKey":"audit_permissions","submissionHash":"71afa91a9a9f1a0b545503e57e7fd2a59dbd30d27c1fa0ba14989714aa833979","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51482","feedbackHash":"d059f77ee8c68644fbe0ca5e9d42b241712ace533ca49d25d5908017ef73a9e9","nodeKey":"build_contract_project","submissionHash":"f3eea77c8f6b4024ce8118d4ec42d0d1dbe246abc7f247c03ea7eb507e4d85f6","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52603","feedbackHash":"000d184d027240243c265740fb5937582caa615266f225f8a310e63a79e371a5","nodeKey":"manifest","submissionHash":"35d494972a1ff32c53a0237867cef5274049d345b03d3dadcff1ccd55712e566","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52596","feedbackHash":"4362a6ada21a5a61a8991ee0c07d4df192a1ec9ba1b8b7e62792e7a28e4de32f","nodeKey":"write_foundry_tests","submissionHash":"09ae2835e73992d2876b722ed7e34fdf1f2047be34443d77d791844b89f9400a","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"01aeea0f724cf7e57527b05db0cbc69b59bdb56b578f7ea1664cdd5f646e50e4","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"b6d2cda125088f55e970a56df30e26cfe581c4e3b125191dc62acd4432282dca","device":"629987aaf6c7aa88","findings":[{"description":"_transfer rejects a zero receiver but never a zero sender. transferFrom(address(0), to, 0) passes the allowance check (allowance[0][spender] is 0 and 0 < 0 is false), passes the balance check (balanceOf[0] is 0 and 0 < 0 is false), and emits Transfer(address(0), to, 0). The only Transfer events a fixed-supply token should ever emit from the zero address are the single constructor mint; off-chain indexers, explorers and accounting tools treat Transfer-from-zero as a mint, so any caller can plant spurious zero-value mint records for any address at any time. No balance or supply changes, so funds are not at risk. The reference ERC-20 implementation (OpenZeppelin v5 ERC20._transfer) reverts with ERC20InvalidSender(address(0)) on this input. Fix: in _transfer, revert when from == address(0), for example with a new InvalidSender(address) error. Tests in test/ do not assert the current behaviour; test_transferFromZeroWithoutApprovalSucceeds uses a non-zero owner.","line":56,"path":"src/QuorumToken.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {QuorumToken} from \"src/QuorumToken.sol\";\n\n/// @notice Anyone can emit a mint-shaped `Transfer(address(0), to, 0)` by calling\n/// `transferFrom(address(0), to, 0)`: the allowance check passes because 0 <= 0, and `_transfer`\n/// only rejects a zero receiver, never a zero sender. A plain ERC-20 (OpenZeppelin v5) reverts with\n/// `ERC20InvalidSender(address(0))` here. Fails on the current code; passes once a zero `from` reverts.\ncontract ZeroSenderEventTest is Test {\n    QuorumToken token;\n    address deployer = address(0xD0);\n    address attacker = address(0xBAD);\n\n    function setUp() public {\n        vm.prank(deployer);\n        token = new QuorumToken();\n    }\n\n    function test_transferFromZeroSenderReverts() public {\n        vm.prank(attacker);\n        vm.expectRevert();\n        token.transferFrom(address(0), attacker, 0);\n    }\n}","reproduction":"Deploy QuorumToken from any address. From any other address call transferFrom(address(0), <that address>, 0). Expected: revert (invalid sender). Actual: returns true and emits Transfer(address(0), <that address>, 0). Also reproducible as vm.prank(0xBAD); token.transferFrom(address(0), 0xBAD, 0) in a Foundry test.","severity":"low","title":"transferFrom accepts address(0) as the sender, letting anyone emit a mint-shaped Transfer(0x0, to, 0) event"}],"hash":"09ae2835e73992d2876b722ed7e34fdf1f2047be34443d77d791844b89f9400a","nodeId":"1bfd6e3a-a437-49cd-8630-c0820fd1bdfa","outcome":"completed","summary":"The findings file is in place and is deliberately excluded from git by the harness, as is scratch. The work is complete.\n\n**Outcome.** Two new test files under `test/` extend the existing suite from 11 to 59 tests, all passing on forge 1.8.3 with the verifier's fuzz seed and without it. The only defect found is low severity and is reported in the findings file rather than asserted as correct.\n\n**What I added**\n\n- `test/QuorumToken.edge.t.sol`: 46 unit and fuzz tests over the failure paths and edges. Zero-amount and exact-balance transfers, one wei over, max uint, self-transfers, approve overwriting and resetting, allowance boundaries including max minus one, transferFrom without approval, with infinite allowance but no balance, to the zero address, and the check ordering. It also verifies reverted calls leave allowances untouched, that the token refuses ETH and unknown selectors, that 22 common admin signatures all revert from a stranger and from the deployer without moving anything, and mirrors the launch floor's opcode scan. Fuzz tests run at 1000 runs with `bound` rather than `vm.assume`.\n- `test/QuorumToken.invariant.t.sol`: a handler with six actors and a ghost ledger of balances, allowances, sent and received amounts. It drives successful calls with bounded inputs and deliberately exercises the four revert paths, checking the exact error bytes each time. Seven invariants run with fail-on-revert at 256 runs by 64 depth: constant supply, balances summing to supply, balances and allowances matching the ghost ledger, flows reconciling, nothing held by the zero address, the token or the handler, and constant metadata. A second open-world suite fuzzes the token directly with arbitrary senders and confirms supply and the zero-address balance never move.\n\n**Finding reported** in `.imd-findings.json` with a self-contained proof that fails on the current code. The internal transfer rejects a zero receiver but never a zero sender, so anyone can call `transferFrom(address(0), to, 0)` and emit a mint-shaped `Transfer(0x0, to, 0)` event. No balance or supply changes, so I rated it low. OpenZeppelin's ERC-20 reverts on this input, and the fix is a one-line zero-sender check.\n\n**Not changed:** the implementation, configuration, `lib/`, and the earlier contributor's test file.","treeHash":"f889829f6fdaadd19986d60854400c2258571c28","usage":{"cachedInputTokens":617742,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":25526,"runtime":"claude","turns":21,"wallClockMs":383869}},{"artifacts":[],"attempt":1,"bundleHash":"75c0e63ebe2c11b37e2da1c3f1dfb5963620cbb32a5ebf08191bc804ac7ae49f","device":"7e64d4917fa814e6","findings":[],"hash":"35d494972a1ff32c53a0237867cef5274049d345b03d3dadcff1ccd55712e566","nodeId":"6e0e0d51-7940-46f9-a441-e1974f50b21d","outcome":"completed","summary":"`launch.json` is written and verified. Only that file was added; nothing else in the tree changed.\n\n**What the manifest says**\n- Kind `custom_token`, token contract `QuorumToken` with name Quorum, symbol QRM, 18 decimals.\n- Constructor arguments are an empty list, matching the accepted source's parameterless constructor.\n- Total supply is 1000000000000000000000000000 minor units, exactly what the constructor mints to msg.sender.\n- Application contracts list is empty, since the accepted tree contains only the token.\n- Pool and economics are copied verbatim from the launch: IMD pair address, fee 12500, tick spacing 60, one-to-one provenance price, 8000 pool bps, 7,000 IMD opening cap, and the requester's remainder address.\n- Notes describe the token's fixed supply, lack of admin functions, exact transfers and absence of application contracts.\n\n**Checks run**\n- `forge build` compiled cleanly with solc 0.8.26.\n- `forge test` with the verifier's fuzz seed passed all 11 existing tests.\n- A JSON check confirmed only schema keys are present, addresses are lowercase, uint256 values are strings, fee and decimals are numbers, and notes are well under the 4000 character limit.\n- `git status` shows launch.json as the only new or changed path.","treeHash":"5ffbe0212d9b960cac1c18efa7f9767274d5e36c","usage":{"cachedInputTokens":195668,"inputTokens":162,"model":"claude-fable-5-1","outputTokens":2739,"runtime":"claude","turns":6,"wallClockMs":52177}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"72ae9b5bbd1a54b6","findings":[{"citation":"resolved","description":"Merged from audit_math, audit_flow, audit_economics (second finding), audit_permissions and write_foundry_tests, all the same root cause. `_transfer` (line 56) only checks `to == address(0)`. `transferFrom` takes `from` as a free parameter; with value 0 the allowance branch passes (allowance[address(0)][caller] is 0, and `0 < 0` is false, line 46) and the balance check passes (balanceOf[address(0)] is 0, `0 < 0` false, line 58), so the call returns true and emits Transfer(address(0), to, 0). No balance or totalSupply changes, and a nonzero value reverts with InsufficientAllowance before reaching the balance check, so funds are never at risk; the impact is event integrity only. Indexers, explorers and supply monitors conventionally classify Transfer-from-zero as a mint, so any unprivileged caller can plant zero-value mint records for any address on a token the README and manifest notes describe as having no mint. OpenZeppelin v5 ERC20._transfer reverts with ERC20InvalidSender(address(0)) on this input. Minimal fix preserving intended behaviour: add `if (from == address(0)) revert InvalidSender(from);` beside the receiver check in _transfer (new error InvalidSender(address)); no legitimate flow originates from address(0). Severity low (the highest the specialists gave); the swarm share, seed, distributor claims and swaps are unaffected.","line":56,"path":"src/QuorumToken.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {QuorumToken} from \"src/QuorumToken.sol\";\n\n/// @notice Anyone can emit a mint-shaped `Transfer(address(0), to, 0)` by calling\n/// `transferFrom(address(0), to, 0)`: the allowance check passes because 0 <= 0, and `_transfer`\n/// only rejects a zero receiver, never a zero sender. A plain ERC-20 (OpenZeppelin v5) reverts with\n/// `ERC20InvalidSender(address(0))` here. Fails on the current code; passes once a zero `from` reverts.\ncontract ZeroSenderEventTest is Test {\n    QuorumToken token;\n    address deployer = address(0xD0);\n    address attacker = address(0xBAD);\n\n    function setUp() public {\n        vm.prank(deployer);\n        token = new QuorumToken();\n    }\n\n    function test_transferFromZeroSenderReverts() public {\n        vm.prank(attacker);\n        vm.expectRevert();\n        token.transferFrom(address(0), attacker, 0);\n    }\n}","reproduction":"State: fresh QuorumToken deployed by 0xD0 (totalSupply 1e27, all held by 0xD0). Call from alice (0xA1): token.transferFrom(address(0), bob (0xB0), 0). Expected (ERC-20 reference semantics, no mint exists): revert. Actual: returns true and emits Transfer(0x0000000000000000000000000000000000000000, 0xB0, 0); totalSupply stays 1000000000000000000000000000 and balanceOf(0xB0) stays 0. Contrast: alice calling token.transferFrom(address(0), bob, 1) reverts InsufficientAllowance(0xA1, 0, 1). Reproduced on this tree with vm.expectEmit(true,true,false,true); emit Transfer(address(0), bob, 0); assertTrue(token.transferFrom(address(0), bob, 0)) (passes on current code), and the attached proof test test_transferFromZeroSenderReverts fails on current code with \"next call did not revert as expected\" and passes once a zero `from` reverts.","severity":"low","snippet":"        if (to == address(0)) revert InvalidReceiver(to);","title":"_transfer rejects a zero receiver but not a zero sender: anyone can emit a mint-shaped Transfer(address(0), to, 0) via transferFrom"},{"citation":"resolved","description":"From audit_economics. `approve` writes the new allowance unconditionally and the contract exposes no relative adjustment (increaseAllowance/decreaseAllowance) or permit. A spender who sees a pending approve that lowers their allowance can spend the current allowance first and then the new one. This is the behaviour of the ERC-20 interface itself (OpenZeppelin v5 ERC20 is identical) and the requester asked for a plain ERC-20, so it is recorded as an informational trust assumption, not a defect: a holder can lose at most an amount they had already approved to a spender they chose. The launch flows (factory transfer to the distributor, pool seed, distributor claims, PoolManager swaps) never reduce an allowance. If wanted, add decreaseAllowance(spender, subtractedValue) or document the approve-to-zero-first convention.","line":38,"path":"src/QuorumToken.sol","reproduction":"State: deployer 0xD0 holds the supply. 1) 0xD0 calls approve(alice, 100). 2) 0xD0 submits approve(alice, 50) to lower the allowance. 3) alice front-runs with transferFrom(0xD0, alice, 100): allowance 100 >= 100, succeeds, allowance 0. 4) approve(alice, 50) lands, allowance 50. 5) alice calls transferFrom(0xD0, alice, 50). Expected by the deployer: alice takes at most 50 (or 100 if the reduction never applied). Actual: balanceOf(alice) == 150. Reproduced on this tree with a Foundry test performing exactly these five calls (assertEq(token.balanceOf(alice), 150) passes).","severity":"info","snippet":"        allowance[msg.sender][spender] = value;","title":"approve() overwrites the allowance in place with no decreaseAllowance or permit, so a spender can front-run a reduction and spend old plus new allowance (inherent ERC-20 approve race)"}],"hash":"6dc1f937dfbb7c51944341574bcddd0a5c31e6c62d920288366b64d74da57ead","nodeId":"a8378f57-6a17-4307-b51c-9c397a2cd74a","outcome":"completed","summary":"The review is complete and the findings file is written. The working tree is otherwise untouched: the scratch tests are removed, and `git status` shows only the untracked findings file.\n\n**Outcome.** The token is a plain fixed-supply ERC-20 and no funds-at-risk defect exists. The code holds against the protected floor: supply is minted once to the factory, no mint, burn, owner, pause, blacklist or fee path exists, transfers are exact for every sender, and the creation and runtime code are far below the size limits with no DELEGATECALL, CALLCODE or SELFDESTRUCT. The manifest matches the source (contract name, empty constructor arguments, supply, decimals, lowercase addresses). The project's own suite of 61 tests, including the invariant tests, passes.\n\n**Findings kept, in .imd-findings.json:**\n\n1. **Low.** `_transfer` rejects a zero receiver but not a zero sender, so anyone can call `transferFrom(address(0), to, 0)` and make the token emit a mint-shaped `Transfer(address(0), to, 0)` event. Five specialists (math, flow, economics, permissions, and the test writer) reported this same root cause, merged into one finding. Reproduced: the call returns true and emits the event while supply and balances stay unchanged; a nonzero value reverts on the allowance check. The tester's proof test fails on the current code for exactly the stated reason, so it is attached. Impact is event integrity only, so severity stays low.\n\n2. **Info.** The inherent ERC-20 approve overwrite race, reported by the economics specialist. Reproduced (alice ends with 150 after a 100 then 50 approval), but it is the interface's own behaviour and the brief asked for a plain ERC-20, so it is recorded as a trust note rather than a defect.\n\n**Coverage.** All three entry points are answered: approve and transfer hold, transferFrom carries finding 1. Two invariant rows record the fixed-supply and exact-flow checks.","treeHash":null,"usage":{"cachedInputTokens":411086,"inputTokens":162,"model":"claude-fable-5-1","outputTokens":6610,"runtime":"claude","turns":6,"wallClockMs":133490}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"7eaf1cd0fb3df712","findings":[{"citation":"resolved","description":"Asymmetry guide, step 3 (branch-symmetry): `_transfer` rejects the zero address as receiver but has no mirror check for the zero address as sender. `transfer` cannot reach it (msg.sender is never zero), but `transferFrom` takes `from` as a free parameter. With value 0 the allowance check in transferFrom passes (allowance[0][caller] == 0, and 0 < 0 is false) and the balance check passes (balanceOf[0] == 0, and 0 < 0 is false), so any unprivileged caller can make the token emit `Transfer(address(0), anyAddress, 0)`. The reference ERC-20 implementations (OpenZeppelin v5 `_transfer` reverts with ERC20InvalidSender(address(0))) reject this. No balance or supply changes: a non-zero value from address(0) reverts (InsufficientAllowance first, InsufficientBalance if an allowance existed, which it cannot since address(0) cannot call approve). Impact is limited to event integrity: indexers, explorers and accounting tools that treat `Transfer` from the zero address as a mint record a mint of zero at an arbitrary time, which can confuse supply-change monitors and pollute holder/mint histories. No funds are at risk. Minimal fix that preserves intended behaviour: add `if (from == address(0)) revert InvalidSender(from);` beside the receiver check (or reject in transferFrom), matching the OpenZeppelin semantics the README's 'plain ERC-20' claim implies.","line":56,"path":"src/QuorumToken.sol","reproduction":"State: fresh QuorumToken deployed by 0xD0. Call, from any address (e.g. 0xA1): token.transferFrom(address(0), 0xB0, 0). Actual: returns true and emits Transfer(from=0x0000000000000000000000000000000000000000, to=0xB0, value=0); balances unchanged. Expected (OpenZeppelin ERC20 semantics): revert ERC20InvalidSender(address(0)). Verified with a Foundry test using vm.expectEmit(true,true,false,true); emit Transfer(address(0), bob, 0); assertTrue(token.transferFrom(address(0), bob, 0)) which passes on the current code. Companion check: token.transferFrom(address(0), 0xB0, 1) reverts InsufficientAllowance(0xA1, 0, 1), so no real mint is reachable.","severity":"low","snippet":"        if (to == address(0)) revert InvalidReceiver(to);\n        uint256 bal = balanceOf[from];\n        if (bal < value) revert InsufficientBalance(from, bal, value);","title":"Asymmetric zero-address guard: transferFrom accepts from == address(0) and emits a spurious zero-value mint-shaped Transfer event"}],"hash":"71afa91a9a9f1a0b545503e57e7fd2a59dbd30d27c1fa0ba14989714aa833979","nodeId":"3b16c924-8e4a-4464-852b-03aa05f23e5a","outcome":"completed","summary":"The review is complete and the findings file is written at the repository root.\n\n**Outcome:** The Quorum token holds up well in my area. It has no owner, no roles, no initializer, no mint, burn, pause, fee or external call, so the access-control surface is just the three ERC-20 entry points, and no Trust Gap seam can exist because there is no privileged actor or pricing formula to combine with. I found one low-severity asymmetry.\n\n**Finding (low):** `_transfer` in `src/QuorumToken.sol` rejects the zero address as receiver but has no mirror check for the zero address as sender. Because `transferFrom` takes the sender as a free parameter, any caller can run\n\n```\ntoken.transferFrom(address(0), bob, 0)\n```\n\nand it returns true and emits a Transfer event from the zero address with value zero. OpenZeppelin v5 reverts this with an invalid-sender error. No balance or supply changes, and a non-zero value from the zero address still reverts on the allowance check, so the impact is limited to spurious mint-shaped events that can confuse indexers and supply monitors. The one-line fix is a sender-zero check beside the receiver check.\n\n**Verified as holding:**\n- `approve` writes only the caller's own allowance and rejects a zero spender.\n- `transfer` binds the sender to the caller; self-transfer leaves the balance unchanged, and the unchecked arithmetic cannot overflow since balances always sum to the fixed supply.\n- `transferFrom` keys the allowance on the caller, decrements correctly, and treats max allowance as infinite as documented.\n- The constructor mints exactly one billion tokens with 18 decimals to the factory and hands out no role.\n- Probing a dozen admin selectors plus a bare call confirmed no privileged function, fallback or receive exists.\n- Both aderyn leads are style-only and open no permission path.\n\nThe coverage record has nine rows: the three listed entry points plus the constructor, three invariants, the Trust Gap seams, and the static-analysis leads. All existing project tests pass, and my scratch tests were deleted after confirming the behaviour.","treeHash":null,"usage":{"cachedInputTokens":392710,"inputTokens":226,"model":"claude-fable-5-1","outputTokens":9851,"runtime":"claude","turns":19,"wallClockMs":139101}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"fa4b91aed45c94fa","findings":[{"citation":"resolved","description":"_transfer validates the receiver but not the sender. transferFrom(address(0), to, 0) passes the allowance branch (allowance[0][caller] == 0, and 0 < 0 is false), passes the balance check (balanceOf[0] == 0, and 0 < 0 is false) and emits Transfer(address(0), to, 0). The ERC-20 convention reserves Transfer events from the zero address for mints, and the reference OpenZeppelin implementation reverts with ERC20InvalidSender for from == address(0). No balance or supply changes (a non-zero value reverts with InsufficientAllowance before reaching the balance check), so the only effect is a spurious zero-value 'mint' event that indexers and explorers may display as token creation. Minimal fix that preserves intended behaviour: add `if (from == address(0)) revert InvalidSender(from);` at the top of _transfer (introducing an InvalidSender error). Not a security defect of the launch flows; recorded for event hygiene.","line":56,"path":"src/QuorumToken.sol","reproduction":"State: fresh QuorumToken deployed by factory F. Call: vm.prank(anyone); token.transferFrom(address(0), anyone, 0). Expected (reference behaviour): revert for sender == address(0). Actual: returns true and emits Transfer(0x0000000000000000000000000000000000000000, anyone, 0); totalSupply remains 1000000000000000000000000000 and balanceOf(anyone) remains 0. Reproduced by test_zeroValueTransferFromZeroAddressEmitsMintLikeEvent in test/scratch/Review.t.sol (passes on current code, i.e. the event is emitted).","severity":"info","snippet":"        if (to == address(0)) revert InvalidReceiver(to);","title":"_transfer does not reject from == address(0): anyone can emit a mint-shaped Transfer(0x0, to, 0) event via zero-value transferFrom"}],"hash":"9d983ddbc7f204d3e99ab957bb85a0afa32c5faf2625d8c0edd8efda419b1608","nodeId":"5365bab8-549c-4974-a42d-4dc112eccb69","outcome":"completed","summary":"The findings file is valid JSON, the quoted snippet matches line 56 of the source exactly, and git reports no tracked file changed (the findings file and the scratch test are ignored or untracked, as intended).\n\n**Outcome.** The Quorum token is a 65-line dependency-free ERC-20 with no owner, mint, burn, pause, fee or external call. Across the Execution Trace, Periphery and First Principles checklists I found no defect that affects funds, supply or the launch flows. I report one informational deviation.\n\n**What I checked and confirmed in `src/QuorumToken.sol`**\n- Constructor mints exactly one billion tokens with 18 decimals to the deployer and sets `totalSupply` to the same value. Nothing else ever writes supply.\n- `transfer` and `transferFrom` move the full value with no fee, return true, and reject the zero receiver. The unchecked arithmetic is safe since the subtraction is guarded and total balances are fixed at 1e27. Self-transfers re-read storage and leave the balance unchanged.\n- `approve` rejects a zero spender and overwrites rather than accumulates. The infinite-allowance path skips the decrement as documented.\n- Launch flows are exact: factory to distributor, distributor to claimant, factory to pool, and a trader buying and selling all land whole with supply unchanged.\n- Runtime bytecode contains no DELEGATECALL, CALLCODE, SELFDESTRUCT or any CALL or CREATE opcode. ETH sent to the contract reverts. Unknown selectors such as mint, burn, pause or owner revert.\n- Sum-of-balances conservation held across 256 fuzz runs of random transfer and transferFrom sequences.\n- Both aderyn leads are non-defects: the literal evaluates to exactly 1e27, and the mutable `totalSupply` is a gas nit only.\n\n**The one finding, severity info.** `_transfer` validates the receiver but not the sender, so `transferFrom(address(0), x, 0)` succeeds and emits a mint-shaped `Transfer(0x0, x, 0)` event. Balances and supply are untouched, and a non-zero amount reverts before the balance check. The reference OpenZeppelin implementation rejects a zero sender. The minimal fix is a sender check at the top of `_transfer`. This is event hygiene, not a security issue, so it carries no proof file.\n\n**Coverage.** All three listed entry points have rows: `approve` and `transfer` hold, `transferFrom` points to finding 1. Six extra rows record the invariants above and the two static-analysis leads. Nothing in my area was left unreached.","treeHash":null,"usage":{"cachedInputTokens":412222,"inputTokens":226,"model":"claude-fable-5-1","outputTokens":12184,"runtime":"claude","turns":16,"wallClockMs":178313}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"91d3f9816008ce9f","findings":[{"citation":"resolved","description":"approve(spender, value) writes the new allowance unconditionally and the contract offers no increaseAllowance/decreaseAllowance or permit path, so an allowance can only be changed by an absolute overwrite. A spender who sees a pending approve that lowers their allowance can spend the current allowance first and then the new one. This is the standard ERC-20 approve race (inherent to the ERC-20 interface; OpenZeppelin v5 behaves the same way), so it is reported as low: a holder loses up to the previous allowance to a spender they already trusted with that amount. It does not affect the launch flows: the factory, distributor and PoolManager never rely on reducing an allowance. Minimal, design-preserving mitigation, if wanted: add decreaseAllowance(spender, subtractedValue) (and optionally increaseAllowance) so holders can shrink an allowance relative to its current value; otherwise document that holders must approve 0 and wait before approving a new nonzero amount.","line":38,"path":"src/QuorumToken.sol","reproduction":"State: deployer holds the supply. 1) deployer calls approve(alice, 100). 2) deployer broadcasts approve(alice, 50) intending to lower the allowance. 3) alice sees it in the mempool and front-runs with transferFrom(deployer, alice, 100): allowance 100 >= 100, succeeds, allowance becomes 0. 4) deployer's approve(alice, 50) lands, allowance becomes 50. 5) alice calls transferFrom(deployer, alice, 50). Expected by the deployer: alice can take at most 50 (or at most 100 if the reduction never applied). Actual: alice holds 150. Verified with a Foundry test on this tree (test/scratch/Leads.t.sol test_approveRace: balanceOf(alice) == 150).","severity":"low","snippet":"        allowance[msg.sender][spender] = value;","title":"approve() overwrites allowance in place, so a spender can front-run a reduction and spend old plus new allowance"},{"citation":"resolved","description":"_transfer rejects a zero receiver but not a zero sender, and transferFrom only checks allowance[from][msg.sender] >= value, which is 0 >= 0 for from == address(0) and value == 0. Any unprivileged account can therefore call transferFrom(address(0), anyAddress, 0) and the contract emits Transfer(address(0), anyAddress, 0), the same shape as the constructor's mint event. No balance or totalSupply changes (the nonzero case reverts on allowance first, then on balance), so there is no on-chain economic impact; the only effect is on off-chain consumers that classify Transfer-from-zero events as mints or that count 'minted' events for this fixed-supply token. OpenZeppelin v5's _transfer reverts with ERC20InvalidSender for from == address(0). If the author wants parity, add `if (from == address(0)) revert InvalidSender(from);` at the top of _transfer; this changes no legitimate flow because no real transfer originates from address(0).","line":56,"path":"src/QuorumToken.sol","reproduction":"State: freshly deployed QuorumToken. Call, from any account alice: token.transferFrom(address(0), bob, 0). Expected (ERC-20 reference behaviour, OZ v5): revert ERC20InvalidSender(address(0)). Actual: returns true and emits Transfer(address(0), bob, 0); totalSupply stays 1_000_000_000e18 and balanceOf(bob) stays 0. Verified with a Foundry test on this tree (test/scratch/Leads.t.sol test_phantomMintEventFromZeroAddress, vm.expectEmit on Transfer(address(0), bob, 0) passes). For contrast, transferFrom(address(0), bob, 1) from alice reverts with InsufficientAllowance(alice, 0, 1).","severity":"info","snippet":"        if (to == address(0)) revert InvalidReceiver(to);","title":"transferFrom(address(0), to, 0) succeeds and lets any caller emit a mint-shaped Transfer(address(0), to, 0) event"}],"hash":"a6a1fd669a4f33c9e179aa64637ae0b3f1e529b53ff068fdbf40ef96eca9b615","nodeId":"a492bac9-fd32-4dbd-9940-4e86b5408c48","outcome":"completed","summary":"The review is complete and written to `.imd-findings.json`. The JSON parses, both cited lines match the tree, the scratch tests are removed, and no repository file was changed.\n\n**Outcome:** the Quorum token holds against the Economic Security, Invariant and Flow Gap guides. No finding rises above low.\n\n**What I checked**\n- **Invariants.** Sum of balances equals total supply held under a Foundry invariant run of 128,000 fuzzed transfer, approve and transferFrom calls. Total supply is written once in the constructor and nothing else touches it. The unchecked arithmetic in `_transfer` and `transferFrom` is guarded by the preceding balance and allowance checks, and the receiver addition cannot overflow because all balances sum to 1e27.\n- **Economic security.** The contract makes no external calls and has no receive, fallback, assembly, libraries, owner, pause, blacklist, fee or burn. There is nothing to re-enter, break or freeze.\n- **Flow gaps.** Every launch flow moves exactly the stated amount and returns true, so the swarm share, the single-sided seed through the PoolManager, distributor claims and trader buys and sells all arrive whole.\n- **Entry points.** All three state-changing functions have coverage rows, plus five rows for the invariants and flows above.\n\n**Findings recorded**\n1. **Low.** The standard ERC-20 approve race. A spender who front-runs a reduction from 100 to 50 ends up with 150. This is inherent to the ERC-20 interface and does not touch any launch flow. I noted an optional `decreaseAllowance` as a design-preserving mitigation.\n2. **Info.** A zero-value `transferFrom` from the zero address succeeds and emits a mint-shaped Transfer event. Balances and supply are unaffected. OpenZeppelin rejects this sender, and a one-line check would match that.\n\nBoth aderyn leads were confirmed as non-defects: the large literal is the supply constant, and the mutable total supply has no writer after the constructor. Nothing in the assigned area was left unreached.","treeHash":null,"usage":{"cachedInputTokens":563696,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":12981,"runtime":"claude","turns":22,"wallClockMs":206276}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"9506ecec995bdbf9","findings":[{"citation":"resolved","description":"Boundary (sentinel address x zero input). _transfer rejects the zero address only as the receiver. The sender side has no sentinel check, and the balance and allowance guards are strict less-than comparisons, so when value == 0 both pass for from == address(0): balanceOf[address(0)] (0) is not < 0, and allowance[address(0)][msg.sender] (0) is not < 0. The call succeeds, returns true, and emits Transfer(address(0), to, 0), which is the shape of a mint. The balance and supply math is unaffected (nothing moves, totalSupply stays 1e27), but any unprivileged caller can emit unlimited zero-value Transfer(0x0, anyAddress, 0) events, which ERC-20 indexers, explorers and accounting tools conventionally classify as mints. The README states no mint exists; OpenZeppelin v5 ERC20 reverts this case with ERC20InvalidSender. Minimal fix that preserves intended behaviour: add `if (from == address(0)) revert InvalidSender(from);` at the top of _transfer (or in transferFrom), mirroring the existing receiver check.","line":56,"path":"src/QuorumToken.sol","reproduction":"State: fresh QuorumToken. Call from any EOA (e.g. 0xB0): token.transferFrom(address(0), 0xA1, 0). Expected: revert (no balance and no allowance from the zero address; a plain ERC-20 with no mint should never emit a Transfer from address(0) after construction). Actual: returns true and emits Transfer(from=0x0000000000000000000000000000000000000000, to=0xA1, value=0); totalSupply remains 1000000000000000000000000000 and all balances are unchanged. With value 1 the same call reverts with InsufficientAllowance(0xB0, 0, 1), confirming the zero-input edge is the only path. Verified with test test_transferFromZeroSenderEmitsMintShapedEvent in test/scratch/MathEdges.t.sol (passes on current code, i.e. the event is emitted).","severity":"low","snippet":"        if (to == address(0)) revert InvalidReceiver(to);","title":"transferFrom accepts from == address(0) with value 0 and emits a mint-shaped Transfer event"}],"hash":"a91eab4abe442d33faadcd059b5f0a6b350d12390d0aec4b098f34538ed1e553","nodeId":"ebf03c5e-dde4-4c7b-858c-828ec42b1869","outcome":"completed","summary":"The findings file is valid JSON, the cited snippet matches line 56 of the source, and the scratch test directory is the only thing I wrote outside it.\n\n**Result: one low-severity boundary finding, no math defects.** The Quorum token is a 60-line plain ERC-20 with no fixed-point math, decimals scaling, external calls, payable paths, downcasts or `abi.decode`, so most items in the Math Precision, Boundary and Numerical Gap guides have no instance to fail. I traced the remaining arithmetic and tested every edge the guides name.\n\n**What holds**\n- The unchecked subtraction in `_transfer` and `transferFrom` is always preceded by a strict less-than guard, so it cannot wrap.\n- The unchecked `balanceOf[to] += value` cannot overflow because the sum of balances is pinned to the 1e27 supply. A handler-driven Foundry invariant over four actors confirmed this across 256 runs.\n- Self-transfer, zero-value transfers, balance-minus-one, exact balance, balance-plus-one, `uint256.max` amounts, exact allowance, max-minus-one allowance and the infinite-allowance sentinel all behave as intended.\n- The constructor constant is exactly 1e27 and is minted once to `msg.sender`, matching the manifest supply.\n\n**The finding (low):** `transferFrom(address(0), anyone, 0)` succeeds from any caller and emits `Transfer(0x0, anyone, 0)`, a mint-shaped event, because the zero-address sentinel is checked only on the receiver and both guards are strict comparisons that zero passes. No value or supply changes, but indexers treat such events as mints and the README says no mint exists. The fix is a one-line sender check mirroring the receiver check.\n\n**Coverage:** all three listed entry points have rows, plus three invariant rows. Only the existing suite and my scratch tests ran; Slither and aderyn output had no medium-or-higher leads and the two low lines are gas and style, not defects. Nothing outside test/scratch/ and .imd-findings.json was written.","treeHash":null,"usage":{"cachedInputTokens":556774,"inputTokens":322,"model":"claude-fable-5-1","outputTokens":10166,"runtime":"claude","turns":11,"wallClockMs":154583}},{"artifacts":[],"attempt":1,"bundleHash":"e21db1163c9d630068db961c782e6247063805ebd8ff9b340b6f8b4b2dc65e02","device":"dedd4428da8fa41e","findings":[],"hash":"f3eea77c8f6b4024ce8118d4ec42d0d1dbe246abc7f247c03ea7eb507e4d85f6","nodeId":"05b4f4bc-eabc-4969-897a-6d128ec80c9e","outcome":"completed","summary":"I built the Quorum (QRM) token as `contract QuorumToken` in `src/QuorumToken.sol`. `forge build`, `forge test` (run with the verifier's fuzz seed; 11 tests pass) and `forge fmt --check` all pass. **It isn't committed:** git has no author name or email set on this machine and refused the commit, so I left the files staged rather than change git's configuration.\n\n- **Token:** a plain ERC-20, named Quorum with symbol QRM and 18 decimals. The constructor takes no arguments and mints all 1,000,000,000 QRM once to the deployer, which is the launch factory. There is no owner, mint, burn, pause, blacklist, fee or tax, so the supply can never grow and nobody can move or freeze someone else's balance. It makes no outside calls and uses no libraries.\n- **Small choices of mine:**\n  - An allowance set to the maximum value never goes down, so it works as unlimited approval.\n  - Transfers to the zero address revert, and so do approvals of the zero address.\n- **Tests:** `test/QuorumToken.t.sol` checks:\n  - name, symbol, decimals, supply, and the mint going to the deployer;\n  - transfers and allowance spending;\n  - failures for too little balance, too little allowance, a zero receiver and a zero spender;\n  - that no mint function exists;\n  - a fuzz test that transfers never change the total supply.\n\n  I couldn't run the launch's own protected tests here, because they rely on harness files and settings that only the verifier has.\n- **Setup:** `foundry.toml` pins solc 0.8.26 with `bytecode_hash = \"none\"` and the optimizer on. forge-std v1.9.4 is copied into `lib/forge-std` as plain files, not a submodule.\n- **Docs:** `README.md` covers the deployment values, assumptions and an \"After launch\" section, which is empty because the token has nothing to configure.\n\nStaged: `foundry.toml`, `.gitignore`, `src/`, `test/`, `lib/` and `README.md`.","treeHash":"c81b5eae9d3ab1a5e3888d2eafb55c2168aa13ce","usage":{"cachedInputTokens":256208,"inputTokens":14,"model":"claude-opus-5-5","outputTokens":4913,"runtime":"claude","turns":7,"wallClockMs":54865}}],"verification":[{"checks":[{"durationMs":1551,"exitCode":0,"name":"build","output":"Compiling 27 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.48s\nCompiler run successful!\n","passed":true},{"durationMs":11692,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 11 tests for test/QuorumToken.t.sol:QuorumTokenTest\n[PASS] testFuzz_transferConservesSupply(address,uint256) (runs: 256, μ: 98591, ~: 98638)\nLogs:\n  Bound result 100461111397670\n\n[PASS] test_approveAndTransferFrom() (gas: 139177)\n[PASS] test_approveZeroSpenderReverts() (gas: 32807)\n[PASS] test_constructorEmitsMint() (gas: 8107)\n[PASS] test_infiniteAllowanceNotDecremented() (gas: 121992)\n[PASS] test_metadataAndSupply() (gas: 49597)\n[PASS] test_noMintFunction() (gas: 37644)\n[PASS] test_transfer() (gas: 89915)\n[PASS] test_transferFromOverAllowanceReverts() (gas: 86798)\n[PASS] test_transferInsufficientReverts() (gas: 37398)\n[PASS] test_transferToZeroReverts() (gas: 32894)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 61.58ms (62.56ms CPU time)\n\nRan 46 tests for test/QuorumToken.edge.t.sol:QuorumTokenEdgeTest\n[PASS] testFuzz_approveIsIdempotentAndOverwrites(address,uint256,uint256) (runs: 1000, μ: 145467, ~: 145978)\n[PASS] testFuzz_finiteAllowanceDecrementsExactly(uint256,uint256) (runs: 1000, μ: 155125, ~: 157643)\nLogs:\n  Bound result 223380329305505582790221249558203827680898912421441328182234458196247\n  Bound result 875342044194460636198525009\n\n[PASS] testFuzz_infiniteAllowanceNeverDecrements(uint256) (runs: 1000, μ: 137940, ~: 138168)\nLogs:\n  Bound result 282411630104401232427192443\n\n[PASS] testFuzz_multiHopConservesSupply(uint256,uint256,uint256) (runs: 1000, μ: 264434, ~: 269097)\nLogs:\n  Bound result 10\n  Bound result 1\n  Bound result 0\n\n[PASS] testFuzz_roundTripRestoresBalances(address,uint256) (runs: 1000, μ: 114426, ~: 114375)\nLogs:\n  Bound result 125019\n\n[PASS] testFuzz_selfTransferIsIdentity(uint256,uint256) (runs: 1000, μ: 116772, ~: 117296)\nLogs:\n  Bound result 20937647609317992637890050\n  Bound result 6113651149860075527883392\n\n[PASS] testFuzz_transferFromOverAllowanceRevertsWithExactArgs(uint256,uint256) (runs: 1000, μ: 101601, ~: 101989)\nLogs:\n  Bound result 342591613449368337020937647951909606087258387\n  Bound result 2396314825832331763723381424946082978121749997\n\n[PASS] testFuzz_transferOverBalanceRevertsWithExactArgs(address,uint256,uint256) (runs: 1000, μ: 104199, ~: 104750)\nLogs:\n  Bound result 76947112036687513593194227\n  Bound result 115792089237316195423570985008687907853269984665640492859390994592309043073586\n\n[PASS] testFuzz_transferWithinBalanceMovesExactly(address,uint256,uint256) (runs: 1000, μ: 174437, ~: 177038)\nLogs:\n  Bound result 1000000000000000000\n  Bound result 244\n\n[PASS] test_allowanceIsDirectional() (gas: 102323)\n[PASS] test_approveDoesNotRequireBalance() (gas: 108075)\n[PASS] test_approveEmitsApproval() (gas: 67880)\n[PASS] test_approveOverwritesRatherThanAdds() (gas: 94955)\n[PASS] test_approveSelfIsAllowed() (gas: 130790)\n[PASS] test_approveZeroResetsAllowance() (gas: 127066)\n[PASS] test_approveZeroSpenderWithZeroValueStillReverts() (gas: 32836)\n[PASS] test_constantMatchesMintedSupply() (gas: 45379)\n[PASS] test_decimalsIsEighteen() (gas: 17262)\n[PASS] test_noAdminFunctionExists() (gas: 1187525)\n[PASS] test_runtimeHasNoDelegatecallCallcodeOrSelfdestruct() (gas: 1677371)\n[PASS] test_sameTransferTwiceMovesTwice() (gas: 117294)\n[PASS] test_secondDeploymentIsIndependent() (gas: 116704)\n[PASS] test_selfTransferLeavesBalanceUnchanged() (gas: 110862)\n[PASS] test_selfTransferOverBalanceReverts() (gas: 89785)\n[PASS] test_tokenRefusesEther() (gas: 71778)\n[PASS] test_transferEmitsExactEvent() (gas: 66268)\n[PASS] test_transferExactBalanceDrainsToZero() (gas: 112276)\n[PASS] test_transferFromAllowanceCheckPrecedesBalanceCheck() (gas: 87069)\n[PASS] test_transferFromEmitsTransferFromOwnerNotSpender() (gas: 116325)\n[PASS] test_transferFromExactAllowanceConsumesItFully() (gas: 164562)\n[PASS] test_transferFromInfiniteAllowanceCanDrainFullBalance() (gas: 142595)\n[PASS] test_transferFromInfiniteAllowanceStillChecksBalance() (gas: 97060)\n[PASS] test_transferFromOneUnderMaxAllowanceIsDecremented() (gas: 125439)\n[PASS] test_transferFromOverBalanceRevertsAndKeepsAllowance() (gas: 166910)\n[PASS] test_transferFromOwnBalanceWithoutSelfApprovalReverts() (gas: 37940)\n[PASS] test_transferFromRoundTripOwnerToOwner() (gas: 108748)\n[PASS] test_transferFromToSelfBySpenderIsAllowed() (gas: 117672)\n[PASS] test_transferFromToZeroRevertsAndKeepsAllowance() (gas: 107981)\n[PASS] test_transferFromWithoutApprovalReverts() (gas: 39941)\n[PASS] test_transferFromZeroWithoutApprovalSucceeds() (gas: 67953)\n[PASS] test_transferMaxUintReverts() (gas: 37820)\n[PASS] test_transferOneWeiOverBalanceReverts() (gas: 111653)\n[PASS] test_transferToTokenContractIsAllowedAndStuck() (gas: 102752)\n[PASS] test_transferZeroFromEmptyAccountSucceedsAndEmits() (gas: 62568)\n[PASS] test_transferZeroToZeroAddressStillReverts() (gas: 32870)\n[PASS] test_unknownSelectorReverts() (gas: 51540)\nSuite result: ok. 46 passed; 0 failed; 0 skipped; finished in 61.76ms (561.20ms CPU time)\n\nRan 1 test for test/QuorumToken.invariant.t.sol:QuorumTokenOpenInvariantTest\n[PASS]\nQuorumTokenOpenInvariantTest invariants:\n[PASS] invariant_deployerNeverExceedsSupply\n[PASS] invariant_supplyNeverChanges\n[PASS] invariant_zeroAddressNeverHoldsTokens\n QuorumTokenOpenInvariantTest invariants (runs: 256, calls: 16384, reverts: 10764)\n\n╭-------------+--------------+-------+---------+----------╮\n| Contract    | Selector     | Calls | Reverts | Discards |\n+=========================================================+\n| QuorumToken | approve      | 5508  | 35      | 0        |\n|-------------+--------------+-------+---------+----------|\n| QuorumToken | transfer     | 5426  | 5341    | 0        |\n|-------------+--------------+-------+---------+----------|\n| QuorumToken | transferFrom | 5450  | 5388    | 0        |\n╰-------------+--------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 251.72ms (250.42ms CPU time)\n\nRan 1 test for test/QuorumToken.invariant.t.sol:QuorumTokenInvariantTest\n[PASS]\nQuorumTokenInvariantTest invariants:\n[PASS] invariant_allowancesMatchGhostLedger\n[PASS] invariant_balancesMatchGhostLedger\n[PASS] invariant_flowsReconcile\n[PASS] invariant_metadataIsConstant\n[PASS] invariant_sumOfBalancesEqualsSupply\n[PASS] invariant_totalSupplyIsConstant\n[PASS] invariant_zeroAddressAndTokenHoldNothing\n QuorumTokenInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------+---------------------------+-------+---------+----------╮\n| Contract      | Selector                  | Calls | Reverts | Discards |\n+========================================================================+\n| QuorumHandler | approve                   | 2052  | 0       | 0        |\n|---------------+---------------------------+-------+---------+----------|\n| QuorumHandler | approveZeroSpender        | 2020  | 0       | 0        |\n|---------------+---------------------------+-------+---------+----------|\n| QuorumHandler | strangerCannotMove        | 2036  | 0       | 0        |\n|---------------+---------------------------+-------+---------+----------|\n| QuorumHandler | transfer                  | 2087  | 0       | 0        |\n|---------------+---------------------------+-------+---------+----------|\n| QuorumHandler | transferFrom              | 2086  | 0       | 0        |\n|---------------+---------------------------+-------+---------+----------|\n| QuorumHandler | transferFromOverAllowance | 2095  | 0       | 0        |\n|---------------+---------------------------+-------+---------+----------|\n| QuorumHandler | transferOverBalance       | 1954  | 0       | 0        |\n|---------------+---------------------------+-------+---------+----------|\n| QuorumHandler | transferToZero            | 2054  | 0       | 0        |\n╰---------------+---------------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 4\n  Bound result 3\n  Bound result 0\n  Bound result 5\n  Bound result 2\n  Bound result 0\n  Bound result 2\n  Bound result 3\n  Bound result 4\n  Bound result 0\n  Bound result 3\n  Bound result 0\n  Bound result 4\n  Bound result 5\n  Bound result 1\n  Bound result 0\n  Bound result 2\n  Bound result 5\n  Bound result 0\n  Bound result 5\n  Bound result 0\n  Bound result 3\n  Bound result 0\n  Bound result 0\n  Bound result 2\n  Bound result 4\n  Bound result 2\n  Bound result 3\n  Bound result 3\n  Bound result 4515\n  Bound result 1\n  Bound result 2\n  Bound result 3\n  Bound result 578211850632867393520163802078677499900\n  Bound result 3\n  Bound result 242\n  Bound result 3\n  Bound result 2\n  Bound result 5\n  Bound result 0\n  Bound result 4\n  Bound result 0\n  Bound result 2\n  Bound result 3\n  Bound result 5\n  Bound result 0\n  Bound result 1\n  Bound result 4\n  Bound result 0\n  Bound result 2\n  Bound result 822333294739597949471917823180105001410002\n  Bound result 4\n  Bound result 0\n  Bound result 5\n  Bound result 5\n  Bound result 1\n  Bound result 2\n  Bound result 1\n  Bound result 5\n  Bound result 2164\n  Bound result 3\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 301\n  Bound result 4\n  Bound result 208\n  Bound result 5\n  Bound result 4\n  Bound result 2\n  Bound result 4\n  Bound result 4\n  Bound result 1001\n  Bound result 1\n  Bound result 0\n  Bound result 1\n  Bound result 5\n  Bound result 0\n  Bound result 5\n  Bound result 2\n  Bound result 3\n  Bound result 7\n  Bound result 5\n  Bound result 5\n  Bound result 2\n  Bound result 0\n  Bound result 3\n  Bound result 1\n  Bound result 5\n  Bound result 3\n  Bound result 0\n  Bound result 4\n  Bound result 2\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129639933\n  Bound result 4\n  Bound result 3\n  Bound result 3\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129639934\n  Bound result 2\n  Bound result 0\n  Bound result 4\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 4\n  Bound result 5\n  Bound result 0\n  Bound result 0\n  Bound result 2\n  Bound result 0\n  Bound result 3\n  Bound result 0\n  Bound result 1\n  Bound result 1\n  Bound result 0\n  Bound result 4\n  Bound result 1000\n  Bound result 1\n  Bound result 0\n  Bound result 5\n  Bound result 3\n  Bound result 1\n  Bound result 22\n  Bound result 5\n  Bound result 2\n  Bound result 123\n  Bound result 5\n  Bound result 4\n  Bound result 5\n  Bound result 3\n  Bound result 3733035942434827233235290915161242343895080516605\n  Bound result 0\n  Bound result 536\n  Bound result 4\n  Bound result 3\n  Bound result 0\n  Bound result 1\n  Bound result 4\n  Bound result 6959\n  Bound result 4\n  Bound result 0\n  Bound result 5\n  Bound result 0\n  Bound result 1\n  Bound result 86803\n  Bound result 2\n  Bound result 0\n  Bound result 2\n  Bound result 3\n  Bound result 1\n  Bound result 5\n  Bound result 3\n  Bound result 0\n  Bound result 3\n  Bound result 3\n  Bound result 2\n  Bound result 9339\n  Bound result 4\n  Bound result 10006\n  Bound result 2\n  Bound result 5\n  Bound result 2\n  Bound result 0\n  Bound result 4\n  Bound result 5\n  Bound result 2\n  Bound result 5\n  Bound result 5\n  Bound result 5\n  Bound result 0\n  Bound result 1\n  Bound result 4\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 5\n  Bound result 4\n  Bound result 5\n  Bound result 14398244246782556433137843604394225532704444733549521989\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 11.62s (11.61s CPU time)\n\nRan 4 test suites in 11.62s (11.99s CPU time): 59 tests passed, 0 failed, 0 skipped (59 total tests)\n","passed":true},{"durationMs":39,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"QuorumToken.approve(address,uint256)\",\"QuorumToken.transfer(address,uint256)\",\"QuorumToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":2,\"README.md\":26,\"foundry.toml\":10,\"src/QuorumToken.sol\":65,\"test/QuorumToken.edge.t.sol\":549,\"test/QuorumToken.invariant.t.sol\":304,\"test/QuorumToken.t.sol\":98},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"09ae2835e73992d2876b722ed7e34fdf1f2047be34443d77d791844b89f9400a","verifiedTreeHash":"f889829f6fdaadd19986d60854400c2258571c28","verifierVersion":"0.1.0+d952fceb"},{"checks":[{"durationMs":828,"exitCode":0,"name":"build","output":"Compiling 25 files with Solc 0.8.26\nSolc 0.8.26 finished in 755.58ms\nCompiler run successful!\n","passed":true},{"durationMs":83,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 11 tests for test/QuorumToken.t.sol:QuorumTokenTest\n[PASS] testFuzz_transferConservesSupply(address,uint256) (runs: 256, μ: 98188, ~: 98638)\nLogs:\n  Bound result 324\n\n[PASS] test_approveAndTransferFrom() (gas: 139177)\n[PASS] test_approveZeroSpenderReverts() (gas: 32807)\n[PASS] test_constructorEmitsMint() (gas: 8107)\n[PASS] test_infiniteAllowanceNotDecremented() (gas: 121992)\n[PASS] test_metadataAndSupply() (gas: 49597)\n[PASS] test_noMintFunction() (gas: 37644)\n[PASS] test_transfer() (gas: 89915)\n[PASS] test_transferFromOverAllowanceReverts() (gas: 86798)\n[PASS] test_transferInsufficientReverts() (gas: 37398)\n[PASS] test_transferToZeroReverts() (gas: 32894)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 3.58ms (5.22ms CPU time)\n\nRan 1 test suite in 4.40ms (3.58ms CPU time): 11 tests passed, 0 failed, 0 skipped (11 total tests)\n","passed":true},{"durationMs":37,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"QuorumToken.approve(address,uint256)\",\"QuorumToken.transfer(address,uint256)\",\"QuorumToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":2,\"README.md\":26,\"foundry.toml\":10,\"launch.json\":24,\"src/QuorumToken.sol\":65,\"test/QuorumToken.t.sol\":98},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"35d494972a1ff32c53a0237867cef5274049d345b03d3dadcff1ccd55712e566","verifiedTreeHash":"5ffbe0212d9b960cac1c18efa7f9767274d5e36c","verifierVersion":"0.1.0+d952fceb"},{"checks":[{"durationMs":979,"exitCode":0,"name":"build","output":"Compiling 25 files with Solc 0.8.26\nSolc 0.8.26 finished in 865.90ms\nCompiler run successful!\n","passed":true},{"durationMs":84,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 11 tests for test/QuorumToken.t.sol:QuorumTokenTest\n[PASS] testFuzz_transferConservesSupply(address,uint256) (runs: 256, μ: 98188, ~: 98638)\nLogs:\n  Bound result 39675166645420999771658\n\n[PASS] test_approveAndTransferFrom() (gas: 139177)\n[PASS] test_approveZeroSpenderReverts() (gas: 32807)\n[PASS] test_constructorEmitsMint() (gas: 8107)\n[PASS] test_infiniteAllowanceNotDecremented() (gas: 121992)\n[PASS] test_metadataAndSupply() (gas: 49597)\n[PASS] test_noMintFunction() (gas: 37644)\n[PASS] test_transfer() (gas: 89915)\n[PASS] test_transferFromOverAllowanceReverts() (gas: 86798)\n[PASS] test_transferInsufficientReverts() (gas: 37398)\n[PASS] test_transferToZeroReverts() (gas: 32894)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 4.79ms (6.68ms CPU time)\n\nRan 1 test suite in 5.40ms (4.79ms CPU time): 11 tests passed, 0 failed, 0 skipped (11 total tests)\n","passed":true},{"durationMs":35,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"QuorumToken.approve(address,uint256)\",\"QuorumToken.transfer(address,uint256)\",\"QuorumToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":2,\"README.md\":26,\"foundry.toml\":10,\"src/QuorumToken.sol\":65,\"test/QuorumToken.t.sol\":98},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":618,"exitCode":0,"name":"slither","output":"slither: no results at low impact or above","passed":true},{"durationMs":315,"exitCode":0,"name":"aderyn","output":"[low] large-numeric-literal at src/QuorumToken.sol:11: Large Numeric Literal\n[low] state-variable-could-be-immutable at src/QuorumToken.sol:13: State Variable Could Be Immutable","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"f3eea77c8f6b4024ce8118d4ec42d0d1dbe246abc7f247c03ea7eb507e4d85f6","verifiedTreeHash":"c81b5eae9d3ab1a5e3888d2eafb55c2168aa13ce","verifierVersion":"0.1.0+d952fceb"}]}