diff --git a/docs/dev/tutorials/ftso/getting-random-numbers.md b/docs/dev/tutorials/ftso/getting-random-numbers.md
new file mode 100644
index 000000000..26cc83e97
--- /dev/null
+++ b/docs/dev/tutorials/ftso/getting-random-numbers.md
@@ -0,0 +1,111 @@
+# Getting Random Numbers
+
+This tutorial shows how to obtain random numbers from the [Flare Systems Protocol (FSP)](../../../tech/flare-systems-protocol.md), the infrastructure that powers most current Flare protocols.
+The source of the randomness is the submissions from all [FTSO data providers](../../../tech/ftso.md#procedure-overview) 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](../../../tech/ftso.md#vote-power).
+
+!!! info "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](../../getting-started/setup/index.md).
+
+For easy navigation, numbered comments in the source code (e.g. `// 1.`) link to the tutorial sections below.
+
+{% import "runner.md" as runner with context %}
+
+=== "Solidity"
+
+ {{ runner.sol("ftso/", "GetRandomNumber") | indent(4) }}
+
+=== "JavaScript"
+
+ {{ runner.js("ftso/", "GetRandomNumber", runFromBrowser='false') | indent(4) }}
+
+
+
+
+
+## Tutorial
+
+### 1. Import Dependencies
+
+The tutorial uses the following dependencies:
+
+* The [Flare Periphery Package](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contracts) for Solidity and the [Flare Periphery Artifacts Package](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contract-artifacts) for JavaScript, which provide the API for all Flare smart contracts.
+
+* If you use JavaScript, the [ethers](https://www.npmjs.com/package/ethers) package is also needed to work with smart contracts.
+
+{{ runner.multisnippet("ftso/GetRandomNumber", 3, 5, 8, 11) }}
+
+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`](FlareContractRegistry.md) contains the current addresses for all Flare smart contracts, and it is [the only recommended way](../../getting-started/contract-addresses.md) to retrieve them.
+
+=== "Solidity"
+
+ The `FlareContractsRegistryLibrary` contract from the Flare Periphery Package accesses the Flare Contract Registry for you, as shown next.
+
+=== "JavaScript"
+
+ The address of the Flare Contract Registry is the same on all of [Flare's networks](../../../tech/flare.md#flare-networks), and it is the only Flare address that needs to be hard-coded into any program.
+
+ ```javascript title="GetRandomNumber.js" linenums="3"
+ --8<-- "./docs/samples/ftso/GetRandomNumber.js:3:4"
+ ```
+
+ ```javascript title="GetRandomNumber.js" linenums="13"
+ --8<-- "./docs/samples/ftso/GetRandomNumber.js:13:17"
+ ```
+
+### 3. Retrieve the Relay Contract
+
+Use the [`getContractAddressByName()`](FlareContractRegistry.md#fn_getcontractaddressbyname_82760fca) method of the [`FlareContractRegistry`](FlareContractRegistry.md) smart contract to retrieve the address of the `Relay` smart contract.
+
+{{ runner.multisnippet("ftso/GetRandomNumber", 9, 11, 20, 27) }}
+
+### 4. Get the Random Number
+
+Get the latest generated random number by calling the `getRandomNumber()` method of the `Relay` contract.
+
+{{ runner.multisnippet("ftso/GetRandomNumber", 12, 15, 30, 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 to `false`, and dapps can decide whether they should discard the generated number.
+
+* `timestamp` is the [UNIX timestamp](https://en.wikipedia.org/wiki/Unix_time) 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](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contracts) and from [JavaScript](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contract-artifacts), to work with the Flare API.
+* How to get the latest random number via the `Relay` contract.
diff --git a/docs/dev/tutorials/ftso/index.md b/docs/dev/tutorials/ftso/index.md
index 79af91f28..fb9fc983b 100644
--- a/docs/dev/tutorials/ftso/index.md
+++ b/docs/dev/tutorials/ftso/index.md
@@ -6,3 +6,4 @@ These code samples and explanations show how to use the [FTSO system](../../../t
* [FTSO Developer Overview](../../../dev/reference/ftso.md)
* [Getting FTSO Data Feeds](../ftso/getting-data-feeds.md)
+* [Getting Random Numbers](../ftso/getting-random-numbers.md)
diff --git a/docs/samples/ftso/GetRandomNumber.js b/docs/samples/ftso/GetRandomNumber.js
new file mode 100644
index 000000000..d4e495251
--- /dev/null
+++ b/docs/samples/ftso/GetRandomNumber.js
@@ -0,0 +1,36 @@
+const FLARE_CONTRACTS = "@flarenetwork/flare-periphery-contract-artifacts";
+const FLARE_RPC = "https://coston-api.flare.network/ext/C/rpc";
+const FLARE_CONTRACT_REGISTRY_ADDR =
+ "0xaD67FE66660Fb8dFE9d6b1b4240d8650e30F6019";
+
+async function runGetRandomNumber() {
+ // 1. Import Dependencies
+ const ethers = await import("ethers");
+ const flare = await import(FLARE_CONTRACTS);
+ const provider = new ethers.JsonRpcProvider(FLARE_RPC);
+
+ // 2. Access the Contract Registry
+ const flareContractRegistry = new ethers.Contract(
+ FLARE_CONTRACT_REGISTRY_ADDR,
+ flare.nameToAbi("FlareContractRegistry", "coston").data,
+ provider
+ );
+
+ // 3. Retrieve the Relay Contract
+ const relayAddress = await flareContractRegistry.getContractAddressByName(
+ "Relay"
+ );
+ const relay = new ethers.Contract(
+ relayAddress,
+ flare.nameToAbi("IRelay", "coston").data,
+ provider
+ );
+
+ // 4. Get the Random Number
+ const [randomNumber, isSecure, timestamp] = await relay.getRandomNumber();
+ console.log("Random Number is", randomNumber);
+ console.log("Is it secure", isSecure);
+ console.log("Creation timestamp is", timestamp);
+}
+
+runGetRandomNumber();
\ No newline at end of file
diff --git a/docs/samples/ftso/GetRandomNumber.sol b/docs/samples/ftso/GetRandomNumber.sol
new file mode 100644
index 000000000..e65b1bef3
--- /dev/null
+++ b/docs/samples/ftso/GetRandomNumber.sol
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: MIT
+pragma solidity >=0.7.6 <0.9;
+
+import {IRelay} from "@flarenetwork/flare-periphery-contracts/coston/util-contracts/userInterfaces/IRelay.sol";
+import {FlareContractsRegistryLibrary} from "@flarenetwork/flare-periphery-contracts/coston/util-contracts/ContractRegistryLibrary.sol";
+
+contract GetRandomNumber {
+ function generateNumber() external view returns (uint256, bool, uint256) {
+ address relayAddress =
+ FlareContractsRegistryLibrary.getContractAddressByName("Relay");
+ IRelay relay = IRelay(relayAddress);
+ (uint256 randomNumber, bool isSecure, uint256 timestamp) =
+ relay.getRandomNumber();
+
+ return (randomNumber, isSecure, timestamp);
+ }
+}
\ No newline at end of file
diff --git a/docs/samples/ftso/GetRandomNumber.t.sol b/docs/samples/ftso/GetRandomNumber.t.sol
new file mode 100644
index 000000000..c39e6dc67
--- /dev/null
+++ b/docs/samples/ftso/GetRandomNumber.t.sol
@@ -0,0 +1,33 @@
+// 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"
+ );
+ }
+}
\ No newline at end of file
diff --git a/docs/samples/ftso/TestGetRandomNumber.js b/docs/samples/ftso/TestGetRandomNumber.js
new file mode 100644
index 000000000..e7e6e4b8c
--- /dev/null
+++ b/docs/samples/ftso/TestGetRandomNumber.js
@@ -0,0 +1,16 @@
+const { 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);
+ });
+});
\ No newline at end of file
diff --git a/include/runner.md b/include/runner.md
index d136e8f49..dab89462c 100644
--- a/include/runner.md
+++ b/include/runner.md
@@ -20,7 +20,7 @@
3. Initialize project and install dependencies with:
```bash
npm init
- npm install ethers@6.3 @flarenetwork/flare-periphery-contract-artifacts@0.1.7
+ npm install ethers@6.3 @flarenetwork/flare-periphery-contract-artifacts@0.1.15
```
4. Run the program with:
```bash
diff --git a/mkdocs.yml b/mkdocs.yml
index 1f4b97c56..0f614c936 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -232,6 +232,7 @@ nav:
- dev/tutorials/ftso/index.md
- dev/reference/ftso.md
- dev/tutorials/ftso/getting-data-feeds.md
+ - dev/tutorials/ftso/getting-random-numbers.md
- State Connector:
- dev/tutorials/sc/index.md
- dev/tutorials/sc/address-validity.md