Docs/Contracts/Hybrid Vrf

Hybrid VRF Contract

Selective verifiable randomness with AutoLoopHybridVRFCompatible — cheap standard ticks with VRF only when you need it.


Overview

AutoLoopHybridVRFCompatible is the sweet spot for most on-chain games. Standard ticks run at minimal gas cost (~90k). VRF ticks (~240k gas) fire only when your contract requests randomness — for loot drops, critical hits, spawns, or any random event.

Best for: RPGs, roguelikes, loot systems, any game that needs occasional randomness but not on every tick.

How It Works

  1. Your contract overrides _needsVRF(loopID) to decide when VRF is needed
  2. shouldProgressLoop() encodes (needsVRF, loopID, gameData) — the worker reads the flag
  3. On standard ticks: worker sends data as-is, contract calls _onTick() (~90k gas)
  4. On VRF ticks: worker wraps data in a VRF envelope with an ECVRF proof, contract verifies the proof and calls _onVRFTick() (~240k gas)

Integration

import "@luckymachines/autoloop/src/AutoLoopHybridVRFCompatible.sol";

contract MyHybridGame is AutoLoopHybridVRFCompatible {
    uint256 public score;
    uint256 public lastRoll;
    uint256 public interval;
    uint256 public lastTimeStamp;
    uint256 private _loopID;

    constructor(uint256 _interval) {
        interval = _interval;
        lastTimeStamp = block.timestamp;
    }

    // When to request VRF (e.g., every 10th tick)
    function _needsVRF(uint256 loopID)
        internal view override returns (bool)
    {
        return loopID % 10 == 0;
    }

    // Readiness check
    function _shouldProgress()
        internal view override
        returns (bool ready, bytes memory gameData)
    {
        ready = (block.timestamp - lastTimeStamp) > interval;
        gameData = abi.encode(score);
    }

    // Standard tick — cheap, no randomness
    function _onTick(bytes memory gameData) internal override {
        lastTimeStamp = block.timestamp;
        ++score;
        ++_loopID;
    }

    // VRF tick — includes verified randomness
    function _onVRFTick(bytes32 randomness, bytes memory gameData)
        internal override
    {
        lastRoll = (uint256(randomness) % 6) + 1;
        lastTimeStamp = block.timestamp;
        score += lastRoll;
        ++_loopID;
    }
}

Key Functions

_needsVRF(uint256 loopID) → bool

Override this to control when VRF randomness is requested. The worker reads this flag from shouldProgressLoop() output.

Examples:

  • return loopID % 10 == 0; — VRF every 10th tick
  • return loopID % 5 == 0; — VRF every 5th tick
  • return true; — VRF on every tick (equivalent to full VRF)

_shouldProgress() → (bool ready, bytes memory gameData)

Override with your timing/readiness logic. Returns whether the loop is ready and any game-specific data to pass through.

_onTick(bytes memory gameData)

Called on standard ticks (when _needsVRF() returns false). This is your cheap game logic — movement, timers, state updates.

_onVRFTick(bytes32 randomness, bytes memory gameData)

Called on VRF ticks with verified randomness. Use randomness for loot tables, dice rolls, critical hits, spawn locations, etc.

ERC-165 Interface

Workers auto-detect hybrid VRF contracts via ERC-165:

bytes4 constant HYBRID_VRF_INTERFACE_ID =
    bytes4(keccak256("AutoLoopHybridVRFCompatible"));

Data Format

The shouldProgressLoop() return value encodes:

abi.encode(bool needsVRF, uint256 loopID, bytes gameData)

For VRF ticks, the worker wraps this in a VRF envelope (>= 640 bytes). The contract detects the format by data size and routes accordingly.

Events

EventWhen
StandardTick(uint256 indexed loopID, uint256 timestamp)On standard (non-VRF) ticks
HybridVRFTick(uint256 indexed loopID, bytes32 randomness, uint256 timestamp)On VRF ticks

Gas Cost

Tick TypeGasCost (2026)
Standard tick~90,000~$0.008
VRF tick~240,000~$0.022
Average (VRF every 10th)~105,000~$0.010

Hybrid VRF is 56% cheaper than full VRF and only 16% more than standard — the ideal balance for games with occasional randomness.

Hybrid VRF is unique to AutoLoop. Chainlink has no equivalent — you would need to manually coordinate Keepers + VRF v2.5 with separate subscriptions and LINK balances, and there is no way to selectively request VRF on certain ticks.