{"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":"22f2418a-3937-4933-b153-9ddbfd5eae58","kind":"audit","nodes":[{"acceptedSubmissionHash":"edb46dd1ba05cd8b3c9f42a7b922833716eebff832943d00f945b19bfec33ea0","dependsOn":[],"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":"f2ac3cf616171924033d550ace54843599120f5545a441f020940dde1137011a","dependsOn":[],"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":"cba3408a9b46bf241b3e3ff2cac841baaa0acbfbf4b090ed0b89f4acce7f9bd8","dependsOn":["audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"6dc8376d6243ecb079c11f1e93f795dd31a137180e06d3385cd968e4a18e454d","dependsOn":[],"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":"31925e02a2a81e9743f7ac76c4e3d9624e6ee5cfaf21fa306e7930645c254cea","dependsOn":[],"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"}],"objective":"Audit the SigilNFT contract in this repository (src/SigilNFT.sol, 142 lines, Solidity 0.8.26, OpenZeppelin v5 submodule lib/openzeppelin-contracts at fcbae5394ae8ad52d8e580a3477db99814b9d565, forge-std at bf647bd6046f2f7da30d0c2bf435e5c76a780c1b). It is the \"Illuminati.Earth Magik Sigil\" ERC-721 collection (name constant \"Illuminati.Earth Magik Sigil\" in script/DeploySigilNFT.s.sol; confirm the README, deploy script, tests and integration guide all use exactly that name), intended for a real Base mainnet deploy: 2,300 max supply, free mint (gas only), fixed 10% EIP-2981 royalty to a treasury set once at deploy, mint and rework gated by EIP-712 vouchers signed by an owner-rotatable signer. Write nothing to the repository; deliver report.md. REPORT BRANDING: this review is published as part of Illuminati.Earth marketing. Title report.md \"Illuminati.Earth Magik Sigil: Contract Review\" and call the collection \"Illuminati.Earth Magik Sigil\" (exactly that spelling and capitalisation) everywhere in the report. Keep the tone factual and plain: no hype, no claims of safety beyond the evidence, no investment language, and no statement that the contract is \"audited\" or \"secure\".\n\nSCOPE: src/SigilNFT.sol, script/DeploySigilNFT.s.sol, test/SigilNFT.t.sol (27 tests), README.md and docs/INTEGRATION.md. Check that the README and integration guide match the code exactly.\n\nHARD QUESTIONS (answer each with a verdict and evidence, a reproducible test where possible):\n1. Voucher replay and binding: can a mint or rework voucher be redeemed by anyone except the named caller? Across chains, across contract deployments, or after a signer rotation? Is the EIP-712 domain (name \"SigilNFT\", version \"1\", chainId, verifyingContract) correct, and are the MINT and REWORK typehashes exactly consistent with the encoded structs, including the dynamic string tokenURI hashed with keccak256(bytes(...))? Any signature malleability or ECDSA edge case (zero address recovery, s-value, 65 vs 64-byte signatures)?\n2. Supply and sigil mapping: can supply exceed MAX_SUPPLY (2300)? Is the ordering of the _nextTokenId check, the tokenOfSigil check and the writes safe? Can a sigilId be minted twice, or a token be reissued after transfer? Is tokenOfSigil == 0 a safe \"unminted\" sentinel given token ids start at 1?\n3. Reentrancy: _safeMint calls onERC721Received on the recipient. State is written before the call; confirm no reentrant path through mint or rework can mint extra supply, reuse a voucher, or corrupt tokenVersion or tokenURI.\n4. Rework logic: tokenVersion strictly increases; can a version be skipped or reused to brick or hijack a token's metadata? Does the holder-only check (_ownerOf) behave for non-existent tokens? Is MetadataUpdate (ERC-4906) emitted on mint and rework, and does supportsInterface advertise ERC-4906, ERC-2981 and ERC-721 correctly through the multiple-inheritance override?\n5. Owner and signer powers: confirm owner can only call setSigner, ownership transfer and renounce behave as OpenZeppelin v5 Ownable2-less Ownable (single-step) and the risks that creates; the signer cannot touch tokens it does not hold. State what a compromised signer can and cannot do.\n6. Royalty: _setDefaultRoyalty(treasury, 1000) with an unchecked treasury argument; what if treasury is the zero address (it reverts in ERC2981, confirm). Any way to change royalty after deploy?\n7. Deadline and timing: block.timestamp > deadline comparison, voucher expiry edge cases, deadlines near uint256 max.\n8. Gas and DoS: unbounded strings (tokenURI length), large calldata, any griefing of mint for other users.\n9. Deploy script and tests: does the deploy script pass the right arguments, does the test suite genuinely cover the risks above, and what is missing?\n\nMETHOD: use the Pashov methodology and specialties. Reproduce every finding against the code; discard unreproducible claims. Rate each finding by severity and likelihood, give a concrete fix, and separate real defects from design trade-offs already documented in the README threat model. Do not claim this review is a substitute for an independent human audit.","parentJobId":null,"planHash":"d3abd18c98ce8613fead4bc8afe4e5b0d91272bd1826a59db3a1d38b201dbe04","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"22f2418a-3937-4933-b153-9ddbfd5eae58","publication":{"commit":null,"deliveredAt":null,"repoUrl":null},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"50971","feedbackHash":"b61f6eecf3795ccaa4cf415ba376f9168768881c2cd3bc9959e75cf67edc174a","nodeKey":"audit_economics","submissionHash":"edb46dd1ba05cd8b3c9f42a7b922833716eebff832943d00f945b19bfec33ea0","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50962","feedbackHash":"91b0b8f732bfeb26044bac76d653fb4dacf253c684b1704c25d366a5c9c6c183","nodeKey":"audit_flow","submissionHash":"f2ac3cf616171924033d550ace54843599120f5545a441f020940dde1137011a","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51725","feedbackHash":"37ed490492cead22cb19b0bea8b66eea8ede45c703c3f29589ec102446cfc22a","nodeKey":"audit_judge","submissionHash":"cba3408a9b46bf241b3e3ff2cac841baaa0acbfbf4b090ed0b89f4acce7f9bd8","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50956","feedbackHash":"2cc9e6fcc6beef1e2f9cbee8a8c63509157e633dfb07fd70fc0dd527bc75257e","nodeKey":"audit_math","submissionHash":"6dc8376d6243ecb079c11f1e93f795dd31a137180e06d3385cd968e4a18e454d","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51226","feedbackHash":"3b3e7c4c830ee56649c4f8825fdf890cceffc441ae2c1b7901658f98ac838905","nodeKey":"audit_permissions","submissionHash":"31925e02a2a81e9743f7ac76c4e3d9624e6ee5cfaf21fa306e7930645c254cea","tag1":"review:submission","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"2b81bbe31c250a9eef14f59d122247a360e269e2613e2b65c454d3485eb1de6a","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"720122d0ca9f60ca","findings":[{"citation":"resolved","description":"For Illuminati.Earth Magik Sigil, an on-chain read sees only confirmed tokenVersion state; it does not reserve a version for an outstanding voucher. Two requests made before either transaction lands can both read version 1 and receive different, valid version-2 vouchers. After either is redeemed, src/SigilNFT.sol:113 rejects the other with StaleVersion. This is a documentation/integration defect, not a contract permission bypass: the strict-increase guard works as implemented. Impact is a rejected legitimate rework, wasted gas if submitted, and a need to reissue the voucher; any off-chain payment/refund effect depends on the out-of-scope backend. Likelihood: medium when overlapping requests are supported. Fix the guide to require one outstanding rework per token with serialized issuance through confirmation, or an atomic pending-version allocator plus explicit ordering, supersession and reissue behavior; allocating distinct versions alone does not make out-of-order execution succeed.","line":116,"path":"docs/INTEGRATION.md","reproduction":"Executed against Foundry-compiled repository bytecode on a local Anvil chain with chainId 8453. Deploy with a nonzero treasury and signer; mint sigilId=bytes32(uint256(1)) to Alice with version=1. Before submitting either rework, read tokenVersion(1) twice: both reads return 1. Using the current signer and EIP-712 domain {name:'SigilNFT',version:'1',chainId:8453,verifyingContract:deployed address}, sign Rework(Alice,1,'ipfs://request-one',2,type(uint256).max) and Rework(Alice,1,'ipfs://request-two',2,type(uint256).max). Alice redeems the first successfully, setting tokenVersion(1)=2. Alice then submits the second unchanged: it reverts with StaleVersion() at src/SigilNFT.sol:113. Expected from the guide: these on-chain reads avoid the collision; actual: the second distinct signed request is unusable until reissued with a higher version.","severity":"low","snippet":"- Use `version` strictly greater than `tokenVersion(tokenId)`. Reading it on-chain avoids collisions\n  if a user requests two reworks before the first lands.","title":"Integration guide incorrectly promises that an on-chain version read prevents concurrent rework collisions"}],"hash":"31925e02a2a81e9743f7ac76c4e3d9624e6ee5cfaf21fa306e7930645c254cea","nodeId":"bfebf497-1c43-4423-bffe-8dfd8d8f23cb","outcome":"completed","summary":"# Illuminati.Earth Magik Sigil: Contract Review\n\n**One low-severity documentation defect was reproduced.** No permission bypass or supply-cap violation was reproduced.\n\nFindings are saved in [.imd-findings.json](/Users/hhyy/.lobster_wallets/identitymd/work/22f2418a-3937-4933-b153-9ddbfd5eae58/bfebf497-1c43-4423-bffe-8dfd8d8f23cb/.imd-findings.json). Tracked source and configuration files remain unchanged.\n\nReviewed commit: `e466fcb14c1c7d53b39b2c01a5dc742dc216a62f`. Scope covered the five requested files and relevant inherited implementations. Both dependency commits match the brief. Review used the supplied Pashov checklists, emphasizing Access Control, Trust Gap and Asymmetry, plus entry-point enumeration and applicable boundary checks.\n\n`forge test --offline --no-cache -vv` passed **27/27 tests** with Solidity 0.8.26. Additional checks ran against compiled bytecode on local Anvil, and the deployment script completed a local simulation using synthetic inputs.\n\n**Finding — Low severity; medium likelihood: concurrent rework guidance is incorrect.**\n\nAt [docs/INTEGRATION.md:116](/Users/hhyy/.lobster_wallets/identitymd/work/22f2418a-3937-4933-b153-9ddbfd5eae58/bfebf497-1c43-4423-bffe-8dfd8d8f23cb/docs/INTEGRATION.md:116), the guide says reading the on-chain version prevents collisions between pending requests.\n\nReproduction: Alice owns token `1`, currently version `1`. Two requests read that state before either transaction lands. The signer issues distinct vouchers for `ipfs://request-one` and `ipfs://request-two`, both version `2`. Redeeming the first succeeds; redeeming the second reverts with `StaleVersion()` at contract line 113. This was reproduced locally.\n\nThe consequence is a rejected legitimate request, possible wasted transaction gas, and voucher reissuance. Any payment consequence depends on the unreviewed backend.\n\n**Fix:** document serialized issuance with one outstanding rework per token, or atomic version allocation with explicit ordering, supersession and reissue handling. Distinct version allocation alone does not guarantee successful execution out of order.\n\n1. **Voucher replay and binding — correct within the domain, with revocation caveats.**\n\n   Contract lines 88 and 116 encode `msg.sender`, the relevant identifier, the URI hash, version and deadline. Both typehash declarations exactly match their encoded field order and types. Dynamic strings correctly use `keccak256(bytes(tokenURI_))`.\n\n   OpenZeppelin constructs the domain using name `\"SigilNFT\"`, version `\"1\"`, current `chainId` and `address(this)`. This technical domain name intentionally differs from the collection name. Local checks rejected redemption by another caller, on another deployment, and after changing the chain ID; signatures for the changed chain then worked.\n\n   Rotation rejects the previous signer’s vouchers while a different signer is configured. **Restoring signer A after A→B→A reactivates unused, unexpired A vouchers**, subject to sigil/version checks; this was reproduced. Rotation has no permanent revocation epoch. Likewise, an unused holder voucher can become usable after ownership returns to that holder.\n\n   ECDSA rejects high-`s` signatures, invalid recovery, invalid `v`, and malformed lengths. The selected `bytes` overload accepts 65-byte signatures and rejects 64-byte ERC-2098 signatures. These cases were exercised locally. ERC-1271 validation is unsupported. Identical chain IDs and contract addresses on duplicated chain histories remain outside domain separation’s protection.\n\n2. **Supply and sigil mapping — cap and uniqueness enforced.**\n\n   Lines 84–85 check sigil occupancy before the supply cap. Lines 92–96 increment the counter and write the mapping, version and URI before the callback. There is no external interaction between validation and these writes.\n\n   IDs start at `1`, so mapping value `0` is an unambiguous unminted sentinel. Even `sigilId = bytes32(0)` works correctly, as confirmed locally. Transfers never clear th","treeHash":null,"usage":{"cachedInputTokens":1868416,"inputTokens":129408,"model":"gpt-6-astra","outputTokens":20883,"runtime":"codex","turns":6,"wallClockMs":952748}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"bb0a3bf63233e5e5","findings":[{"citation":"resolved","description":"The Illuminati.Earth Magik Sigil version invariant has a reachable terminal state. mint accepts every nonzero uint256 version (lines 83 and 95), and rework accepts any larger version and stores it verbatim (lines 113 and 120). A correctly signed voucher with version = 2**256 - 1 therefore permanently disables rework: no representable future version can satisfy the guard. Transferring the token and rotating the signer do not reset its version, so a subsequent holder also loses the documented ability to rework. This is distinct from the documented ability to change metadata before sale: it irreversibly removes the next holder's metadata-update capability. Likelihood: low; the current signer must explicitly authorize this extreme value and the named holder must redeem it. There is no signature bypass or way for a signer alone to freeze someone else's token. Concrete fix: require mint version 1 and rework version exactly tokenVersion[tokenId] + 1, making saturation infeasible through normal operation. This intentionally removes arbitrary version jumps; if jumps must remain supported, use a separately incremented authorization nonce and treat the signed metadata version as non-authorizing data. Add maximum-version regression tests and make the backend allocate versions rather than accepting an unrestricted client value.","line":113,"path":"src/SigilNFT.sol","reproduction":"Reproduced offline with Solidity 0.8.26 against the pinned dependencies in test_maxMintVersionPermanentlyBlocksReworkAfterTransfer and test_maxReworkVersionPermanentlyBlocksReworkAfterTransfer. On chainId 8453 at timestamp 100, deploy with signer vm.addr(0xA11CE) and a nonzero treasury. Sign the exact documented EIP-712 Mint for minter 0x1111, sigilId bytes32(0), tokenURI \"first\", version type(uint256).max, deadline 10000 using key 0xA11CE; redeem as 0x1111. Token 1 is created at the maximum version. Transfer token 1 to 0x2222. Sign Rework(holder=0x2222, tokenId=1, tokenURI=\"next\", version=type(uint256).max, deadline=10000) with the authorized signer and submit as 0x2222. Actual: StaleVersion; every other uint256 version is smaller and also rejected. Expected under the documented repeatable rework behavior: the terminal version is prevented before it can disable the next holder's updates. The same final state is reached by minting version 1 and redeeming a signed rework at version type(uint256).max before transferring.","severity":"low","snippet":"        if (version <= tokenVersion[tokenId]) revert StaleVersion();","title":"A maximum voucher version permanently disables rework, including for later holders"},{"citation":"resolved","description":"The Illuminati.Earth Magik Sigil integration guide tells the backend that reading tokenVersion on-chain avoids collisions between reworks requested before the first transaction lands. Both reads return the same applied version while both vouchers are pending, so a backend using currentVersion + 1 can sign two different URIs at the same next version. Redeeming either voucher permanently invalidates the other. The contract correctly rejects the stale voucher; the defect is the integration guidance, which can cause a user to receive an unusable voucher, incur failed-transaction gas, and require reissuance after paying the off-chain rework charge described at line 115. Likelihood: medium when a holder makes concurrent requests or retries before inclusion. Fix the guide to require serialized per-token issuance/redemption or atomic pending-version reservation with explicit ordering, replacement and stale-voucher reissuance rules. An on-chain read alone is insufficient, and allocating distinct versions alone still permits a higher version to invalidate a lower pending one.","line":116,"path":"docs/INTEGRATION.md","reproduction":"Reproduced offline in test_pendingVersionsCollideDespiteChainReads. At timestamp 100, mint token 1 to 0x1111 at version 1. Request A reads tokenVersion(1)=1 and signs Rework(holder=0x1111, tokenId=1, tokenURI=\"request-a\", version=2, deadline=10000). Before A is mined, request B independently reads tokenVersion(1)=1 and signs the same fields with tokenURI=\"request-b\" and version=2. Both use the current signer and the correct EIP-712 domain. Submit A as 0x1111: succeeds and stores version 2. Submit B as 0x1111 before deadline: reverts StaleVersion. Expected from the quoted guide: reading on-chain prevents the collision. Actual: two independent on-chain reads still produce colliding signed versions.","severity":"low","snippet":"- Use `version` strictly greater than `tokenVersion(tokenId)`. Reading it on-chain avoids collisions\n  if a user requests two reworks before the first lands.","title":"Integration guidance incorrectly claims an on-chain version read prevents pending rework collisions"}],"hash":"6dc8376d6243ecb079c11f1e93f795dd31a137180e06d3385cd968e4a18e454d","nodeId":"2eb9dbff-f768-48a0-8db3-ee932b01ed01","outcome":"completed","summary":"Completed [report.md](/tmp/report.md) and [.imd-findings.json](/home/imd-worker/.identitymd/work/22f2418a-3937-4933-b153-9ddbfd5eae58/2eb9dbff-f768-48a0-8db3-ee932b01ed01/.imd-findings.json).\n\nTwo low-severity findings:\n\n- Signer-approved maximum versions permanently disable future reworks.\n- Integration guidance incorrectly claims on-chain reads prevent pending-version collisions.\n\nAll 27 existing tests and 26 additional review tests passed, including 768 fuzz cases. The review tests reproduced both defects. Source, configuration, and dependencies remain unchanged.","treeHash":null,"usage":{"cachedInputTokens":1172480,"inputTokens":121084,"model":"gpt-6-astra","outputTokens":18204,"runtime":"codex","turns":6,"wallClockMs":775999}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"004eae350f695d24","findings":[{"citation":"resolved","description":"Illuminati.Earth Magik Sigil accepts any nonzero uint256 version at mint (lines 83 and 95), and any larger version at rework (lines 113 and 120). A valid voucher can therefore set tokenVersion to 2**256 - 1, leaving no representable version for any later rework. Transfers and signer rotation cannot repair this state. This contradicts the repeatable rework behavior described at README.md lines 23-26 and persists for a subsequent holder; the README threat model discusses changing art before sale but does not disclose irreversible loss of rework capability. Severity: low. Likelihood: low, because the current signer must approve this extreme version and the named holder must submit it. This is a signer-dependent input/liveness defect, not an authorization bypass or a way for the signer alone to alter another holder's token. Fix: require mint version 1 and rework version exactly the current version plus 1, so saturation cannot be reached by an arbitrary jump, and allocate versions in the backend. That fix changes the currently permitted version-jump policy and should be explicit. If arbitrary metadata versions must remain, separate them from an on-chain sequential authorization nonce and define their saturation semantics.","line":113,"path":"src/SigilNFT.sol","reproduction":"Executed with Foundry 1.8.3, Solidity 0.8.26, and the pinned dependencies in /tmp/sigil-judge-tests/Reproductions.t.sol: test_maxMintVersionDisablesReworkAfterTransferAndRotation and test_maxReworkVersionDisablesReworkAfterTransfer. Set chainId=8453 and timestamp=100. Deploy with name Illuminati.Earth Magik Sigil, symbol SIGIL, treasury=address(0x777), signer=vm.addr(42). Using key 42 and domain {name:SigilNFT,version:1,chainId:8453,verifyingContract:address(nft)}, sign Mint(address(0x1111),bytes32(0),first,2**256-1,10000), hashing the URI with keccak256(bytes(uri)); redeem as 0x1111. Transfer token 1 to 0x2222 and rotate the signer to vm.addr(43). Sign Rework(0x2222,1,next,2**256-1,10000) with key 43 and redeem as 0x2222: StaleVersion(). Every other uint256 version is smaller and fails the same guard. tokenVersion stays at the maximum and tokenURI stays first. A second executed test reaches the same terminal state by minting at version 1, applying a valid rework at the maximum, transferring, then attempting another valid maximum-version rework. Expected: a voucher cannot irreversibly eliminate a later holder's rework capability without an explicit freeze feature; actual: both mint and rework can establish this permanent terminal state.","severity":"low","snippet":"        if (version <= tokenVersion[tokenId]) revert StaleVersion();","title":"A maximum voucher version permanently prevents subsequent rework"},{"citation":"resolved","description":"The Illuminati.Earth Magik Sigil integration guide incorrectly claims that reading tokenVersion on-chain avoids collisions between reworks requested before the first transaction lands. Reads do not reserve a version: concurrent requests can both read 1 and receive valid, distinct version-2 vouchers. Once either is redeemed, the contract correctly rejects the other with StaleVersion(). Severity: low; likelihood: medium where overlapping requests or retries are allowed. Impact is an unusable legitimate voucher, gas expense if a reverting transaction is submitted, and required reissuance. No backend implementation or actual off-chain payment loss was established. This is a documentation/integration defect, not a permission bypass. Fix: describe the race and serialize issuance per token through confirmation or expiry, or atomically reserve pending versions with explicit ordering, supersession, stale-voucher reissuance and reorg handling. Distinct allocated versions alone do not solve out-of-order redemption because a higher version invalidates a lower one. The four specialist reports of this mechanism are merged here.","line":116,"path":"docs/INTEGRATION.md","reproduction":"Executed in test_concurrentVersionReadsDoNotReserveVersions in /tmp/sigil-judge-tests/Reproductions.t.sol with Foundry 1.8.3 and Solidity 0.8.26. Set chainId=8453, timestamp=100, treasury=0x777 and signer=vm.addr(42); deploy with name Illuminati.Earth Magik Sigil and symbol SIGIL. Mint sigilId=bytes32(uint256(123)) to Alice=0x1111 with version=1, URI first and deadline=10000. Before either rework is submitted, independently compute versionA=tokenVersion(1)+1 and versionB=tokenVersion(1)+1; both equal 2. Using key 42 and the correct deployed EIP-712 domain, sign Rework(Alice,1,ipfs://request-a,2,10000) and Rework(Alice,1,ipfs://request-b,2,10000). Alice submits A, then B at timestamp 100. Expected according to the quoted guide: the on-chain reads avoid a collision. Actual: A succeeds; B reverts StaleVersion(); tokenVersion(1) remains 2 and tokenURI(1) remains ipfs://request-a.","severity":"low","snippet":"- Use `version` strictly greater than `tokenVersion(tokenId)`. Reading it on-chain avoids collisions\n  if a user requests two reworks before the first lands.","title":"On-chain version reads do not prevent concurrent rework voucher collisions"}],"hash":"cba3408a9b46bf241b3e3ff2cac841baaa0acbfbf4b090ed0b89f4acce7f9bd8","nodeId":"814ea9ef-7d00-4199-b0c6-acd53fd1da4b","outcome":"completed","summary":"Delivered [report.md](/tmp/sigil-judge-report/report.md) and [.imd-findings.json](/root/nft3/.identitymd/work/22f2418a-3937-4933-b153-9ddbfd5eae58/814ea9ef-7d00-4199-b0c6-acd53fd1da4b/.imd-findings.json).\n\nTwo reproduced low-severity findings:\n\n- Maximum voucher version permanently prevents rework.\n- On-chain reads do not prevent concurrent voucher collisions.\n\nAll 27 original tests and 21 additional checks passed. Project source and configuration remain unchanged.","treeHash":null,"usage":{"cachedInputTokens":1379584,"inputTokens":122393,"model":"gpt-6-astra","outputTokens":18833,"runtime":"codex","turns":6,"wallClockMs":821399}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"35c52a5b502e847c","findings":[{"citation":"resolved","description":"The integration guide for Illuminati.Earth Magik Sigil incorrectly says reading tokenVersion on-chain avoids collisions when two reworks are requested before the first transaction lands. A read does not reserve a version: both requests can read the same value and receive valid vouchers with the same next version. SigilNFT.rework correctly rejects the second redemption at src/SigilNFT.sol:113. An integrator following this guidance can issue an unusable voucher and cause a reverted transaction (and gas expense if submitted). Likelihood: medium when concurrent requests are allowed; no backend implementation or actual off-chain payment loss was established. This is a documentation/integration defect, not an authorization bypass. Fix: document the race and serialize issuance per token until redemption/expiry, or atomically reserve versions while handling out-of-order redemption and reissuing stale vouchers. Merely assigning distinct versions is insufficient if a higher version lands first.","line":116,"path":"docs/INTEGRATION.md","reproduction":"Reproduced offline with Solidity 0.8.26 / Foundry 1.8.3 in test_concurrentVersionReadDoesNotReserveVersion. Set chainId=8453 and timestamp=100. Deploy with name Illuminati.Earth Magik Sigil, symbol SIGIL, treasury=address(0x777), signer=vm.addr(42). Mint sigilId=bytes32(uint256(123)) to Alice=address(0xA11CE) using a valid version=1 voucher with deadline=1000. Before submitting any rework, read tokenVersion(1) independently for requests A and B: both return 1. Sign two EIP-712 Rework vouchers using key 42 and the deployed contract domain, both for holder Alice, tokenId=1, version=2, deadline=1000, with tokenURI ipfs://request-a and ipfs://request-b respectively. Alice redeems A, then B. Expected according to the guide: the on-chain reads avoid a version collision. Actual: A succeeds and sets version=2; B reverts StaleVersion(); tokenURI remains ipfs://request-a. Reversing the submission order makes A stale instead.","severity":"low","snippet":"- Use `version` strictly greater than `tokenVersion(tokenId)`. Reading it on-chain avoids collisions\n  if a user requests two reworks before the first lands.","title":"On-chain version reads do not prevent concurrent rework voucher collisions"}],"hash":"edb46dd1ba05cd8b3c9f42a7b922833716eebff832943d00f945b19bfec33ea0","nodeId":"d0f021fc-b553-4f3a-95f4-3f04761916f4","outcome":"completed","summary":"Completed [report.md](/tmp/report.md), covering all nine questions.\n\nOne low-severity finding: the integration guide incorrectly claims on-chain version reads prevent concurrent voucher collisions. Recorded in [.imd-findings.json](/home/identitymd5/.identitymd/work/22f2418a-3937-4933-b153-9ddbfd5eae58/d0f021fc-b553-4f3a-95f4-3f04761916f4/.imd-findings.json).\n\nAll 27 existing tests and 28 supplemental tests passed. No contract defect was substantiated. Source and configuration files remain unchanged.","treeHash":null,"usage":{"cachedInputTokens":1693824,"inputTokens":140630,"model":"gpt-6-astra","outputTokens":22377,"runtime":"codex","turns":7,"wallClockMs":974131}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"3f6a9bdd601cb99f","findings":[{"citation":"resolved","description":"The integration guide for Illuminati.Earth Magik Sigil says that reading tokenVersion on-chain avoids collisions when two reworks are requested before the first lands. Both requests can read the same confirmed version and both be signed with that version plus one. The contract correctly rejects the second voucher after the first executes, contradicting the documented backend guarantee. Severity: low; likelihood: medium when requests overlap. This is an integration-guidance defect, not an authorization bypass. Fix: serialize outstanding reworks per token until confirmation, or atomically reserve pending versions in the backend and explicitly handle out-of-order redemption, stale vouchers, replacement and reorgs. A fresh on-chain read alone is insufficient.","line":116,"path":"docs/INTEGRATION.md","reproduction":"Mint token 1 to Alice with version 1. Before submitting either rework, read tokenVersion(1) twice; both return 1. The authorized signer signs Rework(Alice,1,\"request-a\",2,10000) and Rework(Alice,1,\"request-b\",2,10000) in domain {name:\"SigilNFT\",version:\"1\",chainId:8453,verifyingContract:address(nft)}. At timestamp 100 Alice redeems request-a, then request-b. Expected under docs/INTEGRATION.md: the prescribed reads avoid collisions. Actual: the first succeeds and the second reverts StaleVersion(); tokenVersion remains 2 and tokenURI remains \"request-a\". Reproduced with Foundry test_concurrentOnchainReadsAllocateDuplicateVersion in the temporary review harness; original source unchanged.","severity":"low","snippet":"- Use `version` strictly greater than `tokenVersion(tokenId)`. Reading it on-chain avoids collisions\n  if a user requests two reworks before the first lands.","title":"Concurrent rework requests still collide under the documented version allocation rule"}],"hash":"f2ac3cf616171924033d550ace54843599120f5545a441f020940dde1137011a","nodeId":"a411e084-433f-4579-885e-28f01b80d482","outcome":"completed","summary":"Delivered [report.md](/tmp/sigil-review-ekm01tj3/report.md), covering all nine questions.\n\nFound one low-severity documentation defect: concurrent rework requests can receive colliding voucher versions. Recorded in [.imd-findings.json](/home/seat/.identitymd/work/22f2418a-3937-4933-b153-9ddbfd5eae58/a411e084-433f-4579-885e-28f01b80d482/.imd-findings.json).\n\nAll 27 supplied tests and 28 additional review tests passed. Source and configuration files remain unchanged.","treeHash":null,"usage":{"cachedInputTokens":1573504,"inputTokens":120517,"model":"gpt-6-astra","outputTokens":23664,"runtime":"codex","turns":7,"wallClockMs":966586}}],"verification":[]}