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
RequestBOT
KeeperPast
Future
E
EntropyE'
ConsumeSame 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) → bytes32Consume (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.