future-drand is a pattern for using drand randomness within smart contracts.
The name iterates on "future blockhash" - a flawed, insecure pattern that developers have attempted to use for years.
future-drand is the same concept done properly: commitment to a future value followed by execution.
Using future-drand requires:
- Committing to a future drand beacon value on-chain
- Using the
src/DrandRegistry.solto store and verify that value - Executing the earlier commitment
Minimal example:
pragma solidity ^0.8.30;
import {IDrandRegistry} from "future-drand/IDrandRegistry.sol";
import {FutureDrandConsumer} from "future-drand/FutureDrandConsumer.sol";
/// @notice One-shot example with no funds or user-supplied outcome inputs.
contract CoinFlip is FutureDrandConsumer {
uint64 public immutable round;
bool public settled;
bool public heads;
constructor(IDrandRegistry _drand) FutureDrandConsumer(_drand) {
round = _commitFutureRound(0);
}
function settle(bytes calldata signature) external returns (bool) {
require(!settled);
settled = true;
_acceptCarriedProof(round, signature);
heads = _deriveRandomness(round, bytes32("coinflip")) % 2 == 0;
return heads;
}
}forge install future-drand/future-drandimport {FutureDrandConsumer} from "future-drand/FutureDrandConsumer.sol";drand is a distributed randomness beacon operated by the League of Entropy.
The League of Entropy is a group of orgs including Cloudflare, the Ethereum Foundation and many others.
drand's quicknet network publishes a new beacon (random value) every 3 seconds, which anyone can verify on-chain.
Disclaimer: future-drand is not associated with drand or the LoE
src/DrandRegistry.sol- verification + permanent round cachesrc/BLS12381.sol- minimal EIP-2537 precompile wrapper (decompression, hash-to-curve, pairing)src/IDrandRegistry.sol- consumer interfacesrc/FutureDrandConsumer.sol- optional consumer baseexamples/- bankroll, lottery and bounty modestest/- official-vector, malleability, freshness and fuzz suites
Canonical address: 0xbeac0377D6fe8B753767616749AEe539E80F6bE6
Etherscan: 0xbeac0377D6fe8B753767616749AEe539E80F6bE6
Deployed chains: contractscan.xyz/contract/0xbeac0377D6fe8B753767616749AEe539E80F6bE6
Deploy it to any EIP-2537 chain using Arachnid:
cast send --private-key $DEPLOYER_KEY 0x4e59b44847b379578588920cA78FbF26c0B4956C "0x000000000000000000000000000000000000000000000000000000007da4798b$(forge inspect src/DrandRegistry.sol:DrandRegistry bytecode | cut -c3-)"Init code hash: 0x16134ab9154febf44b1f9e1806385abbcd31adda84dbd2d1a72b80e7bf73f1d2
The factory, salt, hash and address are pinned in CI by test/integration/DeploymentIdentity.t.sol.
- drand randomness is safe and verifiable unless a threshold of League of Entropy operators collude
- If drand itself migrates or halts, this version of the protocol expires and a new deployment is required. Recovery is the consumer's responsibility
- BLS verification is a minimal in-repo wrapper over the EIP-2537 precompiles (
src/BLS12381.sol); curve, subgroup and canonicality checks are the precompiles' mandated behaviour - Consumers are responsible for appropriate reveal timing (see below) and this advice may become invalidated by future updates
- Everything that selects an outcome (salt, participants, payout rules) must be committed before the target round exists - an input chosen after the beacon is public can be mined to a favorable result
- These contracts are unaudited. See
SECURITY.md
A commitment is only secure if it targets a round of drand that is genuinely in the future, so we must carefully consider how block.timestamp is inserted into blocks.
The optional minRequestableRound() buffer (19 seconds) is a recommended floor, analyzed below.
Rounds tick every 3 seconds and a timestamp lands 0-2s into the current round, so an N-round buffer places its target 3N-2 to 3N seconds after the timestamp. Durations below quote the floor.
block.timestamp is consensus-enforced to exact slot times and can be trusted not to lag.
Slots and rounds share a fixed phase, so an N-round buffer targets a value published exactly 3N-2 seconds after the timestamp.
An attacker can cheat by withholding a block until the beacon resolves, publishing it only when favorable. To keep that option alive past the slot boundary, the next proposer must also be theirs.
Therefore, an N-round buffer requires k = 1 + floor((3N-2) / 12) consecutive proposers.
Consumers must choose an appropriate buffer based on the value at risk in a single block.
Given one coordinated operator controlling 10% (~$7 billion of staked ETH) or 2% (~$1.4 billion) of Ethereum's proposers:
| Min. rounds | Attack requires | Cost per attempt | Window @ 10% | Window @ 2% |
|---|---|---|---|---|
| 7 (19s) | 2 consecutive slots | ~$480 | ~20 min | ~8 hours |
| 11 (31s) | 3 consecutive slots | ~$720 | ~3 hours | ~17 days |
| 15 (43s) | 4 consecutive slots | ~$960 | ~1.4 days | ~2.4 years |
| 23 (67s) | 6 consecutive slots | ~$1,440 | ~5 months | ~5,900 years |
| 31 (91s) | 8 consecutive slots | ~$1,920 | ~38 years | ~15 million years |
Proposer assignments are known minutes in advance, so an attacker waits for a run of their own slots rather than forcing one - the windows above are the average wait between chances. Each row leaves the first honest proposer 7 seconds to publish, but if that slot is missed or late, the attacker needs one fewer of their own. A failed attempt forfeits the run's rewards and MEV, and leaves missed slots on-chain that can later be correlated with winning bets.
The deployed default is 7 rounds. Consumers whose single-block value at risk justifies a rarer window should raise _extraRounds - every additional slot (12 seconds) increases the wait 10x for the 10% operator, 50x at 2%. By a 2 minute buffer even the 10% operator waits ~3,800 years, and past two finalized epochs (~13 minutes) the commit block is final before the beacon exists and the attack becomes impossible.
Note: this research is unverified and every L2 is different - use on L2s at your own risk.
Typically, block.timestamp is controlled by a single sequencer.
Base (OP Stack) and Robinhood Chain (Arbitrum Nitro) are slightly different:
| Base (OP Stack) | Robinhood Chain (Arbitrum Nitro) | |
|---|---|---|
block.timestamp |
genesis + 2 x block number, a counter |
asserted by the sequencer, monotonic |
| how it can lag | only by halting block production - a visible outage | silently, inside a 96h permitted envelope |
| observed skew (informal sample) | +0.04s | -1.50s |
On either chain, an innocently lagging sequencer clock could make the recommended target unsafe by default.
However, a beacon cannot exist before the League of Entropy signs it, so every posted beacon is proof of real time:
// a beacon exists for a time this chain claims has not happened yet
uint64 anchor = drand.maxPostedRound(); // 0 until the first beacon is posted
bool clockLagging = anchor != 0 && drand.roundTimestamp(anchor) > block.timestamp;Consumers can reduce reliance on the sequencer clock by posting a recent beacon and committing in the same transaction:
_acceptCarriedProof(newestRound, signature); // raise the floor to construction time
round = _commitFutureRound(0); // future relative to the anchorCarrying fresh proof with a commitment protects against innocent clock lag.
A malicious L2 sequencer could withhold the transaction until the buffer elapses, but only if drand.maxPostedRound() stays stale.
If it is kept fresh by users, the sequencer would also have to censor all beacon submissions for a window while attacking.
Either is severe and overt misbehaviour for an L2 sequencer: withholding shows up in inclusion times, and a lagging clock is visible on-chain. A sequencer prepared to do this could also front-run swaps.
Chainlink VRF is the established, status-quo option for on-chain randomness in 2026.
future-drand trades built-in callback and pre-settlement secrecy for permissionless delivery and automatic cost sharing.
| Property | future-drand | Chainlink VRF v2.5 |
|---|---|---|
| Randomness | Public threshold beacon | Request-specific VRF proof |
| Delivery | User, protocol bot, or searcher/bounty | Automatic callback |
| Reuse | Each 3s round is shared via public registry | Request-scoped; applications can build their own batching |
| Setup | Registry; no account or provider funding | Subscription or pre-funded wrapper |
| Migration surface | drand chain identity and public key | Coordinator, key hashes, billing and product version |
| Availability | Any EIP-2537 chain | Requires Chainlink VRF deployment |
| Result visibility | Public the moment the round is signed | Private until callback; operator sees first |
Protocol overhead is one proof submission per round (~204k gas), shared by every outcome that uses that round:
| Outcomes sharing a round | Protocol gas per outcome |
|---|---|
| 1 | ~204k |
| 10 | ~20k |
| 100 | ~2k |
Application settlement runs on top of this and varies by integration.
To estimate dollar cost: gas_price_gwei x eth_price_usd / 1000 = $/M gas
At 0.1 gwei and $2,000/ETH:
- A fresh proof costs ~$0.04
- An outcome sharing a round with 99 others pays ~$0.0004 for its randomness.
At those same example prices, a Chainlink VRF v2.5 subscription request:
- Bills 115k verification gas on top of the application callback (Chainlink's Ethereum example), then a 20-24% premium on both
- A 200k callback therefore bills ~315k gas: ~$0.063 before the premium, ~$0.076-0.078 after
- This overhead repeats per request rather than falling with volume
- Batching is possible, but still protocol independent
Counting protocol overhead only, future-drand is cheaper from 2 outcomes per shared round.
drand operates two relevant 3-second randomness networks:
| Property | quicknet | evmnet |
|---|---|---|
| Launched | 2023 | 2024 |
| Curve | BLS12-381 | BN254 |
| Security | 120+ bits | Approximately 80 bits |
| Signature | 48 bytes compressed | 64 bytes |
| EVM support | Requires EIP-2537 | Works with older BN254 precompiles |
| Main advantage | Stronger security and modern cryptography | Compatibility with older EVM chains |
| Reference verification cost | 140,580 gas | 159,274 gas |
future-drand uses quicknet for greater security and because EIP-2537 is available on most chains:
| Chain | EIP-2537 |
|---|---|
| Ethereum | yes |
| Base | yes |
| Arbitrum One | yes |
| OP Mainnet | yes |
| Robinhood Chain | yes |
| Polygon PoS | yes |
| BNB Chain (BSC) | yes |
| Monad | yes |
| Avalanche C-Chain | no |
| HyperEVM | no |