Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
Free 30-min Web3 Consultation
Book Consultation
Smart Contract Security Audits
View Audit Services
Custom DeFi Protocol Development
Explore DeFi
Full-Stack Web3 dApp Development
View App Services
LABS
Guides

How to Implement Verifiable Randomness for Experiment Sampling

A technical guide for developers on implementing cryptographically verifiable randomness for selecting control groups and assigning parameters in decentralized experiments.
Chainscore © 2026
introduction
TECHNICAL GUIDE

Introduction to Verifiable Randomness in Experiments

A guide to implementing cryptographically secure, on-chain randomness for sampling and assignment in Web3 experiments and applications.

Verifiable Randomness Functions (VRFs) provide a cryptographically secure method for generating random numbers that can be independently verified. Unlike pseudo-random number generators (PRNGs) used in traditional software, a VRF produces a random output and an accompanying cryptographic proof. This proof allows anyone to verify that the output was generated correctly from a given input and secret key, without revealing the key itself. This property is essential for trustless systems where participants must be confident that a random outcome was not manipulated by the operator.

In the context of experiments—such as A/B testing, participant sampling, or randomized control trials (RCTs) on-chain—VRFs ensure the integrity of the sampling process. For example, when assigning users to a control or treatment group, a VRF guarantees the assignment is provably fair and unpredictable. Leading protocols like Chainlink VRF and Witnet offer this as a service, providing on-chain randomness that is resistant to manipulation by miners, validators, or the oracle nodes themselves. The process typically involves a user contract requesting randomness, the oracle network generating it off-chain, and then delivering the random value and proof back to the chain for verification and use.

Implementing VRF requires a smart contract to request and consume the randomness. Below is a simplified example using a Chainlink VRF v2 consumer contract. The key steps are: requesting randomness by paying a fee and providing a job ID, receiving the callback with the random number, and then using it for your application logic, such as sampling.

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";

contract ExperimentSampler is VRFConsumerBaseV2 {
    VRFCoordinatorV2Interface COORDINATOR;
    uint64 s_subscriptionId;
    bytes32 s_keyHash;
    uint32 callbackGasLimit = 100000;
    uint16 requestConfirmations = 3;
    uint32 numWords = 1;
    uint256 public randomResult;
    address public requester;

    constructor(
        uint64 subscriptionId,
        address vrfCoordinator,
        bytes32 keyHash
    ) VRFConsumerBaseV2(vrfCoordinator) {
        COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
        s_subscriptionId = subscriptionId;
        s_keyHash = keyHash;
    }

    function requestRandomSample() external returns (uint256 requestId) {
        requester = msg.sender;
        requestId = COORDINATOR.requestRandomWords(
            s_keyHash,
            s_subscriptionId,
            requestConfirmations,
            callbackGasLimit,
            numWords
        );
        return requestId;
    }

    function fulfillRandomWords(uint256, uint256[] memory randomWords) internal override {
        randomResult = randomWords[0];
        // Use randomResult for sampling logic, e.g., assign to group A if result % 2 == 0
    }
}

When designing an experiment, you must decide on the sampling frame (e.g., all wallet holders of an NFT) and the assignment logic. The random seed, provided by the VRF, is the input to this logic. Common techniques include using modulo operations (randomValue % totalParticipants) to select an index or hashing the random value with a participant's address for a deterministic yet fair assignment. It's critical to store the random result and any derived assignments on-chain to maintain a permanent, auditable record. This transparency allows for post-experiment verification, where any third party can replay the assignment logic with the published random seed to confirm its correctness.

Security considerations are paramount. The main cost is the oracle gas fee, which must be managed. You must also ensure your contract's fulfillRandomWords callback has sufficient gas (callbackGasLimit) and logic to handle the random result without failing. A failed callback can stall your experiment. Furthermore, while the random number itself is secure, the use of that number in your contract must also be secure. For instance, avoid logic where a participant can revert a transaction if they don't like the random outcome they see in a mempool. Using a commit-reveal scheme or requiring the randomness to be used in a subsequent transaction can mitigate this.

Verifiable randomness moves experiments from being trust-based to being proof-based. This is a foundational shift for decentralized science (DeSci), on-chain games, and fair distribution mechanisms. By leveraging VRFs, researchers and developers can create experiments where the sampling methodology is as robust and transparent as the results, enabling truly credible and collaborative research in the Web3 ecosystem.

prerequisites
IMPLEMENTING VRF

Prerequisites and Setup

This guide details the technical prerequisites and initial setup required to implement verifiable randomness for experiment sampling using Chainlink VRF.

