# Smart Contracts (/docs/builders/vrf/smart-contracts)

# Smart Contract Integration

Learn how to integrate Fast VRF into your smart contracts for secure, verifiable randomness.

<Callout type="warn" title="Mainnet Production Readiness">
  The VRF contracts shown here are suitable for testnet development. Production mainnet deployment requires additional security measures:

  1. **Cryptographic Proof Verification**: Contracts must verify cryptographic proofs to ensure the VRF backend cannot manipulate outcomes.

  2. **Spam Prevention Mechanisms**: Production contracts should implement one or both of:
     * **Whitelisting**: Restrict VRF requests to approved contracts only
     * **Request Fees**: Charge a small fee per VRF request to prevent abuse
</Callout>

## VRF Coordinator

The VRF Coordinator manages randomness requests and fulfillment on RISE Chain.

```solidity
// RISE Testnet VRF Coordinator
address constant VRF_COORDINATOR = 0x9d57aB4517ba97349551C876a01a7580B1338909;
```

## Required Interfaces

Your contract must implement these interfaces to interact with VRF:

```solidity
// Interface for requesting random numbers
interface IVRFCoordinator {
    function requestRandomNumbers(
        uint32 numNumbers,  // How many random numbers you need
        uint256 seed        // Seed for randomness generation
    ) external returns (uint256 requestId);
}

// Interface your contract must implement
interface IVRFConsumer {
    function rawFulfillRandomNumbers(
        uint256 requestId,
        uint256[] memory randomNumbers
    ) external;
}
```

## Basic Implementation

### Minimal VRF Consumer

```solidity
contract BasicVRFConsumer is IVRFConsumer {
    IVRFCoordinator public coordinator;

    mapping(uint256 => uint256) public results;

    event RandomnessRequested(uint256 indexed requestId);
    event RandomnessFulfilled(uint256 indexed requestId, uint256 randomNumber);

    constructor(address _coordinator) {
        coordinator = IVRFCoordinator(_coordinator);
    }

    function requestRandom() external returns (uint256) {
        uint256 requestId = coordinator.requestRandomNumbers(1, block.timestamp);
        emit RandomnessRequested(requestId);
        return requestId;
    }

    function rawFulfillRandomNumbers(
        uint256 requestId,
        uint256[] memory randomNumbers
    ) external override {
        require(msg.sender == address(coordinator), "Only VRF coordinator");
        require(randomNumbers.length > 0, "No random numbers");

        results[requestId] = randomNumbers[0];
        emit RandomnessFulfilled(requestId, randomNumbers[0]);
    }
}
```

## Advanced Patterns

### Request Tracking

Track who made each request and handle state properly:

```solidity
contract TrackedVRFConsumer is IVRFConsumer {
    IVRFCoordinator public coordinator;

    struct Request {
        address requester;
        uint256 timestamp;
        bool fulfilled;
        uint256 result;
    }

    mapping(uint256 => Request) public requests;
    mapping(address => uint256[]) public userRequests;

    function requestRandomForUser() external returns (uint256) {
        uint256 requestId = coordinator.requestRandomNumbers(
            1,
            uint256(keccak256(abi.encode(msg.sender, block.timestamp)))
        );

        requests[requestId] = Request({
            requester: msg.sender,
            timestamp: block.timestamp,
            fulfilled: false,
            result: 0
        });

        userRequests[msg.sender].push(requestId);
        return requestId;
    }

    function rawFulfillRandomNumbers(
        uint256 requestId,
        uint256[] memory randomNumbers
    ) external override {
        require(msg.sender == address(coordinator), "Only VRF coordinator");
        Request storage request = requests[requestId];
        require(request.requester != address(0), "Invalid request");
        require(!request.fulfilled, "Already fulfilled");

        request.fulfilled = true;
        request.result = randomNumbers[0];

        // Process the result for the user
        _processRandomness(request.requester, randomNumbers[0]);
    }

    function _processRandomness(address user, uint256 randomNumber) internal {
        // Your custom logic here
    }
}
```

### Multiple Random Numbers

Request and handle multiple random values in one call:

```solidity
contract MultiRandomConsumer is IVRFConsumer {
    IVRFCoordinator public coordinator;

    event LotteryDrawn(uint256[] winningNumbers);

    function drawLottery() external returns (uint256) {
        // Request 6 random numbers for lottery
        return coordinator.requestRandomNumbers(6, block.timestamp);
    }

    function rawFulfillRandomNumbers(
        uint256 requestId,
        uint256[] memory randomNumbers
    ) external override {
        require(msg.sender == address(coordinator), "Only VRF coordinator");
        require(randomNumbers.length == 6, "Expected 6 numbers");

        uint256[] memory lotteryNumbers = new uint256[](6);
        for (uint i = 0; i < 6; i++) {
            // Generate lottery numbers 1-49
            lotteryNumbers[i] = (randomNumbers[i] % 49) + 1;
        }

        emit LotteryDrawn(lotteryNumbers);
    }
}
```

