Why Automata VRF

Automata VRF is a project that generates verifiable random numbers that can be easily integrated into dApps.

Automata VRF may serve as an alternative on a network, which is not supported by Chainlink, for existing projects that have already integrated Chainlink VRF interface in their contracts.

The randomness generation process is executed in a trusted oracle, uses DRAND as a reputable source of entropy, which is then verified by an on-chain AutomataVRFCoordinator contract.

Theoretical example of a dApp that has integrated with Automata VRF contract.

Projects may use one of the two approaches below to integrate Automata VRF.

Approach A: Direct integration with Automata VRF Coordinator

This approach is most intuitive for integrating a trusted on chain oracle for smart contracts, as the contract can simply invoke:

  • getLatestRandomWords(): get at most 2**32 random words at a time.

  • getLatestRandomness(): fetch the latest verifiable randomness, produced by the off chain oracle. Consumers may use this value as a seed to generate one or more random values using more complex algorithms.

Code example of the HundoRando contract using Approach A:

// implements a contract that generates a random number from 1 to 100

import {IAutomataVRFCoordinator} from "@automata-network/contracts/vrf/IAutomataVRFCoordinator.sol";

contract HundoRando {
  IAutomataVRFCoordinator vrfCoordinator;
  uint256 randomNum; // stores a random number between 1 to 100
  uint256 currentRound;
	
  constructor(address _vrfCoordinator) {
    vrfCoordinator = IAutomataVRFCoordinator(_vrfCoordinator);
  }

  function getNewRando() public {
    uint256 newRound = vrfCoordinator.getCurrentRound();
    require(newRound > currentRound, "not the latest rando!");
    currentRound = newRound;
    uint256[] memory randomWords = vrfCoordinator.getLatestRandomWords(uint64(1));
    randomNum = randomWords[0] % 100 + 1;
  }
}

Code example of the HundoRando contract using Approach B:

// implements a contract that generates a random number from 1 to 100

import {VRFConsumerBaseV2} from "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol";
import {VRFCoordinatorV2Interface} from "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";

contract HundoRando is VRFConsumerBaseV2 {
  // stores the AutomataVRFCoordinator address here
  VRFCoordinatorV2Interface vrfCoordinator;

  // stores a random number between 1 to 100
  uint256 randomNum;

  constructor(address _vrfCoordinator) VRFConsumerBaseV2(_vrfCoordinator) {
    vrfCoordinator = VRFCoordinatorV2Interface(_vrfCoordinator);
  }
  
  /// @dev handles rawFulfillRandomWords() callback here
  function fulfillRandomWords(
    uint256 requestId, 
    uint256[] memory randomWords
  ) internal override {
    randomNum = randomWords[0] % 100 + 1;
  }

  function getNewRando() public {
    // most projects would receive the requestId here
    // once integrated/migrated to AutomataVRF, they get the roundId instead
    // it is totally fine to treat a roundId as its own unique requestId
    // because the oracle actively submits new randomness within a defined time period
    // unlike Chainlink VRF, where randomness is only produced when explicitly requested by the consumers
    // in other words, the requestId is not specially created for any particular consumers
      uint256 requestId = vrfCoordinator.requestRandomWords(
      bytes32(0), // NOT-APPLICABLE: keyHash
      uint64(0), // NOT-APPLICABLE: subId
      uint16(0), // NOT-APPLICABLE: minimum request confirmation
      uint32(0), // NOT-APPLICABLE: consumers pay gas directly
      uint32(1) // one random word
    );

    // additional implementation here, usually involves the handling of requestIds
  }
}

Interface that integrators use

abstract contract VRFConsumerBaseV2 {
  error OnlyCoordinatorCanFulfill(address have, address want);
  address private immutable vrfCoordinator;

  /**
   * @param _vrfCoordinator address of VRFCoordinator contract
   */
  constructor(address _vrfCoordinator) {
    vrfCoordinator = _vrfCoordinator;
  }

  /**
   * @notice fulfillRandomness handles the VRF response. Your contract must
   * @notice implement it. See "SECURITY CONSIDERATIONS" above for important
   * @notice principles to keep in mind when implementing your fulfillRandomness
   * @notice method.
   *
   * @dev VRFConsumerBaseV2 expects its subcontracts to have a method with this
   * @dev signature, and will call it once it has verified the proof
   * @dev associated with the randomness. (It is triggered via a call to
   * @dev rawFulfillRandomness, below.)
   *
   * @param requestId The Id initially returned by requestRandomness
   * @param randomWords the VRF output expanded to the requested number of words
   */
  function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal virtual;

  // rawFulfillRandomness is called by VRFCoordinator when it receives a valid VRF
  // proof. rawFulfillRandomness then calls fulfillRandomness, after validating
  // the origin of the call
  function rawFulfillRandomWords(uint256 requestId, uint256[] memory randomWords) external {
    if (msg.sender != vrfCoordinator) {
      revert OnlyCoordinatorCanFulfill(msg.sender, vrfCoordinator);
    }
    fulfillRandomWords(requestId, randomWords);
  }
}

NOTE: If your project integrates Chainlink VRF via the direct funding method, then you may not be able to directly integrate Automata VRF, unless you deploy your own VRFV2Wrapper contract that points to AutomataVRFCoordinator.

Last updated