GOAT Network
Services

GOAT VRF

GOAT VRF uses the drand network to deliver verifiable randomness to EVM contracts on GOAT Network.

Request Flow

VRF request flow
Consumer contract
      |
      v
Request randomness from GoatVRF
      |
      v
Oracle service watches requests
      |
      v
drand proof submitted onchain
      |
      v
Consumer callback receives randomness

Components

ComponentRole
GoatVRF contractVerifies proofs and stores request state
Oracle or relayer servicePulls randomness from drand and submits proofs
Consumer contractRequests randomness and implements the callback

Consumer Integration

To integrate GOAT VRF, implement IRandomnessCallback and call getNewRandom() on the GoatVRF contract.

Consumer contract pattern
// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

contract MyRandomContract is IRandomnessCallback {
  using SafeERC20 for IERC20;

  address public goatVRF;
  address public owner;

  modifier onlyOwner() {
    require(msg.sender == owner, "Only owner");
    _;
  }

  constructor(address goatVRF_) {
    goatVRF = goatVRF_;
    owner = msg.sender;
  }

  function getNewRandom(uint256 maxAllowedGasPrice) external onlyOwner returns (uint256 requestId) {
    address tokenAddress = IGoatVRF(goatVRF).feeToken();
    uint256 callbackGas = 600000;
    uint256 fee = IGoatVRF(goatVRF).calculateFeeWithGasPrice(callbackGas, maxAllowedGasPrice);

    IERC20 token = IERC20(tokenAddress);
    uint256 safetyMargin = fee * 3 / 2;
    token.forceApprove(goatVRF, safetyMargin);

    IDrandBeacon beacon = IDrandBeacon(IGoatVRF(goatVRF).beacon());
    uint256 deadline = block.timestamp + beacon.period();
    requestId = IGoatVRF(goatVRF).getNewRandom(deadline, maxAllowedGasPrice, callbackGas);
  }

  function receiveRandomness(uint256 requestId, uint256 randomness) external override {
    require(msg.sender == goatVRF, "Only GoatVRF can fulfill randomness");

    // Use randomness in your application.
    // For example: selectWinner(requestId, randomness);
  }
}

interface IRandomnessCallback {
  function receiveRandomness(uint256 requestId, uint256 randomness) external;
}

interface IDrandBeacon {
  function period() external view returns (uint256);
}

interface IGoatVRF {
  function calculateFee(uint256 gas) external view returns (uint256 totalFee);

  function calculateFeeWithGasPrice(uint256 gas, uint256 gasPrice) external view returns (uint256 totalFee);

  function getNewRandom(uint256 deadline, uint256 maxAllowedGasPrice, uint256 callbackGas)
    external
    returns (uint256 requestId);

  function cancelRequest(uint256 requestId) external;

  function beacon() external view returns (address beaconAddr);

  function feeToken() external view returns (address tokenAddr);
}

How GOAT VRF Works

Consumer requests randomness

Your contract calls getNewRandom() on the GoatVRF contract, providing a deadline, a maximum gas price you are willing to pay for the callback, and a gas limit for your callback function.

Fee is collected

The GoatVRF contract collects a fee in WGOATBTC tokens to cover the off-chain proof submission and the on-chain callback gas.

Oracle submits a drand proof

The off-chain Oracle service monitors the chain for randomness requests and submits proofs from the drand network.

Callback delivers randomness

When a valid proof is submitted, GoatVRF calls your contract's receiveRandomness() function with the resulting random value.

Implementation Details

Fee Calculation

The randomness fee is based on the callback gas limit and the gas price used for fulfillment.

Calculate VRF fee
uint256 fee = IGoatVRF(goatVRF).calculateFee(600000);
uint256 feeWithLimit = IGoatVRF(goatVRF).calculateFeeWithGasPrice(600000, 0.01 gwei);

Token Approval

The fee token is WGOATBTC. Approve enough allowance before calling getNewRandom, and leave headroom if you expect gas-price movement between estimation and fulfillment.

Approve VRF fee token
address tokenAddress = IGoatVRF(goatVRF).feeToken();
IERC20 token = IERC20(tokenAddress);
token.forceApprove(goatVRF, fee * 3 / 2);

Deadline

Use the drand beacon period to calculate a reasonable fulfillment deadline.

Set request deadline
IDrandBeacon beacon = IDrandBeacon(IGoatVRF(goatVRF).beacon());
uint256 deadline = block.timestamp + beacon.period();

Fee Rule Types

Fee ruleDescription
FixedUses a static fee plus callback gas cost
APRO_BTCAdjusts against the APRO BTC price feed to keep a target USD-denominated value

Configure the active rule through environment variables when deploying GoatVRF:

  • Fixed — set FEE_RULE_TYPE=FIXED and FIXED_FEE=<amount>.
  • APRO_BTC — set FEE_RULE_TYPE=APRO_BTC, TARGET_VALUE=<amount> (in USD with 8 decimals), and PRICE_FEED=<address>. The fee then adjusts automatically with the BTC price to keep a consistent USD value.

The APRO_BTC fee rule contract must be granted permission by APRO before it can be used.

Deployments

Testnet3

PropertyValue
GoatVRF Proxy0xa3aeBE2F0d9daDac9E8111D9D41671A510FFB2ca
Fee Token0xbC10000000000000000000000000000000000000
Beacon TypeBN254
Beacon0x46d74aB88fd5894F82d150ec18912aCC9df80663
Fee Rule TypeAPRO_BTC
Fee Rule0x34fd84eCbFf7B8369b61A34CB6B5666AD3a05F97
Target Value1000000
Price Feed0x0c98A1AAECE12D6815A02fD2A6d24652325FD6Ef
Fee Recipient0xF51d148D4A7ae851c1d5641763081023938c6342
Relayer0x5C71d12522c454689A6A1653A99A44Bd5Fa4A65B
Overhead Gas200000
Max Deadline Delta604800
Request Expire Time604800

Mainnet

PropertyValue
GoatVRF Proxy0x0b481e6133B4e1c489995bF867CA6E5b562c858B
Fee Token0xbC10000000000000000000000000000000000000
Beacon TypeBN254
Beacon0x0EaC9f6B63263038268c675FF7C9Bff6C8B30F76
Fee Rule TypeAPRO_BTC
Fee Rule0xAD2aE757e15BA11e87ad24589bCA077ABE93328C
Target Value1000000
Price Feed0xB587aB45a92e19eE3e0d483bc629DE75Ed575025
Fee Recipient0x53D0A68ed81698239831cd0A09813771A37E7f8F
Relayer0x9DaFB0DC6DCDcD4176a5C420F43C15eBe26305B3
Overhead Gas200000
Max Deadline Delta604800
Request Expire Time604800

The APRO_BTC fee rule adjusts dynamically against the APRO BTC/USD price feed to keep a consistent USD-denominated value. The fee rule contract must be authorized by APRO before use.

Development Commands

Foundry commands
forge build
forge test
forge script script/Deploy.s.sol --rpc-url <rpc_url> --private-key <your_private_key> --broadcast

On this page