// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; /// @notice Minimal VRF v2 (subscription) coordinator surface, as run by Plugin on XDC. interface IVRFCoordinatorV2Like { function requestRandomWords( bytes32 keyHash, uint64 subId, uint16 minimumRequestConfirmations, uint32 callbackGasLimit, uint32 numWords ) external returns (uint256 requestId); } /// @title VRFv2ConsumerExperimental — EXPERIMENTAL, not promoted /// @notice Targets ONLY "VRFCoordinatorV2 1.0.0", the version Plugin has deployed on XDC mainnet /// (typeAndVersion() read on 2026-10-21 at 0xCc017FEe2fbD6722168c07418D9dD6A2509E10B4; /// v2.5 struct-based requestRandomWords selector absent). The coordinator showed no /// events for ~30 days, so treat as experimental. /// @dev Request is owner-gated to stop subscription draining. Clean-room. contract VRFv2ConsumerExperimental { IVRFCoordinatorV2Like public immutable coordinator; bytes32 public immutable keyHash; uint64 public immutable subId; address public immutable owner; uint16 public constant MIN_CONFIRMATIONS = 3; uint32 public constant CALLBACK_GAS = 200_000; mapping(uint256 => uint256[]) internal _words; mapping(uint256 => bool) public pending; uint256 public lastRequestId; error OnlyCoordinator(address caller); error OnlyOwner(); error UnknownRequest(uint256 requestId); event Requested(uint256 indexed requestId); event Fulfilled(uint256 indexed requestId, uint256[] words); constructor(IVRFCoordinatorV2Like _coordinator, bytes32 _keyHash, uint64 _subId) { coordinator = _coordinator; keyHash = _keyHash; subId = _subId; owner = msg.sender; } function request(uint32 numWords) external returns (uint256 id) { if (msg.sender != owner) revert OnlyOwner(); id = coordinator.requestRandomWords(keyHash, subId, MIN_CONFIRMATIONS, CALLBACK_GAS, numWords); pending[id] = true; lastRequestId = id; emit Requested(id); } /// @notice Callback entry point called by the coordinator. function rawFulfillRandomWords(uint256 requestId, uint256[] calldata words) external { if (msg.sender != address(coordinator)) revert OnlyCoordinator(msg.sender); if (!pending[requestId]) revert UnknownRequest(requestId); pending[requestId] = false; _words[requestId] = words; emit Fulfilled(requestId, words); } function randomWords(uint256 requestId) external view returns (uint256[] memory) { return _words[requestId]; } }