Medium Stakes

Exclusive randomness for your contract

Security
8.5
1
Request
Bound to your contract
2
Wait
~70-80 seconds
3
Consume
Only you can access
TX
Request
BOT
Keeper
Past
+
Future
E
Entropy
E'
Consume

Same 32-block entropy as Low Stakes, plus consumer-exclusive consume step

Only your contract can call consumeEntropy() to get a unique derived value (one-time use)

Cost
10K
PLS
Consume
Free
included
Wait Time
~75s
seconds
Difficulty
Medium
to integrate

Security Features

Consumer Lock
Only you can consume
Anti-Grinding
One request per action
Anti-Squatting
Authorization system
Salt Derivation
Extra randomness layer
Great For
Raffles
Fair winner selection
Small Prizes
Under $10k value
NFT Reveals
With anti-grinding
Game Rounds
Per-session randomness
Not For
Big Lotteries
Use High Stakes
High-Value Prizes
Over $10k
Cosmetic Only
Low Stakes cheaper
Last-Mover Games
Need commit-reveal
For Developers

Integration Guide

Consumer-bound entropy with VEOConsumerBase

1Contracts

VE
VEO
Main contract
VE
VEOConsumerBase
Helper base contract

2Using VEOConsumerBase (Recommended)

MyGame.sol
import {VEOConsumerBase} from "./VEOConsumerBase.sol";

contract MyGame is VEOConsumerBase {
    constructor(address _veo)
        VEOConsumerBase(_veo, true) {} // true = enable anti-squatting

    function startGame(bytes32 gameId) external payable {
        // Request entropy bound to this contract
        bytes32 requestId = requestRandom{value: msg.value}(gameId);
    }

    function finishGame(bytes32 requestId) external {
        // Consume and trigger callback
        finalizeRandom(requestId);
    }

    function _onEntropy(
        bytes32 requestId,
        address requester,
        bytes32 entropy,
        bytes32 gameId
    ) internal override {
        // Your game logic here
        uint256 result = uint256(entropy) % 100;
    }
}

3Direct Integration (Advanced)

Solidity
// Setup: Enable authorization in constructor
veo.setRequiresAuthorization(true);
veo.setRequesterAuthorization(address(this), true);

// 1. Request (bound to your contract + actionId)
bytes32 actionId = keccak256(abi.encodePacked("game", gameId));
bytes32 requestId = veo.requestEntropyForConsumer{value: price}(
    address(this),  // consumer = your contract
    actionId        // one active request per actionId
);

// 2. Wait for keeper fulfillment (~75 seconds)

// 3. Consume (only your contract can call this)
bytes32 salt = keccak256(abi.encodePacked(address(this), requestId, block.chainid));
bytes32 entropy = veo.consumeEntropy(requestId, salt);

4All Functions

Request (Consumer-Bound)
requestEntropyForConsumer(consumer, actionId)requestEntropyBulkForConsumer(consumer, actionIds[])activeRequest(consumer, actionId) → bytes32
Consume (Only Consumer)
consumeEntropy(requestId, salt) → bytes32consumeEntropyBulk(requestIds[], salt) → bytes32[]canConsume(requestId) → (bool, string)
Authorization
setRequiresAuthorization(bool)setRequesterAuthorization(addr, bool)isRequesterAuthorized(consumer, requester)
Events
EntropyRequested(requestId, requester, consumer, ...)EntropyConsumed(requestId, consumer, requester, entropy)
Integration Pattern

Working with External Keepers

Let keepers handle fulfillment while you focus on your app

How It Works
1
Your App
requestEntropyForConsumer()
2
Keeper
fulfillEntropy()
3
Poll
getEntropy()
4
Derive
On-chain or Off-chain

Keepers call fulfillEntropy() regardless of tier - they don't need to know if it's Low or Medium

Two Ways to Get Derived Entropy
A
On-Chain (Solidity)
Call consumeEntropy()
Marks entropy as consumed on-chain
Trustless verification
Costs gas (~50k)
Best for: Smart contracts, lotteries, trustless apps
B
Off-Chain (JavaScript)
Compute same formula client-side
Identical derived entropy value
Zero gas cost
Faster UX (no tx confirmation)
Best for: Frontend apps, games, visual generators

AOn-Chain: Wait for Keeper, Then Consume

MyGame.sol
contract MyGame {
    IVEO public veo;
    mapping(bytes32 => bool) public pendingRequests;

    function startGame(bytes32 gameId) external payable {
        bytes32 actionId = keccak256(abi.encodePacked("game", gameId));
        bytes32 requestId = veo.requestEntropyForConsumer{value: msg.value}(
            address(this),
            actionId
        );
        pendingRequests[requestId] = true;
        // Keeper will call fulfillEntropy() - you don't need to!
    }

    function finishGame(bytes32 requestId) external {
        require(pendingRequests[requestId], "Not pending");

        // Check if keeper has fulfilled (optional - consumeEntropy will revert if not)
        (bytes32 entropy, bool fulfilled) = veo.getEntropy(requestId);
        require(fulfilled, "Wait for keeper");

        // Consume to get derived entropy (only your contract can call this)
        bytes32 salt = keccak256(abi.encodePacked(address(this), requestId, block.chainid));
        bytes32 derivedEntropy = veo.consumeEntropy(requestId, salt);

        pendingRequests[requestId] = false;

        // Use the entropy
        uint256 result = uint256(derivedEntropy) % 100;
    }
}

BOff-Chain: Poll and Derive Client-Side

app.js
import { keccak256, AbiCoder, solidityPackedKeccak256 } from "ethers";

// Track pending requests locally
const pendingRequests = new Map(); // requestId -> { userAddress, chainId }

// 1. Request entropy (Medium tier)
async function requestEntropy(userAddress, actionId) {
    const price = await veo.getPrice();
    const tx = await veo.requestEntropyForConsumer(userAddress, actionId, { value: price });
    const receipt = await tx.wait();

    // Parse EntropyRequested event to get requestId
    const event = receipt.logs.find(log => {
        const parsed = veo.interface.parseLog(log);
        return parsed?.name === "EntropyRequested";
    });
    const requestId = veo.interface.parseLog(event).args.requestId;

    // Track locally for keeper detection
    const chainId = (await provider.getNetwork()).chainId;
    pendingRequests.set(requestId, { userAddress, chainId });

    return requestId;
}

// 2. Poll for keeper fulfillment
async function pollForFulfillment() {
    for (const [requestId, { userAddress, chainId }] of pendingRequests) {
        const [baseEntropy, fulfilled] = await veo.getEntropy(requestId);

        if (fulfilled) {
            // 3. Derive entropy client-side (same formula as contract)
            const abiCoder = new AbiCoder();
            const salt = keccak256(
                abiCoder.encode(
                    ["address", "bytes32", "uint256"],
                    [userAddress, requestId, chainId]
                )
            );

            const derivedEntropy = solidityPackedKeccak256(
                ["bytes32", "address", "address", "bytes32", "uint256"],
                [baseEntropy, userAddress, userAddress, salt, chainId]
            );

            pendingRequests.delete(requestId);
            onEntropyReady(derivedEntropy); // Your callback
        }
    }
}

// Poll every 5 seconds
setInterval(pollForFulfillment, 5000);
Key Points
Keepers are tier-agnostic - they just call fulfillEntropy()
Same derived entropy - on-chain and off-chain produce identical values
Consumer binding preserved - only the designated consumer can derive
Track requestIds locally - poll getEntropy() to detect fulfillment

Need MEV protection? Use High Stakes with commit-reveal.