Table of contents
Imagine a world where blockchain games are truly unpredictable. Generating verifiable randomness on public blockchains has long been a challenge for dApp developers, especially those working on games. Blockchains' deterministic natureācrucial for consensusāmakes it seem like true randomness is impossible.
This post will guide you through a solution using 0rbit, an off-chain oracle service, to bring unpredictable and verifiable randomness to your blockchain applications. Weāll use a simple dice game as an example, but the principles can apply to any dApp that needs randomness.
Whatās the secret?
Weāll leverage 0rbitās GET request to fetch off-chain data (in this case, randomness). The goal is to ensure that every dice roll gives a truly random number, verifiable on-chain. Letās jump right in!
Checkpoint 0:š¦ Initialize the Project
To start, weāll use a starter kit developed by the team at Autonomous Finance. This kit is designed to give developers a head start in building AO dApps, making the setup process quick and letting you focus on the core functionality.
First, open your terminal and run the following command:
npx create-ao-dapp@latest
You'll be prompted to enter the name of your project. After that, select the following configurations to set up your project environment:
After the project setup is complete, navigate to the project and install the necessary dependencies using npm install
.
Checkpoint 1:š Understanding the Folder Structure
Letās focus on the AO Process part of the project for this blog. Specifically, weāll be working on generating a random number and bringing it on-chain using 0rbitās GET request.
Our main focus will be the ao
directory. This part of the project is responsible for handling the processes. We'll cover the frontend setup and interaction in the upcoming blog.
ao/: Root directory for AO processes.
random/: Directory for a specific AO process.
scripts/: Contains utility scripts (build, deploy, and test).
src/: Contains source code for the process.
process_lib.lua: Library for the process.
process.lua: Entry point for the process.
reset_modules.lua: Script to reset preloaded modules.
Checkpoint 2:š Functions for AO Process
Next, weāll implement the core logic to generate and receive a random number using 0rbitās GET request. Weāll write these functions in process_lib.lua
.
local mod = {}
function mod.getNumber()
mod.sendReply(
_0RBT_POINTS,
"Transfer",
{
Action = "Transfer",
Recipient = _0RBIT,
Quantity = FEE_AMOUNT,
["X-Url"] = BASE_URL,
["X-Action"] = "Get-Real-Data"
},
""
)
end
function mod.receiveNumber(msg)
local res = msg.Data
return res[0]
end
function mod.sendReply(target, action, tags, data)
Send({
Target = target,
["Response-For"] = action,
Tags = tags,
Data = data
})
end
return mod
Hereās a quick breakdown:
local mod = {}
: This initializes an empty table containing all the process-related functions.getNumber()
This function initiates the process to request a random number from 0rbit via a GET request.
mod.sendReply(...)
InsidegetNumber
, thesendReply
function is called. This handles sending the request to 0rbit._0RBT_POINTS
: Specifies the target process or address where the request will be sent."Transfer"
: This defines the action being performed, which is a token transfer.{ Action = "Transfer", ... }
: TheTags
parameter includes the transfer details (action, recipient, amount) and metadata for the request, like the URL and action to fetch real-world data.""
: This represents the emptydata
payload being sent, as weāre making a GET request.
function mod.receiveNumber(msg)
This function handles the response received from 0rbit after the GET request.
local res = msg.Data
: Extracts theData
field from the response message, which contains the requested random number.return res[0]
: Returns the first element of theres
array, which is the random number received from the GET request.
function mod.sendReply(...)
This utility function is responsible for sending messages to the AO network. Itās used to handle different types of communication.
Target = target
: The process or address that will receive the message.["Response-For"] = action
: Indicates which action this message is a response to.Tags = tags
: Metadata such as the URL or action tags, depending on what the message is about.Data = data
: The actual content or data to be sent with the message.
return mod
Finally, the
mod
object is returned, exposing the functions defined within the module for use in other parts of the process.
Checkpoint 3:š„ The Process
Now that we have our library functions in place, let's write the main process logic inside src/process.lua
. This file contains the core configuration and handler setup for our AO process.
local process = require "lib.process_lib"
Version = "0.0.1"
_0RBIT = "BaMK1dfayo75s3q1ow6AO64UDpD9SEFbeE8xYrY2fyQ"
_0RBT_POINTS = "BUhZLMwQ6yZHguLtJYA5lLUa9LQzLXMXRfaq9FVcPJc"
FEE_AMOUNT = "1000000000000" -- 1 $0RBT
BASE_URL = "http://www.randomnumberapi.com/api/v1.0/random?min=1&max=6&count=1"
-- Handler to Get Random Number
Handlers.add("GetNumber",
Handlers.utils.hasMatchingTag("Action", "GetNumber"),
process.getNumber
)
-- Handler to Receive Data
Handlers.add(
"ReceiveData",
Handlers.utils.hasMatchingTag("Action", "Receive-Response"),
process.receiveNumber
)
Hereās a quick breakdown:
local process = require "lib.process_lib"
This imports theprocess_lib.lua
module, which contains the functions we previously defined (getNumber
,receiveNumber
, andsendReply
).Defining the Constants
Version
: Sets the version of your process._0RBIT
: 0RBT Points Process Address_0RBT_POINTS
: 0rbit Primary Process AddressFEE_AMOUNT
: Fee to send the GET request (equivalent to 1 $0RBT token).BASE_URL
: Points to an external API that generates a random number between 1 and 6
Handlers.add("GetNumber", ...)
This creates a handler with the "GetNumber" action, which triggers theprocess.getNumber
function:"GetNumber"
: The name of the handler.Handlers.utils.hasMatchingTag("Action", "GetNumber")
: This utility function checks if the incoming request has theAction
tag set to"GetNumber"
, and if so, triggers the function.process.getNumber
: Calls thegetNumber
function fromprocess_lib.lua
to initiate the GET request.
Handlers.add("ReceiveData", ...)
This sets up a handler for receiving data once the GET request is complete:"ReceiveData"
: The name of the handler for handling the received data.Handlers.utils.hasMatchingTag("Action", "Receive-Response")
: This utility function checks if the incoming response has theAction
tag set to"Receive-Response"
, and if so, triggers the handler.process.receiveNumber
: Calls thereceiveNumber
function fromprocess_lib.lua
to process the received random number.
Checkpoint 4:š Build the Process and Deploy It
Before building your Lua process, you must make the build script executable. Run the following command in your root directory:
chmod +x ./ao/random-function/scripts/build.sh
Then, execute the build command:
npm run random-function:build
Similarly, to deploy your Lua process, first make the deploy script executable:
chmod +x ./ao/random-function/scripts/deploy.sh
Finally, deploy the process by running:
npm run random-function:deploy
Checkpoint 5:š² Getting a Random Number
With everything deployed, itās time to fetch a random number. Open a new terminal, access the AO interface using aos
, and send a message to your process:
Send({ Target = "YOUR_PROCESS_ID", Action = "GetNumber" })
Remember to replace "YOUR_PROCESS_ID"
it with your actual process ID. Once sent, youāll receive a random numberāverifiably on-chain! š
Checkout the complete codebase here:
https://github.com/0rbit-co/dice-random-function
Whatās Next?
In the next blog, weāll cover building the frontend for our Dice Game!š
If you have any questions, please DM us on Twitter or join our Discord community to learn more about 0rbitš«.
Keep Building! š§±š«š