Getting Random Numbers#
This tutorial shows how to obtain random numbers from the Flare Systems Protocol (FSP), the infrastructure that powers most current Flare protocols. The source of the randomness is the submissions from all FTSO data providers and is therefore not centralized.
Random numbers are generated every 90 seconds and can be read directly from a smart contract.
This is useful in several development contexts where secure, fair random numbers are required, such as in games and certain blockchain protocol functionalities such as selecting a random vote power block.
Security and Fairness
A generated random number is tagged as secure if all data providers correctly followed the FTSO protocol and at least one of them is not malicious.
If a number is tagged as secure, then the protocol guarantees its fairness, meaning that it has no bias and all outcomes are equally probable.
This tutorial shows:
- How to obtain a random number.
- How to use the Flare periphery packages to simplify working with the Flare API.
Code#
Choose your preferred programming language and ensure you have a working development environment.
For easy navigation, numbered comments in the source code (e.g. // 1.
) link to the tutorial sections below.
GetRandomNumber.sol | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Building with Hardhat
- Create a new folder and move into it.
- Initialize a new npm project and install dependencies:
We recommend using this version of Hardhat for testing as it is known to work well.
npm init npm install hardhat@2.22.3 @nomicfoundation/hardhat-toolbox @flarenetwork/flare-periphery-contracts
- Create a new Hardhat project (More information in the Hardhat setup guide):
npx hardhat init
- You will not be using the sample project, therefore:
- Remove
contracts/Lock.sol
- Remove
test/Lock.js
- Remove
- Edit
hardhat.config.js
to specify the correct EVM version. Make sure you include the highlighted lines:hardhat.config.jsrequire("@nomicfoundation/hardhat-toolbox"); /** @type import('hardhat/config').HardhatUserConfig */ module.exports = { solidity: { compilers: [{ version: "0.8.17", settings: { evmVersion: "london" }, }], } };
- Copy the Solidity code above into a new file called
GetRandomNumber.sol
in thecontracts
folder. - Compile with:
npx hardhat compile
Testing with Hardhat
Testing smart contracts before deploying them is typically performed by forking the network or by using mock contracts. These instructions quickly show you how to use the former.
- Build the Hardhat project following the previous instructions.
- Include network information in the
hardhat.config.js
file. Make sure you include the highlighted lines:hardhat.config.jsrequire("@nomicfoundation/hardhat-toolbox"); /** @type import('hardhat/config').HardhatUserConfig */ module.exports = { solidity: { compilers: [{ version: "0.8.17", settings: { evmVersion: "london" }, }], }, networks: { hardhat: { forking: { url: 'https://flare-api.flare.network/ext/bc/C/rpc', }, }, }, };
- Copy the code below into a new file called
TestGetRandomNumber.js
in thetest
folder.TestGetRandomNumber.jsconst { expect } = require("chai"); describe("Test Random Number", function () { let contract; beforeEach(async function () { contract = await ethers.deployContract("GetRandomNumber"); }); it("RandomNumber", async function () { const [randomNumber, isSecure, timestamp] = await contract.generateNumber(); expect(randomNumber).to.be.at.least( 1000000000000000000000000000000000000000n ); expect(isSecure).to.be.true; expect(timestamp).to.be.gt(1695817332); }); });
- Run the test with:
npx hardhat test
Building with Foundry
- If you don't have Foundry installed, follow the instructions for your operating system in the Foundry's Installation guide.
- Create a new Foundry project:
This command creates a new directory called
forge init <PROJECT_NAME>
<PROJECT_NAME>
. Use a name that suits your needs. - Move into the project's directory:
cd <PROJECT_NAME>
- Install dependencies with:
forge install flare-foundation/flare-foundry-periphery-package
- Remove the sample project that Foundry created for you, as you do not need it:
- Remove
src/Counter.sol
- Remove
test/Counter.t.sol
- Remove
- Copy the Solidity code above into a new file called
GetRandomNumber.sol
in thesrc
folder. - Open the
foundry.toml
file, and add the following lines at the end:evm_version = "london" remappings = [ "@flarenetwork/flare-periphery-contracts/=lib/flare-foundry-periphery-package/src/"]
- Compile with:
forge build
Testing with Foundry
Testing smart contracts before deploying them is typically performed by forking the network or by using mock contracts. These instructions quickly show you how to use the former.
- Build the Foundry project following the previous instructions.
- Copy the code below into a new file called
GetRandomNumber.t.sol
in thetest
folder.GetRandomNumber.t.sol// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // Import dependencies import "forge-std/Test.sol"; import "../src/GetRandomNumber.sol"; // Test Contract contract GetRandomNumberTest is Test { string private constant FLARE_RPC = "https://flare-api.flare.network/ext/bc/C/rpc"; uint256 private flareFork; function setUp() public { flareFork = vm.createFork(FLARE_RPC); } function testRandomNumber() public { vm.selectFork(flareFork); GetRandomNumber randNumber = new GetRandomNumber(); (uint256 _randomNumber, bool _isSecure, uint256 _timeStamp) = randNumber .getRandomNumber(); assertGt(_randomNumber, 0, "Random Number expected to be > 0"); assertTrue(_isSecure, "Expect to be true"); assertGt( _timestamp, 1695817332, "Timestamp expected to be greater than a known past block" ); } }
- Run the test with:
forge test -vv
GetRandomNumber.js | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
Run with Node.js
This tutorial has been tested with npm v9.5 and Node.js v18.16.
- Create a new folder and move into it.
- Copy & paste the code above into a new file called
GetRandomNumber.js
. - Initialize project and install dependencies with:
npm init npm install ethers@6.3 @flarenetwork/flare-periphery-contract-artifacts@0.1.15
- Run the program with:
node GetRandomNumber.js
Tutorial#
1. Import Dependencies#
The tutorial uses the following dependencies:
-
The Flare Periphery Package for Solidity and the Flare Periphery Artifacts Package for JavaScript, which provide the API for all Flare smart contracts.
-
If you use JavaScript, the ethers package is also needed to work with smart contracts.
3 4 |
|
8 9 10 |
|
The Periphery Packages simplify working with the Flare smart contracts significantly.
Warning
If you remove this dependency, you must manually provide the signatures for all the methods you want to use.
2. Access the Contract Registry#
The FlareContractRegistry
contains the current addresses for all Flare smart contracts, and it is the only recommended way to retrieve them.
The FlareContractsRegistryLibrary
contract from the Flare Periphery Package accesses the Flare Contract Registry for you, as shown next.
The address of the Flare Contract Registry is the same on all of Flare's networks, and it is the only Flare address that needs to be hard-coded into any program.
GetRandomNumber.js | |
---|---|
3 4 |
|
GetRandomNumber.js | |
---|---|
13 14 15 16 17 |
|
3. Retrieve the Relay Contract#
Use the getContractAddressByName()
method of the FlareContractRegistry
smart contract to retrieve the address of the Relay
smart contract.
9 10 11 |
|
20 21 22 23 24 25 26 27 |
|
4. Get the Random Number#
Get the latest generated random number by calling the getRandomNumber()
method of the Relay
contract.
12 13 14 15 |
|
30 31 32 33 |
|
In addition to the randomNumber
, two other variables are retrieved:
-
isSecure
is a boolean flag that indicates whether the random number was generated securely, according to the description given in the introduction.The random number is based on all the data providers' submissions and is therefore decentralized, improving transparency and fairness. However, this decentralization makes the protocol slightly open to attempts at manipulation. If such manipulation attempts are detected, the
isSecure
flag is set tofalse
, and dapps can decide whether they should discard the generated number. -
timestamp
is the UNIX timestamp indicating the time at the end of the voting epoch during which data was collected from data providers to generate this particular number.The timestamp can be useful, for example, to ensure that certain actions have been performed before the random number was generated. For example, in a roulette game, to ensure all bets were placed before the number was generated. Each voting epoch is a fixed 90-second window.
Conclusion#
This tutorial has shown:
- How to use the Flare Periphery Package, both from Solidity and from JavaScript, to work with the Flare API.
- How to get the latest random number via the
Relay
contract.