Verifiable Random Function (VRF) is a cryptographic tool that provides provably fair and tamper-proof random numbers on-chain. For experiment sampling—such as A/B testing, randomized control trials, or NFT minting—it's critical that the selection process is unbiased and cannot be manipulated by users, miners, or the protocol itself. Chainlink VRF is the industry standard for this, generating randomness that is both unpredictable and publicly verifiable. You'll need a basic understanding of smart contract development with Solidity and familiarity with your chosen blockchain's testnet environment.

Before writing any code, you must secure the necessary resources. First, obtain testnet LINK tokens from a Chainlink Faucet to pay for randomness requests. You will also need a funded wallet on a supported testnet like Sepolia or Polygon Mumbai. Second, create a subscription on the Chainlink VRF portal. The subscription model allows you to prepay for multiple VRF requests, simplifying management and gas costs. Note your Subscription ID and the VRF Coordinator address for your network, as these are required for your contract.

Your smart contract must import and interact with the VRF Coordinator. Start by installing the necessary packages. Using Foundry, run forge install smartcontractkit/chainlink-brownie-contracts. With Hardhat, install @chainlink/contracts. Your contract needs to inherit from VRFConsumerBaseV2 and define two key functions: one to request randomness and another to receive and use it (the fulfillRandomWords callback). You must also store the key hash (gas lane) and callback gas limit specific to your network, which you can find in the Chainlink documentation.

