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 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);
}
}
Approach B: Chainlink VRF Interface via the
NOTE: If your project integrates Chainlink VRF via the method, then you may not be able to directly integrate Automata VRF, unless you deploy your own contract that points to AutomataVRFCoordinator.