## Common Use Cases

### Fair NFT Minting

```solidity
contract FairNFTMint is IVRFConsumer, ERC721 {
    IVRFCoordinator public coordinator;

    uint256 public constant MAX_SUPPLY = 10000;
    uint256 public totalMinted = 0;

    mapping(uint256 => address) public mintRequests;
    mapping(uint256 => uint256) public tokenTraits;

    function requestMint() external payable {
        require(msg.value >= 0.1 ether, "Insufficient payment");
        require(totalMinted < MAX_SUPPLY, "Sold out");

        uint256 requestId = coordinator.requestRandomNumbers(1, totalMinted);
        mintRequests[requestId] = msg.sender;
    }

    function rawFulfillRandomNumbers(
        uint256 requestId,
        uint256[] memory randomNumbers
    ) external override {
        require(msg.sender == address(coordinator), "Only VRF coordinator");

        address minter = mintRequests[requestId];
        require(minter != address(0), "Invalid request");

        uint256 tokenId = totalMinted++;
        uint256 traits = randomNumbers[0];

        tokenTraits[tokenId] = traits;
        _mint(minter, tokenId);

        delete mintRequests[requestId];
    }
}
```

### Random Rewards Distribution

```solidity
contract RandomRewards is IVRFConsumer {
    IVRFCoordinator public coordinator;
    IERC20 public rewardToken;

    address[] public participants;
    mapping(uint256 => uint256) public pendingRewards;

    function distributeRewards(uint256 totalReward) external {
        require(participants.length > 0, "No participants");

        uint256 requestId = coordinator.requestRandomNumbers(
            uint32(participants.length),
            block.timestamp
        );
        pendingRewards[requestId] = totalReward;
    }

    function rawFulfillRandomNumbers(
        uint256 requestId,
        uint256[] memory randomNumbers
    ) external override {
        require(msg.sender == address(coordinator), "Only VRF coordinator");

        uint256 totalReward = pendingRewards[requestId];
        require(totalReward > 0, "No pending reward");

        // Distribute proportionally based on random weights
        uint256 totalWeight = 0;
        for (uint i = 0; i < randomNumbers.length; i++) {
            totalWeight += randomNumbers[i] % 1000;
        }

        for (uint i = 0; i < participants.length; i++) {
            uint256 share = (totalReward * (randomNumbers[i] % 1000)) / totalWeight;
            rewardToken.transfer(participants[i], share);
        }

        delete pendingRewards[requestId];
    }
}
```

## Security Considerations

### Access Control

Always verify the caller is the VRF coordinator:

```solidity
modifier onlyVRFCoordinator() {
    require(msg.sender == address(coordinator), "Only VRF coordinator");
    _;
}

function rawFulfillRandomNumbers(
    uint256 requestId,
    uint256[] memory randomNumbers
) external override onlyVRFCoordinator {
    // Your logic here
}
```

### Request Validation

Validate requests before processing:

```solidity
function rawFulfillRandomNumbers(
    uint256 requestId,
    uint256[] memory randomNumbers
) external override onlyVRFCoordinator {
    // Check request exists
    require(requests[requestId].requester != address(0), "Unknown request");

    // Check not already fulfilled
    require(!requests[requestId].fulfilled, "Already fulfilled");

    // Check expected number count
    require(randomNumbers.length == expectedCount[requestId], "Wrong count");

    // Process...
}
```

### Reentrancy Protection

Use checks-effects-interactions pattern:

```solidity
function rawFulfillRandomNumbers(
    uint256 requestId,
    uint256[] memory randomNumbers
) external override onlyVRFCoordinator nonReentrant {
    // 1. Checks
    require(requests[requestId].valid, "Invalid request");

    // 2. Effects
    requests[requestId].fulfilled = true;
    requests[requestId].result = randomNumbers[0];

    // 3. Interactions
    if (callbacks[requestId] != address(0)) {
        ICallback(callbacks[requestId]).onRandomness(randomNumbers[0]);
    }
}
```

## Best Practices

1. **Always validate** the VRF coordinator address
2. **Track request state** to prevent double processing
3. **Handle failures gracefully** with fallback mechanisms
4. **Use events** for off-chain monitoring
5. **Test thoroughly** with mock VRF before mainnet
6. **Consider gas costs** when requesting multiple numbers
7. **Implement timeouts** for unfulfilled requests
