{"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":"f6ccb7d3-379f-4717-a482-eb86daff4e88","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"a009ca4d1a4fa79e2bc24dba53db65666f13f7625e6d2e487b9465f5ed43088a","dependsOn":["audit_imported_code"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","tools":[]},"key":"adapt_contract_project","kind":"code","role":"implement","skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","state":"accepted"},{"acceptedSubmissionHash":null,"dependsOn":["adapt_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":"working"},{"acceptedSubmissionHash":null,"dependsOn":["adapt_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":"working"},{"acceptedSubmissionHash":"3059c9fa82f748dd89183967feffb478b37ad6742061a27f7c0f54daf8eea006","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","tools":[]},"key":"audit_imported_code","kind":"code","role":"review","skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","state":"accepted"},{"acceptedSubmissionHash":null,"dependsOn":["adapt_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":"waiting"},{"acceptedSubmissionHash":null,"dependsOn":["adapt_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":"working"},{"acceptedSubmissionHash":null,"dependsOn":["adapt_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":"working"},{"acceptedSubmissionHash":null,"dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"failed"},{"acceptedSubmissionHash":null,"dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","state":"working"}],"objective":"# BMC ILLUMINATI PASS #1356 — IMPLEMENTATION ONLY — FIX CRITICAL VRF SECURITY FINDINGS\n\n**OBJECTIVE: IMPLEMENT THE FIX, NOT ANOTHER AUDIT OF UNCHANGED CODE.**\n\nRepository:\nhttps://github.com/0xSt3w/launch-1356-project-bmc-illuminati-pass\n\nSource branch: `security/vrf-recovery`\n\nThe previous IdentityMD audits identified two high-severity vulnerabilities:\n\n1. Unregistered Chainlink VRF key hashes can cause irreversible BMC burns for requests that will never be fulfilled.\n2. A lost VRF request permanently blocks all subsequent NFT reveals.\n\n**IMPLEMENTATION REQUIRED**\n\n- Replace the vulnerable per-mint VRF architecture with a post-sale single-seed architecture.\n- Preserve the 500 NFT collection, 40 initial Genesis, 37 Mystery Genesis and 423 Standard outcomes.\n- Preserve Legendary Genesis #1.\n- Preserve existing BMC mint prices and irreversible burns.\n- Implement an immutable sale deadline and permissionless sale closing.\n- Request the VRF seed only after the sale closes.\n- Implement permissionless timeout recovery with first-delivered-word-wins semantics.\n- Test overlapping requests, delayed responses, selective fulfillment and failed callbacks.\n- Validate the registered Chainlink proving key before requesting randomness.\n- Configure VRF parameters and Genesis image commitment at construction.\n- Ensure pending NFT metadata cannot misrepresent publicly known outcomes.\n- Preserve fair distribution without replacement.\n- Ensure the deployed contract runtime stays below 24,576 bytes. Refactor artwork or metadata code if necessary.\n- Update all Foundry tests, deployment scripts and README.\n- Run the full Foundry suite and Ethereum Mainnet fork tests.\n- Produce a security re-review of the implemented code.\n\n**VERIFIED MAINNET CONFIGURATION**\n\nNetwork: Ethereum Mainnet, chain ID 1\n\nVRF Coordinator:\n`0xD7f86b4b8Cae7D942340FF628F82735b7a20893a`\n\nSubscription ID:\n`27852459484169864437638942739291618120641810244677084392617474192083559562985`\n\n500 Gwei Key Hash:\n`0x3fd2fec10d06ee8f65e7f2e95f5c56511359ece3f33960ad8a866ae24a8ff10b`\n\nPayment mode: native ETH (`nativePayment = true`)\n\nCallback gas limit: 500000\n\nRequest confirmations: 3\n\nBMC token:\n`0x38224239f2BDae357D47cb864BC533d7f72dF961`\n\nVerify these parameters again against the live coordinator before using them.\n\n**DELIVERABLES**\n\n1. Complete corrected Solidity contracts.\n2. Updated deployment scripts.\n3. Foundry tests with passing logs.\n4. Ethereum Mainnet fork verification.\n5. Contract bytecode size report.\n6. Security re-review and documented residual risks.\n7. A downloadable patch or reviewable GitHub Pull Request.\n\nDo not modify `.github/workflows/main.yml`, because previous publication attempts failed due to workflow-token permissions.\n\nIf GitHub publication fails, provide the complete patch as a downloadable artifact instead of claiming successful delivery.\n\n**STRICT RESTRICTIONS**\n\n- NO Ethereum Mainnet deployment.\n- NO broadcast transactions.\n- NO real BMC burns or transfers.\n- NO subscription funding or consumer registration.\n- NO Pull Request merge.\n- NO automatic Mainnet launch.\n\nBefore any paid execution, disclose the total price and scope for approval.\n\n**SUCCESS CRITERIA**\n\nThe job is complete only when the corrected code and tests are delivered, the two high-severity findings are demonstrably resolved, and remaining risks are documented.\n\nDo not report the project as Mainnet-ready solely because existing tests pass.","parentJobId":null,"planHash":"9b3b0b8529e7ba322a05013bbc605431546d4e8938db20cc50c80f46ad8e91b2","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"f6ccb7d3-379f-4717-a482-eb86daff4e88","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":"52681","feedbackHash":"51e261dc8c5d52a2783d81246d55453432ee8d4a8e164ba3c6406f0ee307b3d4","nodeKey":"adapt_contract_project","submissionHash":"a009ca4d1a4fa79e2bc24dba53db65666f13f7625e6d2e487b9465f5ed43088a","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51039","feedbackHash":"e8d294b618f8d6b0bb8504949620c8d7def97366610e718733c784294cc7d541","nodeKey":"audit_imported_code","submissionHash":"3059c9fa82f748dd89183967feffb478b37ad6742061a27f7c0f54daf8eea006","tag1":"review:submission","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"fe284de20c9fd41c4578c2bbfaa3eaa605c722fc80c8afcd97843af6cdff2fb7","state":"blocked","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"9ab27edcfd62be02","findings":[{"citation":"resolved","description":"setVRFConfig (line 196) only requires keyHash != 0, and mintMystery burns the BMC (line 248) before calling requestRandomWords with that key hash. The mainnet VRFCoordinatorV2_5 deliberately does not validate the key hash at request time (its source states: 'we do not check whether the keyHash is valid to save gas ... requests for invalid keyHashes ... will simply not be fulfilled'), so a mistyped or unregistered key hash is accepted on-chain, the burn is irreversible, the request is never served and the token stays AwaitingRandomness forever. Because reveals are strictly in mint order (line 321) every later mystery token is frozen as well. The coordinator exposes s_provingKeys(bytes32) -> (exists, maxGas) and s_provingKeyHashes(); neither is consulted. Live check on 2026-10-12 against 0xD7f86b4b8Cae7D942340FF628F82735b7a20893a: key hash 0x3fd2fec1...8ff10b exists with maxGas 500 gwei, minimumRequestConfirmations 3, maxGasLimit 2,500,000; the single-nibble variant used in the proof does not exist. The proof test is written against the current mintMystery/setVRFConfig API; if the fix moves to the post-sale single-seed design the same assertion (no burn, no request, for an unregistered key hash) must be re-expressed against the new seed-request entry point.","line":253,"path":"src/BMCIlluminatiPass.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {BMCIlluminatiPass} from \"src/BMCIlluminatiPass.sol\";\n\n/// @dev Minimal 18-decimal token with OpenZeppelin-style burnFrom semantics.\ncontract ProofBMC {\n    string public name = \"BMC Illuminati\";\n    string public symbol = \"BMC\";\n    uint8 public constant decimals = 18;\n    uint256 public totalSupply;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    function mint(address to, uint256 amount) external {\n        balanceOf[to] += amount;\n        totalSupply += amount;\n    }\n\n    function approve(address spender, uint256 amount) external returns (bool) {\n        allowance[msg.sender][spender] = amount;\n        return true;\n    }\n\n    function burnFrom(address account, uint256 amount) external {\n        uint256 a = allowance[account][msg.sender];\n        require(a >= amount, \"allowance\");\n        allowance[account][msg.sender] = a - amount;\n        require(balanceOf[account] >= amount, \"balance\");\n        balanceOf[account] -= amount;\n        totalSupply -= amount;\n    }\n}\n\n/// @dev Mirrors the mainnet VRFCoordinatorV2_5 behaviour that matters here: it exposes the\n///      registered proving keys (`s_provingKeys` / `s_provingKeyHashes`) but, exactly like the\n///      real contract (\"Note we do not check whether the keyHash is valid to save gas. The\n///      consequence for users is that they can send requests for invalid keyHashes which will\n///      simply not be fulfilled.\"), `requestRandomWords` accepts any key hash.\ncontract ProofCoordinator {\n    /// @dev Same field layout as Chainlink's VRFV2PlusClient.RandomWordsRequest, so the ABI selector matches.\n    struct RandomWordsRequest {\n        bytes32 keyHash;\n        uint256 subId;\n        uint16 requestConfirmations;\n        uint32 callbackGasLimit;\n        uint32 numWords;\n        bytes extraArgs;\n    }\n\n    struct ProvingKey {\n        bool exists;\n        uint64 maxGas;\n    }\n\n    mapping(bytes32 => ProvingKey) public s_provingKeys;\n    bytes32[] public s_provingKeyHashes;\n    uint256 public nextRequestId = 1;\n    mapping(uint256 => bytes32) public keyHashOfRequest;\n\n    function registerProvingKey(bytes32 keyHash, uint64 maxGas) external {\n        s_provingKeys[keyHash] = ProvingKey({exists: true, maxGas: maxGas});\n        s_provingKeyHashes.push(keyHash);\n    }\n\n    function requestRandomWords(RandomWordsRequest calldata req) external returns (uint256 id) {\n        id = nextRequestId++;\n        keyHashOfRequest[id] = req.keyHash;\n    }\n}\n\n/// @title Proof: BMC is burned for a VRF request made with an unregistered key hash.\n/// @notice Fails on the current code: `mintMystery` burns 350,000 BMC and opens a request the\n///         coordinator will never fulfil because nothing checks the configured key hash against\n///         the coordinator's registered proving keys. Passes once the contract refuses to burn\n///         (at configuration or at request time) when the key hash is not registered.\ncontract KeyHashNotValidatedProof is Test {\n    ProofBMC bmc;\n    ProofCoordinator coordinator;\n    BMCIlluminatiPass pass;\n\n    address owner = makeAddr(\"owner\");\n    address alice = makeAddr(\"alice\");\n\n    bytes32 constant REGISTERED_KEY_HASH = 0x3fd2fec10d06ee8f65e7f2e95f5c56511359ece3f33960ad8a866ae24a8ff10b;\n    // One flipped nibble: a typo nobody will ever serve.\n    bytes32 constant UNREGISTERED_KEY_HASH = 0x3fd2fec10d06ee8f65e7f2e95f5c56511359ece3f33960ad8a866ae24a8ff10c;\n\n    function setUp() public {\n        bmc = new ProofBMC();\n        coordinator = new ProofCoordinator();\n        coordinator.registerProvingKey(REGISTERED_KEY_HASH, 500 gwei);\n        pass = new BMCIlluminatiPass(owner, address(bmc), address(coordinator));\n        vm.prank(owner);\n        pass.setGenesisImagesCID(\"bafybeiproofcid\");\n        for (uint256 i = 0; i < 40; i++) {\n            address g = address(uint160(0x1000 + i));\n            bmc.mint(g, 1_000_000e18);\n            vm.startPrank(g);\n            bmc.approve(address(pass), 1_000_000e18);\n            pass.mintGenesis();\n            vm.stopPrank();\n        }\n        assertEq(pass.totalMinted(), 40);\n    }\n\n    function test_mintMysteryMustNotBurnForUnregisteredKeyHash() public {\n        (bool exists,) = coordinator.s_provingKeys(UNREGISTERED_KEY_HASH);\n        assertFalse(exists, \"precondition: key hash is not registered\");\n\n        vm.prank(owner);\n        try pass.setVRFConfig(1, UNREGISTERED_KEY_HASH, 500_000, 3, true) {}\n        catch {\n            // Rejecting the unregistered key hash at configuration time is an acceptable fix.\n            return;\n        }\n\n        bmc.mint(alice, 350_000e18);\n        uint256 balanceBefore = bmc.balanceOf(alice);\n        uint256 supplyBefore = bmc.totalSupply();\n\n        vm.startPrank(alice);\n        bmc.approve(address(pass), 350_000e18);\n        (bool ok,) = address(pass).call(abi.encodeCall(BMCIlluminatiPass.mintMystery, ()));\n        vm.stopPrank();\n\n        // Expected: the mint reverts because no VRF node serves this key hash, so the request\n        // can never be fulfilled. Actual (current code): the mint succeeds, 350,000 BMC are gone\n        // and token 41 is AwaitingRandomness forever.\n        assertFalse(ok, \"mintMystery must revert when the key hash is not registered on the coordinator\");\n        assertEq(bmc.balanceOf(alice), balanceBefore, \"no BMC may be burned for an unfulfillable request\");\n        assertEq(bmc.totalSupply(), supplyBefore, \"supply must be unchanged\");\n        assertEq(pass.totalMinted(), 40, \"no pending token may be minted\");\n    }\n}","reproduction":"State: CID set, 40 Genesis minted, owner calls setVRFConfig(subId, 0x3fd2fec10d06ee8f65e7f2e95f5c56511359ece3f33960ad8a866ae24a8ff10c /* one flipped nibble, unregistered */, 500000, 3, true), which succeeds. Alice approves 350,000e18 BMC and calls mintMystery(). Expected: revert, no burn, because the coordinator has no proving key for that hash. Actual: 350,000 BMC burned (totalSupply drops), token 41 minted with revealStatus AwaitingRandomness, requestRandomWords returned an id that no VRF node will ever fulfil; finalizeReveals returns 0 forever and tokenURI(41) stays 'Pending Reveal'. Run: forge test --match-path test/scratch/KeyHashNotValidated.t.sol (fails with 'mintMystery must revert when the key hash is not registered on the coordinator').","severity":"high","snippet":"        requestId = VRF_COORDINATOR.requestRandomWords(","title":"mintMystery burns 350,000 BMC for a VRF request whose key hash is never checked against the coordinator's registered proving keys"},{"citation":"resolved","description":"finalizeReveals only advances while the next token in mint order has RandomnessStored. If one request is never delivered (unregistered key hash as in the previous finding; a subscription left unfunded past the Chainlink node request timeout; the owner switching the subscription id via setVRFConfig to one that is later cancelled by its owner once it has no pending requests of its own; coordinator-owner cancellation) the stored words of all later tokens can never be consumed: their holders burned 350,000 BMC each and hold a 'Pending Reveal' NFT forever, and the 37 Genesis / 423 Standard pool is never drawn. The contract header (lines 36-39) presents this as intended and claims v2.5 requests remain pending indefinitely while unfunded, which does not account for node-side request expiry. The existing suite enshrines the freeze as desired behaviour (test/BMCIlluminatiPass.t.sol:605 test_UnavailableRandomnessStaysPendingNoFallback) instead of exercising a recovery. The requester's brief mandates replacing this with a post-sale single seed, a permissionless timeout re-request and first-delivered-word-wins semantics; the proof test is written against the current API and asserts only that a token whose randomness exists on-chain must eventually be revealable when an earlier request is lost.","line":321,"path":"src/BMCIlluminatiPass.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {BMCIlluminatiPass} from \"src/BMCIlluminatiPass.sol\";\n\ninterface IRawFulfill {\n    function rawFulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) external;\n}\n\n/// @dev Minimal 18-decimal token with OpenZeppelin-style burnFrom semantics.\ncontract ProofBMC2 {\n    uint8 public constant decimals = 18;\n    uint256 public totalSupply;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    function mint(address to, uint256 amount) external {\n        balanceOf[to] += amount;\n        totalSupply += amount;\n    }\n\n    function approve(address spender, uint256 amount) external returns (bool) {\n        allowance[msg.sender][spender] = amount;\n        return true;\n    }\n\n    function burnFrom(address account, uint256 amount) external {\n        uint256 a = allowance[account][msg.sender];\n        require(a >= amount, \"allowance\");\n        allowance[account][msg.sender] = a - amount;\n        require(balanceOf[account] >= amount, \"balance\");\n        balanceOf[account] -= amount;\n        totalSupply -= amount;\n    }\n}\n\n/// @dev Coordinator stand-in: records requests, the test decides which ones are ever fulfilled.\ncontract ProofCoordinator2 {\n    /// @dev Same field layout as Chainlink's VRFV2PlusClient.RandomWordsRequest, so the ABI selector matches.\n    struct RandomWordsRequest {\n        bytes32 keyHash;\n        uint256 subId;\n        uint16 requestConfirmations;\n        uint32 callbackGasLimit;\n        uint32 numWords;\n        bytes extraArgs;\n    }\n\n    uint256 public nextRequestId = 1;\n    mapping(uint256 => address) public consumerOf;\n    mapping(uint256 => uint32) public gasOf;\n\n    function requestRandomWords(RandomWordsRequest calldata req) external returns (uint256 id) {\n        id = nextRequestId++;\n        consumerOf[id] = msg.sender;\n        gasOf[id] = req.callbackGasLimit;\n    }\n\n    function fulfill(uint256 requestId, uint256 word) external returns (bool ok) {\n        uint256[] memory words = new uint256[](1);\n        words[0] = word;\n        (ok,) = consumerOf[requestId].call{gas: gasOf[requestId]}(\n            abi.encodeCall(IRawFulfill.rawFulfillRandomWords, (requestId, words))\n        );\n    }\n}\n\n/// @title Proof: one request that is never fulfilled freezes every later reveal forever.\n/// @notice Fails on the current code: token 42's randomness is delivered, but because token 41's\n///         request is never served (unregistered key hash, node-side request expiry, subscription\n///         problems) `finalizeReveals` can never advance past 41 and there is no timeout, re-request\n///         or recovery path. Passes once the protocol can make progress without the lost request.\ncontract LostRequestBlocksRevealsProof is Test {\n    ProofBMC2 bmc;\n    ProofCoordinator2 coordinator;\n    BMCIlluminatiPass pass;\n\n    address owner = makeAddr(\"owner\");\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n\n    function setUp() public {\n        bmc = new ProofBMC2();\n        coordinator = new ProofCoordinator2();\n        pass = new BMCIlluminatiPass(owner, address(bmc), address(coordinator));\n        vm.startPrank(owner);\n        pass.setGenesisImagesCID(\"bafybeiproofcid\");\n        pass.setVRFConfig(1, keccak256(\"500 gwei\"), 500_000, 3, true);\n        vm.stopPrank();\n        for (uint256 i = 0; i < 40; i++) {\n            address g = address(uint160(0x1000 + i));\n            bmc.mint(g, 1_000_000e18);\n            vm.startPrank(g);\n            bmc.approve(address(pass), 1_000_000e18);\n            pass.mintGenesis();\n            vm.stopPrank();\n        }\n    }\n\n    function _mintMystery(address who) internal returns (uint256 id, uint256 req) {\n        bmc.mint(who, 350_000e18);\n        vm.startPrank(who);\n        bmc.approve(address(pass), 350_000e18);\n        (id, req) = pass.mintMystery();\n        vm.stopPrank();\n    }\n\n    function test_laterTokenMustBeRevealableWhenEarlierRequestIsLost() public {\n        (uint256 a, uint256 reqA) = _mintMystery(alice); // 41, its request is never fulfilled\n        (uint256 b, uint256 reqB) = _mintMystery(bob); // 42, fulfilled normally\n        assertEq(a, 41);\n        assertEq(b, 42);\n        assertTrue(reqA != reqB);\n\n        assertTrue(coordinator.fulfill(reqB, 0xB0B), \"delivery for 42 succeeds\");\n        assertEq(uint8(pass.revealStatusOf(b)), uint8(BMCIlluminatiPass.RevealStatus.RandomnessStored));\n\n        // A month passes. Chainlink nodes have long stopped retrying request A. Anyone may try to\n        // drive the reveal queue forward.\n        vm.warp(block.timestamp + 30 days);\n        vm.roll(block.number + 215_000);\n        vm.prank(bob);\n        pass.finalizeReveals(100);\n\n        // Expected: bob's token, whose randomness exists on-chain, obtains an outcome. Actual:\n        // revealedCount stays 0, token 42 stays Pending, and so does every token minted after it.\n        assertEq(\n            uint8(pass.revealStatusOf(b)),\n            uint8(BMCIlluminatiPass.RevealStatus.Revealed),\n            \"token 42 is blocked forever behind the lost request for token 41\"\n        );\n        assertTrue(pass.tierOf(b) != BMCIlluminatiPass.Tier.Pending, \"token 42 never receives a tier\");\n    }\n}","reproduction":"State: CID set, VRF configured, 40 Genesis minted. Alice mints token 41 (request A), Bob mints token 42 (request B). The coordinator delivers request B only (word 0xB0B, callback with 500k gas): token 42 becomes RandomnessStored. Request A is never delivered. 30 days and 215,000 blocks later Bob calls finalizeReveals(100). Expected: token 42 (and anything after it) obtains an outcome. Actual: finalizeReveals returns 0, revealedCount stays 0, revealStatusOf(42) == RandomnessStored (2), tierOf(42) == Pending, and every token 43..500 minted afterwards is likewise frozen. No function on the contract can change this. Run: forge test --match-path test/scratch/LostRequestBlocksReveals.t.sol (fails with 'token 42 is blocked forever behind the lost request for token 41: 2 != 3').","severity":"high","snippet":"        while (done < maxCount && tokenId <= totalMinted && _revealStatus[tokenId] == RevealStatus.RandomnessStored) {","title":"A single unfulfilled VRF request permanently blocks every later reveal; there is no timeout, re-request or recovery path"},{"citation":"resolved","description":"The launch recipe forbids anything configured after deployment rather than in the constructor, and the brief requires VRF parameters at construction. setVRFConfig is an owner setter with no lock: the contract leaves the constructor with vrfConfig.configured == false, so mintMystery reverts VRFNotConfigured until the owner acts, and the owner may later re-point subscriptionId, keyHash, callbackGasLimit or nativePayment mid-sale. Combined with the missing key-hash validation this gives one key the ability to turn every later mystery mint into an unfulfillable burn, or to halt phase 2 entirely by pointing at a subscription this contract is not a consumer of (requestRandomWords then reverts InvalidConsumer). Fix: take subscriptionId, keyHash, callbackGasLimit, requestConfirmations and nativePayment as constructor arguments (all static types), store them immutable, and validate the key hash against the coordinator on first use rather than in the constructor (the launch rehearsal runs constructors on an empty chain).","line":196,"path":"src/BMCIlluminatiPass.sol","reproduction":"Deploy BMCIlluminatiPass(owner, bmc, coordinator); set the CID; mint 40 Genesis. Call mintMystery(): reverts VRFNotConfigured. Owner calls setVRFConfig(validSub, validKey, 500000, 3, true); mints work. Owner then calls setVRFConfig(99 /* a subscription that does not list this contract */, validKey, 500000, 3, true): every subsequent mintMystery reverts at the coordinator with InvalidConsumer until the owner reverts the change; or owner sets an unregistered key hash and every later mint burns BMC for nothing (see the high finding). The existing test test_UnavailableRandomnessStaysPendingNoFallback (test/BMCIlluminatiPass.t.sol:613-614) already performs such a mid-sale reconfiguration.","severity":"medium","snippet":"    function setVRFConfig(","title":"VRF parameters are configured after deployment by the owner and can be changed at any time during the sale"},{"citation":"resolved","description":"The Genesis artwork location is a string written by the owner after deployment. Until that call no mint of any kind is possible (mintGenesis and mintMystery revert GenesisCIDNotSet), and if the owner renounces ownership (OpenZeppelin Ownable.renounceOwnership is exposed) or loses the key before calling it the collection is permanently unmintable. The launch recipe forbids post-deployment initialization and only accepts static constructor arguments, and the brief asks for the Genesis image commitment at construction. A string cannot be passed by the factory; the natural shape is a bytes32 CIDv0 digest (or a bytes32 content hash) stored immutable and rendered into the ipfs:// URI on-chain.","line":186,"path":"src/BMCIlluminatiPass.sol","reproduction":"Deploy BMCIlluminatiPass(owner, bmc, coordinator). Any user approving 1,000,000e18 BMC and calling mintGenesis() gets GenesisCIDNotSet. Owner calls renounceOwnership(). Now setGenesisImagesCID can never be called (onlyOwner with owner == 0), so no token can ever be minted: expected a collection that is live from deployment, actual a permanently dead contract.","severity":"medium","snippet":"    function setGenesisImagesCID(string calldata cid) external onlyOwner {","title":"Genesis image commitment is set post-deployment through an owner call instead of being fixed in the constructor"},{"citation":"resolved","description":"Once rawFulfillRandomWords stores the word (status RandomnessStored, event RandomnessStored with the word) the outcome of that token is a pure function of public state (keccak256(word, tokenId) % remaining against the current pool) whenever all earlier tokens are already revealed, yet tokenURI still returns the neutral 'Pending Reveal' JSON claiming the outcome is undetermined. A buyer of such a token on a marketplace can be sold a known-Standard token as a 'mystery' while the seller already computed the result off-chain. The window is normally short because finalizeReveals is permissionless, but it becomes permanent whenever an earlier request is lost (previous high finding). The brief asks that pending metadata cannot misrepresent publicly known outcomes.","line":509,"path":"src/BMCIlluminatiPass.sol","reproduction":"CID and VRF set, 40 Genesis minted, Alice mints token 41 (request R). Coordinator delivers R with word 4242 using a 95,000 gas callback (enough to store, not to finalize, exactly as test_PermissionlessFinalizationWhenCallbackGasTooLow does). revealStatusOf(41) == RandomnessStored and the word is in the RandomnessStored event. tokenURI(41) decodes to a description containing 'The outcome (Genesis or Standard) is not yet determined on-chain' and attribute Tier = 'Pending Reveal', although keccak256(abi.encode(4242, 41)) % 460 already fixes the tier; anyone can call finalizeReveals(1) and obtain exactly that outcome.","severity":"low","snippet":"            ' - Pending Reveal\",\"description\":\"A BMC Illuminati Pass mystery mint awaiting Chainlink VRF randomness. The outcome (Genesis or Standard) is not yet determined on-chain. Metadata becomes permanent once revealed.\",\"image\":\"data:image/svg+xml;base64,',","title":"Pending metadata states the outcome is 'not yet determined on-chain' even after the random word is stored and public"},{"citation":"resolved","description":"test_RealBMCGenesisMint requires block.chainid == 1 via require instead of selecting a fork from an environment variable or skipping (vm.skip) when none is configured. Running plain `forge test` in this repository reports 43 passed, 1 failed, so CI and any offline verifier (the launch checks run with no network) see a red suite regardless of contract correctness, and the gas_reports/`forge test` signal cannot be used as an acceptance gate. The deliverable asks for fork verification as a separate, explicitly configured run.","line":27,"path":"test/BMCMainnetFork.t.sol","reproduction":"Run `forge test` with no --fork-url and no RPC env. Expected: fork test skipped or isolated under a profile. Actual output: 'Failing tests: Encountered 1 failing test in test/BMCMainnetFork.t.sol:BMCMainnetForkTest [FAIL: Ethereum mainnet fork required] test_RealBMCGenesisMint() (gas: 372)'. Observed on this machine with forge 1.8.3.","severity":"low","snippet":"        require(block.chainid == 1, \"Ethereum mainnet fork required\");","title":"Mainnet fork test hard-fails the default suite when no mainnet RPC is available"},{"citation":"resolved","description":"The suite has no test where one request is lost while later ones are delivered (the exact scenario the brief lists as 'selective fulfillment'), no test that the configured key hash is registered on the coordinator, and no test of a coordinator that consumes a request on a failed callback (the real v2.5 coordinator deletes the commitment and emits success=false; the word is gone). test_UnavailableRandomnessStaysPendingNoFallback instead asserts that after a year a token is still Pending and that the owner's reconfiguration changes nothing, i.e. it locks in the high-severity behaviour. test_UnfundedSubscriptionKeepsTokenPendingUntilFunded in ChainlinkMockIntegration.t.sol relies on VRFCoordinatorV2_5Mock, which fulfils on demand and has no node-side request expiry, so it cannot show the unfunded-subscription loss.","line":605,"path":"test/BMCIlluminatiPass.t.sol","reproduction":"Scenario absent from the suite: mint 41 and 42, deliver only 42, warp 30 days, call finalizeReveals(100), assert 42 revealed. The scratch proof test/scratch/LostRequestBlocksReveals.t.sol runs it and fails. Scenario absent from the suite: setVRFConfig with a key hash the coordinator mock does not list in s_provingKeys, then mintMystery; the scratch proof test/scratch/KeyHashNotValidated.t.sol runs it and fails.","severity":"low","snippet":"    function test_UnavailableRandomnessStaysPendingNoFallback() public {","title":"Test suite asserts the reveal freeze as intended behaviour and never exercises a lost or selectively fulfilled request as a failure"},{"citation":"resolved","description":"The first reveal that draws a Genesis outcome writes revealedCount, mysteryGenesisRevealed, _slotSwap[r], _tier and _artworkId from zero (5 cold 22,100-gas SSTOREs) plus the status update, the _randomWord delete and the event. Measured through finalizeReveals(1) in a scratch test: 151,353 gas including call overhead, above the 110,000 the callback uses to size its batch. With the default 500,000 callback the batch computes to 3 and succeeds because later reveals are cheaper, but the constant and the 'measured ~85k worst case' comment are wrong, and a tighter callbackGasLimit near the 150,000 minimum would make the inline batch revert (caught) on every first delivery, wasting the subscription's gas. No loss of funds or state; the stored word remains finalizable permissionlessly.","line":95,"path":"src/BMCIlluminatiPass.sol","reproduction":"CID and VRF set (callbackGasLimit 500,000), 40 Genesis minted, mint token 41 and choose a word w with keccak256(abi.encode(w, 41)) % 460 < 37. Deliver with a 95,000-gas callback so only the word is stored, then measure gas of finalizeReveals(1): about 151k, versus the 110,000 per-reveal budget at line 95 and the '~85k worst case' claim at line 94.","severity":"info","snippet":"    uint256 public constant GAS_PER_REVEAL = 110_000;","title":"GAS_PER_REVEAL underestimates the first worst-case reveal (measured ~151k vs 110k budget); harmless only because the batch is wrapped in try/catch"}],"hash":"3059c9fa82f748dd89183967feffb478b37ad6742061a27f7c0f54daf8eea006","nodeId":"392e2424-15f9-4101-ad0c-621d075e686b","outcome":"completed","summary":"Review complete. The findings file `.imd-findings.json` holds 8 entries, and the tree is otherwise untouched (git status is clean; the scratch proofs live in the ignored `test/scratch/` directory).\n\n**Scope read.** I read `src/BMCIlluminatiPass.sol`, `src/StandardArt.sol`, `src/interfaces/IBMCToken.sol`, `script/Deploy.s.sol`, all three test files and both mocks, plus the protected harness. The working tree is at main. The `security/vrf-recovery` branch only adds an experimental draw prototype and extra tests, with no change to the production contract. The Chainlink dependency ships only interfaces, the client library and the V2.5 mock, so I confirmed the coordinator's request-time behaviour from its published source rather than from `lib/`. Nothing in scope was unreachable.\n\n**Findings, most severe first:**\n\n- **High, line 253:** mystery mints burn BMC for a request whose key hash is never checked against the coordinator's registered proving keys. The mainnet V2.5 coordinator accepts any key hash at request time by design. Proof test fails on current code.\n- **High, line 321:** one unfulfilled request freezes every later reveal forever. There is no timeout, re-request or recovery, and the existing suite asserts this freeze as intended. Proof test fails on current code.\n- **Medium, line 196:** VRF parameters are an owner setter, changeable mid-sale, instead of constructor-fixed. The launch recipe and the brief both require construction-time configuration.\n- **Medium, line 186:** the Genesis image commitment is set after deployment. Renouncing ownership first makes the collection permanently unmintable.\n- **Low, line 509:** pending metadata claims the outcome is undetermined after the random word is already stored and public.\n- **Low, fork test line 27:** the mainnet fork test hard-fails the default suite offline (43 passed, 1 failed on this machine).\n- **Low, test line 605:** no test covers selective fulfillment, lost requests, or key hash registration.\n- **Info, line 95:** the per-reveal gas budget is below the measured first worst-case reveal (about 151k gas); harmless because of the try/catch, but the comment and constant are wrong.\n\n**Verified against the live coordinator today.** The 500 gwei key hash is registered, minimum confirmations are 3 and max callback gas is 2.5M, matching the brief. The named subscription exists with zero LINK, zero native balance and no consumers, so phase 2 would revert at the coordinator until it is funded and this contract is registered. That is expected under the stated restrictions and is not reported as a defect.\n\n**Clean areas.** Runtime size is 23,488 bytes with no DELEGATECALL, CALLCODE or SELFDESTRUCT. Burn verification, reentrancy guards, the Fisher-Yates draw without replacement, strict in-order finalization and the price constants all behaved correctly under inspection and the existing tests.","treeHash":null,"usage":{"cachedInputTokens":925841,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":39448,"runtime":"claude","turns":25,"wallClockMs":545925}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"d20c1a95c50699ea","findings":[],"hash":"7d01b3379678319e19c525c11dbf3ba224a8d274766c05e0f500c0c65926a1cb","nodeId":"9d43e5f9-7b8b-49d0-af35-20fd001987f4","outcome":"failed","summary":"needs input: Two immutable BMCIlluminatiPass constructor arguments are not given by the brief: genesisImagesDigest (bytes32 sha2-256 digest of the pinned IPFS directory holding the 77 Genesis PNGs; the CID bafybeihfihwwpev4gkqjuqawgtptsv3ynzsokclo6odsolvoa65aqaac2u appears only as a fork-test fixture and the project README calls the CID a blocker until the artwork is uploaded) and saleDuration (seconds the sale stays open after deployment, 3600 to 63072000). Neither can be changed after deployment. — What is the final IPFS CID of the pinned Genesis artwork directory (is it bafybeihfihwwpev4gkqjuqawgtptsv3ynzsokclo6odsolvoa65aqaac2u?), and how long should the sale stay open after deployment (e.g. 30 days)?","treeHash":null,"usage":{"cachedInputTokens":292382,"inputTokens":14,"model":"claude-opus-5-5","outputTokens":2424,"runtime":"claude","turns":7,"wallClockMs":39271}},{"artifacts":[],"attempt":1,"bundleHash":"b8af83ec1679f3e351bafd2765ae58be81badbafa2c4fb85f04fc1615007a0d4","device":"1a7606be97f168d4","findings":[],"hash":"a009ca4d1a4fa79e2bc24dba53db65666f13f7625e6d2e487b9465f5ed43088a","nodeId":"e7419696-68b4-4bc7-96b2-75d3235864c3","outcome":"completed","summary":"Everything is in place and verified. Final state:\n\n**What was delivered**\n\n- `src/BMCIlluminatiPass.sol` rewritten to the post-sale single-seed design. Mystery mints burn and mint only, with no VRF call. An immutable sale deadline, permissionless `closeSale` and `requestSeed`, key-hash validation against the coordinator's `s_provingKeys` registry before any request, one-hour timeout re-requests with first-delivered-word-wins, and stateless reveals computed from `(seed, tokenId)` by the same mint-order Fisher-Yates draw over 37 Genesis and 423 Standard outcomes. All VRF parameters, the Genesis image digest (CIDv1 rendered on-chain) and the sale duration are constructor arguments. The owner is `$owner` and has no on-chain powers.\n- Deploy script, README, website copy and ABI table, `ADAPTATION.md`, security re-review with residual risks, and a test/size report in `docs/`.\n\n**Verification**\n\n| Check | Result |\n| --- | --- |\n| `forge build --sizes` | runtime 23,603 bytes, 973 under the cap |\n| `forge test` offline | 55 passed, 2 fork tests skipped |\n| Fork tests on live mainnet RPC | 2 passed |\n| Forbidden opcode scan, CREATE2 factory rehearsal on empty chain | clean, deploys |\n| Live `cast` reads | key hash registered, typo unregistered, min confirmations 3, max gas 2.5M, subscription owned by the launch owner |\n\n**Both high findings are demonstrably resolved** with tests that re-express the audit proofs against the new entry points, plus overlapping, delayed, selective and failed-callback scenarios.\n\n**Open items for the requester, not blockers for this work**\n\n- Two constructor values only they can give: the Genesis directory digest (candidate from their own fork test is documented but unverified, since IPFS gateways rate-limited) and the sale duration.\n- The live subscription has zero balance and no consumers. `requestSeed` reverts cleanly at the coordinator until the deployed address is added and funded. No BMC is at risk either way.\n\nNothing was broadcast, deployed, funded or merged.","treeHash":"35db2310a86699c70b64d2d2894f98621701981c","usage":{"cachedInputTokens":2983495,"inputTokens":610,"model":"claude-fable-5-1","outputTokens":100024,"runtime":"claude","turns":54,"wallClockMs":1327628}}],"verification":[{"checks":[{"durationMs":2675,"exitCode":0,"name":"build","output":"Compiling 65 files with Solc 0.8.26\nSolc 0.8.26 finished in 2.59s\nCompiler run successful!\n","passed":true},{"durationMs":472,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/BMCMainnetFork.t.sol:BMCMainnetForkTest\n[SKIP] test_LiveVRFParametersMatchTheBrief() (gas: 0)\n[SKIP] test_RealBMCGenesisMintAndSeedRequestGuard() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 2 skipped; finished in 428.81µs (88.97µs CPU time)\n\nRan 5 tests for test/ChainlinkMockIntegration.t.sol:ChainlinkMockIntegrationTest\n[PASS] test_NotAConsumerRevertsWithoutStateChange() (gas: 950955)\n[PASS] test_OverlappingRequestsFirstWordWinsThroughChainlinkMock() (gas: 1497355)\n[PASS] test_RequestAndFulfilThroughChainlinkMock() (gas: 1007642)\n[PASS] test_UnfundedSubscriptionKeepsTokensPendingUntilFunded() (gas: 849510)\n[PASS] test_UnregisteredKeyHashNeverReachesTheCoordinator() (gas: 9064045)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 12.46ms (8.11ms CPU time)\n\nRan 50 tests for test/BMCIlluminatiPass.t.sol:BMCIlluminatiPassTest\n[PASS] testFuzz_SaleDurationSetsDeadline(uint256) (runs: 64, μ: 35863, ~: 36022)\nLogs:\n  Bound result 25416990\n\n[PASS] testFuzz_SingleRevealAlwaysInInventory(uint256) (runs: 64, μ: 9264515, ~: 9264470)\n[PASS] test_CallbackGasUsageIsSmall() (gas: 101105588)\n[PASS] test_CloseSaleIsPermissionlessAfterDeadlineOnly() (gas: 9446520)\n[PASS] test_Constants() (gas: 101071)\n[PASS] test_ConstructorNeedsNoLiveContracts() (gas: 21619)\n[PASS] test_ConstructorValidation() (gas: 8799)\n[PASS] test_DeadlineBoundary() (gas: 9080456)\n[PASS] test_DeployScriptConfig() (gas: 11041370)\n[PASS] test_DeployScriptRequiresOwner() (gas: 6076088)\n[PASS] test_ExcessAllowanceOnlyBurnsPrice() (gas: 297601)\n[PASS] test_FailedCallbackIsRecoverable() (gas: 10121449)\n[PASS] test_FirstMinterGetsLegendaryGenesis1() (gas: 2060352)\n[PASS] test_Full500MintSimulation() (gas: 293983617)\n[PASS] test_GenesisCIDDerivedOnChain() (gas: 40961)\n[PASS] test_GenesisRevertsAfterPhase1() (gas: 8768468)\n[PASS] test_LostRequestRecoveredByTimeoutRerequest() (gas: 9984601)\n[PASS] test_Mints2To40AreSequentialGenesis() (gas: 12562300)\n[PASS] test_MysteryGenesisMetadataAfterReveal() (gas: 51452716)\n[PASS] test_MysteryMintBurnIsIndependentOfVRF() (gas: 8945244)\n[PASS] test_MysteryRevertsDuringPhase1() (gas: 413820)\n[PASS] test_NothingToRevealWithoutMysteryMints() (gas: 454272)\n[PASS] test_OnlyCoordinatorCanFulfill() (gas: 9190759)\n[PASS] test_OutcomesDependOnlyOnSeed() (gas: 28201532)\n[PASS] test_OverlappingRequestsFirstDeliveredWordWins() (gas: 12361588)\n[PASS] test_OwnerHasNoPowers() (gas: 118939)\n[PASS] test_PartialSaleDrawsFromFullPoolWithoutReplacement() (gas: 41435199)\n[PASS] test_PendingMetadataIsNeutral() (gas: 13160096)\n[PASS] test_Phase1CostsExactlyOneMillion() (gas: 348386)\n[PASS] test_Phase2CostsExactly350kAndMakesNoVRFRequest() (gas: 8979291)\n[PASS] test_ReentrancyGenesisBlocked() (gas: 833171)\n[PASS] test_ReentrancyMysteryBlocked() (gas: 9398868)\n[PASS] test_RequestSeedClosesSaleWhenPossible() (gas: 9380960)\n[PASS] test_RequestSeedRateLimitedUntilTimeout() (gas: 9332710)\n[PASS] test_RequestSeedRefusedOnceDelivered() (gas: 9234814)\n[PASS] test_RequestSeedRejectsUnregisteredKeyHash() (gas: 9234170)\n[PASS] test_RequestSeedRevertsCleanlyWhenCoordinatorRefuses() (gas: 9246754)\n[PASS] test_RevertInsufficientAllowance() (gas: 181419)\n[PASS] test_RevertInsufficientBalance() (gas: 195302)\n[PASS] test_RevertNoAllowance() (gas: 146376)\n[PASS] test_RevertWhenTokenDecimalsUnexpected() (gas: 731159)\n[PASS] test_RevertWhenTokenDoesNotActuallyBurn() (gas: 747648)\n[PASS] test_SeedDeliveryRevealsEverything() (gas: 30876343)\n[PASS] test_SellOutClosesSaleImmediately() (gas: 101344668)\n[PASS] test_StandardArtCombinationsUnique() (gas: 1487525)\n[PASS] test_StandardArtCoversEveryTrait() (gas: 744051)\n[PASS] test_StandardArtRendersWithoutGenesisTraits() (gas: 30070001)\n[PASS] test_StandardMetadataAfterReveal() (gas: 21747187)\n[PASS] test_TransfersAreStandard() (gas: 9044998)\n[PASS] test_ViewsRevertForNonexistent() (gas: 46001)\nSuite result: ok. 50 passed; 0 failed; 0 skipped; finished in 385.37ms (1.17s CPU time)\n\nRan 3 test suites in 387.18ms (398.26ms CPU time): 55 tests passed, 0 failed, 2 skipped (57 total tests)\n","passed":true},{"durationMs":855,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"BMCIlluminatiPass.approve(address,uint256)\",\"BMCIlluminatiPass.closeSale()\",\"BMCIlluminatiPass.mintGenesis()\",\"BMCIlluminatiPass.mintMystery()\",\"BMCIlluminatiPass.rawFulfillRandomWords(uint256,uint256[])\",\"BMCIlluminatiPass.renounceOwnership()\",\"BMCIlluminatiPass.requestSeed()\",\"BMCIlluminatiPass.safeTransferFrom(address,address,uint256)\",\"BMCIlluminatiPass.safeTransferFrom(address,address,uint256,bytes)\",\"BMCIlluminatiPass.setApprovalForAll(address,bool)\",\"BMCIlluminatiPass.transferFrom(address,address,uint256)\",\"BMCIlluminatiPass.transferOwnership(address)\"],\"files\":{\".github/workflows/main.yml\":45,\".gitignore\":5,\"ADAPTATION.md\":66,\"README.md\":157,\"artwork/genesis/genesis-01.png\":7910,\"artwork/genesis/genesis-02.png\":6998,\"artwork/genesis/genesis-03.png\":7016,\"artwork/genesis/genesis-04.png\":6664,\"artwork/genesis/genesis-05.png\":7186,\"artwork/genesis/genesis-06.png\":6703,\"artwork/genesis/genesis-07.png\":6488,\"artwork/genesis/genesis-08.png\":6807,\"artwork/genesis/genesis-09.png\":6793,\"artwork/genesis/genesis-10.png\":6550,\"artwork/genesis/genesis-11.png\":6125,\"artwork/genesis/genesis-12.png\":6418,\"artwork/genesis/genesis-13.png\":6298,\"artwork/genesis/genesis-14.png\":5909,\"artwork/genesis/genesis-15.png\":6142,\"artwork/genesis/genesis-16.png\":5727,\"artwork/genesis/genesis-17.png\":6019,\"artwork/genesis/genesis-18.png\":399,\"artwork/genesis/genesis-19.png\":369,\"artwork/genesis/genesis-20.png\":359,\"artwork/genesis/genesis-21.png\":330,\"artwork/genesis/genesis-22.png\":385,\"artwork/genesis/genesis-23.png\":391,\"artwork/genesis/genesis-24.png\":411,\"artwork/genesis/genesis-25.png\":393,\"artwork/genesis/genesis-26.png\":390,\"artwork/genesis/genesis-27.png\":386,\"artwork/genesis/genesis-28.png\":402,\"artwork/genesis/genesis-29.png\":375,\"artwork/genesis/genesis-30.png\":369,\"artwork/genesis/genesis-31.png\":421,\"artwork/genesis/genesis-32.png\":338,\"artwork/genesis/genesis-33.png\":389,\"artwork/genesis/genesis-34.png\":365,\"artwork/genesis/genesis-35.png\":352,\"artwork/genesis/genesis-36.png\":384,\"artwork/genesis/genesis-37.png\":365,\"artwork/genesis/genesis-38.png\":438,\"artwork/genesis/genesis-39.png\":374,\"artwork/genesis/genesis-40.png\":411,\"artwork/genesis/genesis-41.png\":380,\"artwork/genesis/genesis-42.png\":377,\"artwork/genesis/genesis-43.png\":399,\"artwork/genesis/genesis-44.png\":385,\"artwork/genesis/genesis-45.png\":352,\"artwork/genesis/genesis-46.png\":343,\"artwork/genesis/genesis-47.png\":362,\"artwork/genesis/genesis-48.png\":408,\"artwork/genesis/genesis-49.png\":325,\"artwork/genesis/genesis-50.png\":433,\"artwork/genesis/genesis-51.png\":377,\"artwork/genesis/genesis-52.png\":376,\"artwork/genesis/genesis-53.png\":401,\"artwork/genesis/genesis-54.png\":389,\"artwork/genesis/genesis-55.png\":361,\"artwork/genesis/genesis-56.png\":332,\"artwork/genesis/genesis-57.png\":414,\"artwork/genesis/genesis-58.png\":405,\"artwork/genesis/genesis-59.png\":409,\"artwork/genesis/genesis-60.png\":405,\"artwork/genesis/genesis-61.png\":369,\"artwork/genesis/genesis-62.png\":366,\"artwork/genesis/genesis-63.png\":379,\"artwork/genesis/genesis-64.png\":425,\"artwork/genesis/genesis-65.png\":406,\"artwork/genesis/genesis-66.png\":413,\"artwork/genesis/genesis-67.png\":350,\"artwork/genesis/genesis-68.png\":404,\"artwork/genesis/genesis-69.png\":392,\"artwork/genesis/genesis-70.png\":378,\"artwork/genesis/genesis-71.png\":401,\"artwork/genesis/genesis-72.png\":355,\"artwork/genesis/genesis-73.png\":405,\"artwork/genesis/genesis-74.png\":394,\"artwork/genesis/genesis-75.png\":390,\"artwork/genesis/genesis-76.png\":424,\"artwork/genesis/genesis-77.png\":381,\"artwork/tools/generate_genesis.py\":206,\"docs/SECURITY-REVIEW.md\":101,\"docs/TEST-RESULTS.md\":130,\"foundry.toml\":25,\"remappings.txt\":3,\"script/Deploy.s.sol\":89,\"src/BMCIlluminatiPass.sol\":563,\"src/StandardArt.sol\":236,\"src/interfaces/IBMCToken.sol\":18,\"src/interfaces/IVRFCoordinatorProvingKeys.sol\":12,\"test/BMCIlluminatiPass.t.sol\":1121,\"test/BMCMainnetFork.t.sol\":118,\"test/ChainlinkMockIntegration.t.sol\":180,\"test/mocks/MockBMC.sol\":42,\"test/mocks/MockVRFCoordinator.sol\":94,\"test/mocks/VRFCoordinatorV2_5MockWithKeys.sol\":24,\"website/app.js\":196,\"website/config.js\":11,\"website/index.html\":95,\"website/styles.css\":44},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1488,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/BMCIlluminatiPass.sol:241: Reentrancy in BMCIlluminatiPass._burnExact(address,uint256) (src/BMCIlluminatiPass.sol#241-252):\n[medium/medium] reentrancy-no-eth at src/BMCIlluminatiPass.sol:275: Reentrancy in BMCIlluminatiPass.requestSeed() (src/BMCIlluminatiPass.sol#275-300):\n[medium/medium] reentrancy-no-eth at src/BMCIlluminatiPass.sol:226: Reentrancy in BMCIlluminatiPass.mintMystery() (src/BMCIlluminatiPass.sol#226-238):\n[medium/medium] reentrancy-no-eth at src/BMCIlluminatiPass.sol:213: Reentrancy in BMCIlluminatiPass.mintGenesis() (src/BMCIlluminatiPass.sol#213-223):\n[medium/medium] uninitialized-local at src/BMCIlluminatiPass.sol:455: BMCIlluminatiPass.genesisImagesCID().bits (src/BMCIlluminatiPass.sol#455) is a local variable never initialized\n[medium/medium] uninitialized-local at src/BMCIlluminatiPass.sol:454: BMCIlluminatiPass.genesisImagesCID().buffer (src/BMCIlluminatiPass.sol#454) is a local variable never initialized\n[medium/medium] unused-return at src/BMCIlluminatiPass.sol:275: BMCIlluminatiPass.requestSeed() (src/BMCIlluminatiPass.sol#275-300) ignores return value by (exists,None) = IVRFCoordinatorProvingKeys(address(VRF_COORDINATOR)).s_provingKeys(KEY_HASH) (src/BMCIlluminatiPass.sol#282)\n[low/medium] reentrancy-benign at src/BMCIlluminatiPass.sol:241: Reentrancy in BMCIlluminatiPass._burnExact(address,uint256) (src/BMCIlluminatiPass.sol#241-252):\n[low/medium] reentrancy-benign at src/BMCIlluminatiPass.sol:275: Reentrancy in BMCIlluminatiPass.requestSeed() (src/BMCIlluminatiPass.sol#275-300):\n[low/medium] timestamp at src/BMCIlluminatiPass.sol:264: BMCIlluminatiPass._closeSale() (src/BMCIlluminatiPass.sol#264-268) uses timestamp for comparisons\n[low/medium] timestamp at src/BMCIlluminatiPass.sol:443: BMCIlluminatiPass.currentPrice() (src/BMCIlluminatiPass.sol#443-446) uses timestamp for comparisons\n[low/medium] timestamp at src/BMCIlluminatiPass.sol:275: BMCIlluminatiPass.requestSeed() (src/BMCIlluminatiPass.sol#275-300) uses timestamp for comparisons\n[low/medium] timestamp at src/BMCIlluminatiPass.sol:389: BMCIlluminatiPass.saleOpen() (src/BMCIlluminatiPass.sol#389-391) uses timestamp for comparisons","passed":true},{"durationMs":435,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/BMCIlluminatiPass.sol:282: Reentrancy: State change after external call (2 places)\n[high] unsafe-casting at src/BMCIlluminatiPass.sol:220: Unsafe Casting of integers\n[low] centralization-risk at src/BMCIlluminatiPass.sol:43: Centralization Risk\n[low] internal-function-used-once at src/StandardArt.sol:59: Internal Function Used Only Once (5 places)\n[low] large-numeric-literal at src/BMCIlluminatiPass.sol:77: Large Numeric Literal (5 places)\n[low] literal-instead-of-constant at src/BMCIlluminatiPass.sol:458: Literal Instead of Constant (34 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"a009ca4d1a4fa79e2bc24dba53db65666f13f7625e6d2e487b9465f5ed43088a","verifiedTreeHash":"35db2310a86699c70b64d2d2894f98621701981c","verifierVersion":"0.1.0+fb8278c5"}]}