A critical setup step is configuring the callback gas limit appropriately. This limit must be high enough to cover the execution of your fulfillRandomWords function, which performs the actual experiment sampling logic (e.g., selecting a group from a pool). Underestimating this is a common failure point. For complex sampling, a limit of 200,000-500,000 gas may be necessary. Test this thoroughly on a testnet. Additionally, ensure your contract has a secure access control mechanism (like OpenZeppelin's Ownable) for the request function to prevent unauthorized usage and LINK drain.

Finally, deploy your contract to a testnet and fund your Chainlink subscription with LINK. You can then simulate the full flow: call your request function, wait for the VRF coordinator to respond (typically within a few blocks), and verify that the callback executes and your sampling logic works correctly. Use a block explorer to confirm the randomness request and fulfillment transactions. This end-to-end test validates your setup before moving to mainnet, where costs are real and mistakes are permanent.

key-concepts-text
CORE CONCEPTS: VRF AND RANDOMNESS ORACLES

Implementing Verifiable Randomness for Experiment Sampling

This guide explains how to use Verifiable Random Functions (VRFs) from oracles like Chainlink to generate tamper-proof random numbers for on-chain experiments and A/B testing.

Verifiable Random Functions (VRFs) provide cryptographically secure randomness that is both unpredictable and publicly verifiable. Unlike pseudo-random number generators (PRNGs) that can be manipulated by miners or validators, a VRF uses a private key to generate a random number and a cryptographic proof. This proof allows anyone to verify that the number was generated correctly without revealing the key. For on-chain experiments—such as randomizing user groups for an A/B test, selecting NFT traits, or choosing lottery winners—this guarantees fairness and prevents manipulation, which is critical for trustless applications.

To implement VRF for sampling, you first request randomness from a VRF oracle service. With Chainlink VRF, for example, your smart contract calls a requestRandomWords function, which prompts an off-chain oracle node to generate the random number and proof. The oracle then returns the result in a callback to your contract's fulfillRandomWords function. This two-transaction process (request and fulfillment) ensures the random number is not known until after the request transaction is confirmed, preventing front-running. You must fund your contract with LINK tokens to pay for the request.

Here is a basic Solidity example for a contract that requests one random word for sampling two user groups:

solidity
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
contract ExperimentSampler is VRFConsumerBaseV2 {
    uint256 public randomResult;
    constructor(address vrfCoordinator) VRFConsumerBaseV2(vrfCoordinator) {}
    function requestRandomSample(uint64 subId, bytes32 keyHash) external {
        requestRandomWords(keyHash, subId, 3, 100000, 1);
    }
    function fulfillRandomWords(uint256, uint256[] memory randomWords) internal override {
        randomResult = randomWords[0];
        // Use randomResult % 2 to assign users to Group A (0) or Group B (1)
    }
}

When the random number is received, you use it to assign participants. For a simple A/B test with two groups, you could calculate randomResult % 2 to get a 0 or 1. For more complex sampling, like assigning users to one of four cohorts with equal 25% probability, use randomResult % 4. The modulus operation (%) maps the large random integer onto your desired range. It's crucial that the range size is a power of two for perfectly uniform distribution when using modulo, or to use a more robust method like rejection sampling for non-power-of-two ranges to avoid bias.

Key considerations for production use include managing request costs, ensuring your contract has sufficient LINK, and handling the asynchronous nature of the callback. You should also design your sampling logic to be idempotent and secure against reentrancy. For repeated sampling, you may request multiple random words in a single call to save gas. Always verify the randomness on-chain using the provided proof via the coordinator contract. This entire process creates a transparent and auditable trail, which is essential for scientific experiments or any application where procedural fairness must be demonstrable.

PROTOCOL ANALYSIS

Comparison of On-Chain Randomness Solutions

A feature and performance comparison of leading verifiable randomness protocols for on-chain experiment sampling.

Feature / MetricChainlink VRFWitnetAPI3 QRNG

Verifiability Method

Verifiable Random Function (VRF)

Proof of Randomness

Quantum Random Number Generation

On-Chain Proof

Decentralized Oracle Network

Average Request Latency

15-30 sec

45-90 sec

< 5 sec

Request Cost (ETH Mainnet)

$10-50

$2-10

$0.5-2

Anti-Collusion Mechanism

Pre-commitment scheme

Threshold signatures

Multi-provider aggregation

Native Token Required

LINK

WIT

API3

Supported Blockchains

EVM, Solana, Starknet

EVM, Solana

EVM, Polygon, Avalanche

experimental-sampling-logic
IMPLEMENTATION

Step 2: Building Experimental Sampling Logic

This section details how to integrate a verifiable randomness function (VRF) to select participants for on-chain experiments, ensuring the process is transparent and tamper-proof.

The core of experimental sampling is a verifiable random function (VRF). A VRF generates a random number and a cryptographic proof that the number was generated correctly, without bias. For on-chain experiments, you typically request randomness from an oracle like Chainlink VRF or use a native protocol solution like drand. The returned random value becomes the seed for your sampling algorithm, guaranteeing that neither you nor the participants can predict or manipulate who is selected.

Your smart contract must define the eligibility criteria and sampling method. Common approaches include: - Simple random sampling: Using the VRF output to select a subset from a list of eligible addresses. - Stratified sampling: Dividing the population into groups (e.g., by NFT tier) and sampling from each. The logic is executed in a function that is permissioned, often only callable by the experiment owner after the VRF response is received. The selected addresses are then stored on-chain in an array or mapping.

Here is a simplified Solidity example using a VRF input to select winners. This function assumes a list of eligible addresses and a randomWords array provided by a VRF consumer contract.

solidity
function selectSamples(uint256[] memory randomWords) public onlyOwner {
    uint256 numberOfWinners = 3;
    uint256 eligibleCount = eligibleAddresses.length;

    for (uint256 i = 0; i < numberOfWinners; i++) {
        // Use modulo to get an index within the eligible list
        uint256 winnerIndex = randomWords[i] % eligibleCount;
        address winner = eligibleAddresses[winnerIndex];

        // Ensure no duplicate selections (naive example)
        // In production, use a mapping or more robust logic
        selectedWinners.push(winner);
    }
}

This code loops through the provided random words to pick winners, using the modulo operator to map the random number to a valid list index.

After selection, you must emit an event and update the contract state. Emitting an event with the experiment ID, the VRF request ID, and the array of selected addresses creates a permanent, queryable record on the blockchain. The state update should mark the experiment as 'sampled' to prevent re-running the selection. This transparency allows anyone to verify that the sampling was performed correctly using the original VRF proof.

Consider gas optimization and fairness. Sampling a large list on-chain can be expensive. For big populations, consider storing a Merkle root of eligible addresses and proving inclusion off-chain, or sampling in batches. Fairness also requires handling the edge case where the number of eligible participants is less than your desired sample size, and implementing a secure method to prevent duplicate selections in the same sampling round.

Finally, test extensively using forked mainnet environments and VRF mocks. Use tools like Foundry or Hardhat to simulate VRF responses and verify your sampling logic under various conditions (empty list, large list, duplicate random numbers). The integrity of your entire experiment depends on the unbiased and verifiable execution of this step.

verifying-proof-on-chain
IMPLEMENTATION

Step 3: Verifying the Randomness Proof

After generating a random value, the next critical step is to verify the cryptographic proof that ensures the result was generated fairly and cannot be manipulated.

Verification is the process where any participant can independently check that the random number was generated correctly, without needing to trust the entity that produced it. This is done by using the randomness proof—a cryptographic commitment like a VRF proof from Chainlink or a Drand beacon signature—and the public parameters that were agreed upon before the experiment. The verifier inputs this proof and the public seed into a verification function. If the function returns true, it cryptographically confirms that the output is the unique, deterministic result of the input seed and the prover's secret key.

For developers, this means integrating a verification function into your smart contract or backend system. For a Chainlink VRF, your requestRandomWords callback function should verify the proof sent by the oracle. In a Solidity contract using VRF v2, this is handled automatically by the VRFConsumerBaseV2 contract and the fulfillRandomWords function. The key is to ensure your consuming contract only accepts and acts upon a verified response. For a Drand beacon, you would verify the BLS signature of the randomness round against the beacon's public key using a library like drand-client.

A robust verification step must also check for replay attacks and stale data. Always verify that the proof is for the correct request ID (for VRF) or the expected round number (for Drand). In on-chain contexts, this prevents a previously used random value from being submitted again to influence an outcome. Furthermore, implement checks against block timestamp or round expiration to ensure the randomness is sufficiently recent for your application's security requirements.

Consider this simplified pseudocode logic for a verification step:

solidity
function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
    require(requestId == pendingRequestId, "Incorrect request ID");
    require(randomWords.length > 0, "No random words provided");
    // The VRF coordinator's verification happens implicitly before this call.
    uint256 randomSample = randomWords[0] % populationSize;
    _selectSample(randomSample);
}

The require statements are critical pre-conditions, while the actual cryptographic verification is performed by the upstream oracle infrastructure.

Finally, log the verification result and the final random output. This creates a public, auditable trail. Emit an event with the requestId, the verified random value, and a timestamp. This transparency allows third parties, such as experiment participants or auditors, to independently verify the process's integrity long after the selection occurs, reinforcing the trustlessness of the entire sampling methodology.

VERIFIABLE RANDOMNESS

Frequently Asked Questions

Common questions and solutions for developers implementing verifiable randomness (VRF) in on-chain experiments and sampling.

A Verifiable Random Function (VRF) is a cryptographic primitive that produces a random number and a cryptographic proof that the number was generated correctly. For experiment sampling, it ensures participants are selected fairly and the process is tamper-proof.

How it works for sampling:

  1. The protocol submits a request with a seed (e.g., block hash) to the VRF provider (like Chainlink VRF).
  2. The oracle generates a random number and a proof.
  3. The number is used to select a sample from a participant list, often via a modulo operation: selectedIndex = randomNumber % participantCount.
  4. Anyone can verify the proof on-chain to confirm the selection was unbiased and not manipulated by the oracle or the requester.
conclusion
IMPLEMENTATION SUMMARY

Conclusion and Next Steps

This guide has covered the core concepts and practical steps for integrating verifiable randomness into your Web3 experiments. The next phase involves production deployment and exploring advanced use cases.

You now have a functional framework for generating and verifying on-chain randomness using VRF (Verifiable Random Function) providers like Chainlink or API3. The key components are in place: a smart contract to request randomness, a backend service to process the response, and a sampling mechanism for your experiment. Remember that the security of your system hinges on the integrity of the VRF provider and the correct implementation of the callback logic to prevent front-running or manipulation.

For production deployment, rigorous testing is essential. Deploy your contracts to a testnet (e.g., Sepolia, Mumbai) and simulate full request-fulfillment cycles. Use tools like Hardhat or Foundry to write comprehensive tests that verify the randomness is received, the callback executes correctly, and your sampling function distributes selections as expected. Monitor gas costs, as VRF requests and callbacks can be expensive on mainnet.

Consider these advanced implementations to enhance your system. For multi-chain experiments, use a cross-chain messaging protocol like LayerZero or CCIP to coordinate randomness and sampling logic across different networks. To increase transparency, emit events that log the random seed, the requester, and the final sampled results, allowing anyone to cryptographically verify the process off-chain.

The applications for verifiable randomness in Web3 research are expanding. Beyond A/B testing, consider uses like: - Randomizing treasury grant recipients in a DAO - Selecting validators for slashing simulation tests - Assigning users to different fee tier cohorts in a protocol upgrade - Creating provably fair lotteries for NFT or token distribution. Each use case may require tailoring the sampling algorithm and access controls.

To continue your learning, explore the official documentation for major VRF providers: Chainlink VRF v2 and API3 QRNG. For a deeper technical dive into randomness cryptography, research papers on Verifiable Random Functions and commit-reveal schemes provide valuable context. Start with a small, controlled experiment and iterate based on the results and gas economics.