# The Need for Reliable Randomness Using a random number in a smart contract is a common requirement. For example, a game may need to generate a random number to determine the winner of a lottery. However, the blockchain is deterministic, and the result of a smart contract is determined by the input. That is to say, a blockchain is a decentralized and trustless environment where the random number generation can easily be manipulated or biased by any party in the network. e.g. if we use block hash, or timestamp, as the source of randomness, the miner can manipulate it to his advantage, by either withholding the block or manipulating the timestamp. The solution is to use an external source of randomness. Randcast is a service that generates random numbers through a decentralized network and provides them to smart contracts. The randomness generation process is transparent and verifiable, provided by a group of nodes based on the threshold BLS signature scheme. Before given to user's smart contract, the randomness is verified on-chain by Adapter, one of the smart contracts of Randcast. # Request and Fulfillment Lifecycle As we invovle nodes in the network to generate randomness, the randomness generation process is divided into two phases: request and fulfillment. The request phase is the process of requesting randomness, and the fulfillment phase is the process of verifying and returning randomness to the user's smart contract. The request phase is initiated by the user, and the fulfillment phase is initiated by the node in the network. The two phases are connected by a callback mechanism. A callback function is priorly defined in the abstract consumer contract, and overridden by the user to implement the logic of using the randomness. The callback function will be called by the Adapter contract in the fulfillment phase. During the request phase, the user signs a transaction invoking the request function in the consumer contract deployed by the user. The consumer contract then call the request function in the Adapter contract, which emits a `RandomnessRequest` event so that the nodes in the network can listen to it. As a unique identifier of the request, a request ID is generated and returned to the user's consumer contract. The user can use the request ID to query the status of the request. After receiving the request, the nodes in the network will generate and aggregate randomness. The randomness is then committed to the Adapter contract by the committer nodes in the group. Here comes the fulfillment phase. The Adapter contract will verify the randomness and try to call the callback function in the user's consumer contract. No matter the callback function is successfully called or not, the Adapter contract will emit a `RandomnessRequestResult` event to notify the user and the nodes in the network. Note the gas cost of the verification and callback function is directly paid by the committer node, then reimbursed by the user. # Key Concepts Node: A node is the basic entity that forms the ARPA Network. It registers, be grouped, signs the BLS signature, and commits the signature to the Adapter contract. Group: A group works to provide randomness generation service DKG Process: BLS Process: # Subscription We provide a subscription mechanism to handle payment for the fulfillment phase and make it manageable. Note: Randcast v0.1.0 is **FREE**. You **ONLY** need the subscription account to pay for the **Gas fee**. Accordingly, the subscription is funded with ETH tokens. - Create a subscription by calling the `createSubscription` function in the Adapter contract. The subscription is identified by a subscription ID, which is returned to the user and emitted in the `SubscriptionCreated` event. The user can use the subscription ID to query the status of the subscription. - Bind the consumer contract to the subscription by calling the `addConsumer` function in the Adapter contract. The user can bind multiple consumer contracts to the same subscription. - Fund the subscription by calling the `fundSubscription` function in the Adapter contract. - Remove the consumer contract from the subscription by calling the `removeConsumer` function in the Adapter contract. - Cancel the subscription as well as claim the remaining balance by calling the `cancelSubscription` function in the Adapter contract. When the consuming contracts request randomness, the Adapter will check the subscription balance based on the inflight requests. If the subscription balance is insufficient, the request will fail. When the randomness is fulfilled, the transaction costs are calculated and the subscription balance will be deducted accordingly. Here Randcast tries to provide a fail-fast mechanism to get the user informed in the request phase, whether the request will be fulfilled successfully or not. # Automatic Gas Estimation Since there are two phases in the randomness generation process, request and fulfillment don't happen in the same transaction. The user needs to tell the Adapter contract how much gas is needed to call the callback function in the user's consumer contract, so that the Adapter contract can estimate the gas cost of the fulfillment phase to enable the fail-fast mechanism. `callbackGasLimit` and `callbackMaxGasPrice` are two parameters used to estimate the gas cost and instruct the behavior of committer nodes. `callbackGasLimit` is the gas limit of the callback function, and `callbackMaxGasPrice` is the maximum gas price that the user is willing to pay for the callback function. If the gas cost of the callback function is greater than `callbackGasLimit`, the invoking of callback function will fail. If current gas price on-chain is greater than `callbackMaxGasPrice`, the committer nodes will not commit the randomness to the Adapter contract. Users need to calculate carefully to set the `callbackGasLimit` and `callbackMaxGasPrice` to make sure the randomness generation process can be completed successfully. To make life easier, we provide an automatic gas estimation mechanism. As long as the consumer contract extends the `GeneralRandcastConsumerBase` contract, these two parameters will be automatically calculated and set in the first request. With our Randcast CLI, the user can even estimate the gas cost before the first request, so that the user can fund the subscription with just enough balance. The only thing the user needs to do is to implement business logic in the callback function. For experienced users, they can also set these two parameters manually by calling the `setCallbackGasConfig` function in the consumer contract. # User Journey Randcast provides several ways to get started. In next sections, we will introduce how to use Randcast in different ways. ## Use Randcast CLI The user can manage the subscription and consumer contracts, read and filter historical requests and fulfillment results, and estimate the gas cost of the callback function, all in one place. Note: Always use `[COMMAND] -h` to get help. - Create a subscription: ```bash ARPA User CLI〉send cs ``` - Get subscriptions: ```bash ARPA User CLI〉randcast mss ``` ```text my subscriptions: [ 1, ] ``` - Write and deploy a consumer contract, here we use `forge` from `Foundry` to do this, note `--rpc-url` is needed except you are using a local chain: ```bash forge create -i user/examples/GetRandomNumberExample.sol:GetRandomNumberExample --constructor-args 0xa513e6e4b8f2a923d98304ec87f64353c4d5c853 ``` ```text [⠢] Compiling... No files changed, compilation skipped Enter private key: Deployer: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 Deployed to: 0x948B3c65b89DF0B4894ABE91E6D02FE579834F8F Transaction hash: 0xbf63e615506fb89fee3fd53a04f09335eafcb826b7283d2997db86eb7fa852c0 ``` - Bind the consumer contract to the subscription: ```bash ARPA User CLI〉send add-consumer 1 0x948B3c65b89DF0B4894ABE91E6D02FE579834F8F ``` - Check a specific subscription, to make sure that consumer contract is added: ```bash ARPA User CLI〉randcast s 1 ``` ```text owner: 0x70997970c51812dc3a010c7d01b50e0d17dc79c8, consumers: [ 0x948b3c65b89df0b4894abe91e6d02fe579834f8f, ], balance: 0, inflight_cost: 0, req_count: 0, free_request_count: 1, referral_sub_id: 0, req_count_in_current_period: 0, last_request_timestamp: 0 ``` - Estimate gas cost for the fulfillment of the first request: ```bash ARPA User CLI〉randcast estimate-callback-gas 0x948b3c65b89df0b4894abe91e6d02fe579834f8f 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 "getRandomNumber()" "" ``` ```text estimate-callback-gas_limit_res: 97191 ``` ```bash ARPA User CLI〉randcast estimate-payment-amount 97191 ``` ```text payment_amount_in_eth_wei: 2883418467348405 in 3 times of current gas price: 3 * 1425618985 ``` - Then fund the subscription with a little more than the estimated payment amount to cover the surge of gas price: ```bash ARPA User CLI〉send fs 1 3000000000000000 Fund subscription successfully, transaction hash: 0x1301eb5d3751aaed2fe224ed96f6fa31a210d55a05e0e759eb34a4cd577398cb ``` - The subscription should be ready to use now, check it again: ```bash ARPA User CLI〉randcast s 1 ``` ```text owner: 0x70997970c51812dc3a010c7d01b50e0d17dc79c8, consumers: [ 0x948b3c65b89df0b4894abe91e6d02fe579834f8f, ], balance: 3000000000000000, inflight_cost: 0, req_count: 0, free_request_count: 1, referral_sub_id: 0, req_count_in_current_period: 0, last_request_timestamp: 0 ``` - Request a randomness in any way you need, here we use `cast` from `Foundry` to do this, note `--rpc-url` is needed except you are using a local chain: ```bash cast send -i 0x948b3c65b89df0b4894abe91e6d02fe579834f8f "getRandomNumber()" ``` - Query historical randomness requests paid by the subscription, and add optional filter by consumer address, or pending/ success/ failed status: ```bash ARPA User CLI〉randcast rs 1 ``` ```text 4 request(s) found! requests: [ RandomnessRequest { request_id: "f824e7e71084837988642d6e80b6cb632e8dc5bced216143970573cf0e471d48", sub_id: 1, group_index: 0, request_type: Randomness, params: Bytes(0x), sender: 0x5ee8ba836860dd0f65f0815353f924562c32321a, seed: 74177094504516131371017173948908728010834945092155580109839890742103808072250, request_confirmations: 6, callback_gas_limit: 97191, callback_max_gas_price: 85572193836, estimated_payment: 57692002934486676, fulfillment_result: Some( RandomnessRequestResult { request_id: "f824e7e71084837988642d6e80b6cb632e8dc5bced216143970573cf0e471d48", group_index: 0, committer: 0x6d49d417c2ab52b941253cb9f018597f98c2e94b, participant_members: [ 0x6d49d417c2ab52b941253cb9f018597f98c2e94b, 0xc8edfe4a67bccc676222f0e65ef4bd2ede513d92, 0xc832555ba1285fcd9ff224a879727fe80c5c2fe6, ], randommness: 72268503294186913141421904411716557309821678372656178088948653025159185717542, payment: 58534118894026752, flat_fee: 0, success: true, }, ), }, RandomnessRequest { request_id: "63057a4c16c3955847235ec0b2a7e804633dea38f1cc4629dff9c34fd15030ed", sub_id: 1, group_index: 0, request_type: Randomness, params: Bytes(0x), sender: 0x5ee8ba836860dd0f65f0815353f924562c32321a, seed: 89821905989940142828936339811540536617118967869705504742968373863645089094375, request_confirmations: 6, callback_gas_limit: 97191, callback_max_gas_price: 53119805535, estimated_payment: 36312894813447185, fulfillment_result: Some( RandomnessRequestResult { request_id: "63057a4c16c3955847235ec0b2a7e804633dea38f1cc4629dff9c34fd15030ed", group_index: 0, committer: 0xc832555ba1285fcd9ff224a879727fe80c5c2fe6, participant_members: [ 0x6d49d417c2ab52b941253cb9f018597f98c2e94b, 0xc8edfe4a67bccc676222f0e65ef4bd2ede513d92, 0xc832555ba1285fcd9ff224a879727fe80c5c2fe6, ], randommness: 27769558289933409672681258774250203594411447646562338907630847289833910951376, payment: 12159332538611792, flat_fee: 500000000000000, success: true, }, ), }, RandomnessRequest { request_id: "bc258e216cf5b8c4549e2667b9f0f2f8a0e5324283a5dca264a639dd3208a948", sub_id: 1, group_index: 1, request_type: Randomness, params: Bytes(0x), sender: 0x5ee8ba836860dd0f65f0815353f924562c32321a, seed: 65689501420482864634999382338188236957641370482439689010961927607951890384023, request_confirmations: 6, callback_gas_limit: 97191, callback_max_gas_price: 38074832601, estimated_payment: 26169709466100791, fulfillment_result: Some( RandomnessRequestResult { request_id: "bc258e216cf5b8c4549e2667b9f0f2f8a0e5324283a5dca264a639dd3208a948", group_index: 1, committer: 0xc07d3983594dc9ad7259e50736651a617df43170, participant_members: [ 0xde59c3f995d43bbb685652d3e7094c845120f98e, 0xbb38b3302fa38acf3a3aeb7fa2b3d5765cd11244, 0xc07d3983594dc9ad7259e50736651a617df43170, ], randommness: 95246188166766731484576812306679763734598701698698797793739803330019880659163, payment: 10059228421561584, flat_fee: 500000000000000, success: true, }, ), }, RandomnessRequest { request_id: "595e03b0f8ed7a0bcf19b71c8cba59a962d36f85f5e96b0099f03f5ceb19b3bb", sub_id: 1, group_index: 2, request_type: Randomness, params: Bytes(0x), sender: 0x5ee8ba836860dd0f65f0815353f924562c32321a, seed: 116151869703078905907283770278682715348011778460587914840440360538538455582, request_confirmations: 6, callback_gas_limit: 97191, callback_max_gas_price: 38073220653, estimated_payment: 26168622705266723, fulfillment_result: Some( RandomnessRequestResult { request_id: "595e03b0f8ed7a0bcf19b71c8cba59a962d36f85f5e96b0099f03f5ceb19b3bb", group_index: 2, committer: 0xeecc599896c326a692ed88e077d83ae70b368060, participant_members: [ 0xd0ff7138f54b84ac8180a1c0777f56f57845a9f2, 0xeecc599896c326a692ed88e077d83ae70b368060, 0x666bcd629be685c8a197541f3e98ab12f1c5b673, ], randommness: 32764317417096660201628975371287017617294336828480713865304328240339078207177, payment: 9812265191830704, flat_fee: 500000000000000, success: true, }, ), }, ] ``` ## Use Subscription Web UI(coming soon) The Subscription Web UI is a web application that allows users to manage subscriptions and consumer contracts in a graphical way. We will release it soon. ## Use Direct Contract Interaction A DApp or Web3 game developer can also interact with the Adapter contract directly. While you can do this in many ways, such as via Remix or Etherscan, or any other programmatic way, we recommend using Cast by Foundry. (Michael Part) # SDK References Randcast SDK is a set of smart contracts and libraries with introductions and examples that can be used to build a DApp or Web3 game that requires randomness. The SDK is designed to be flexible and extensible. The SDK is written in Solidity and can be used in any EVM-compatible blockchain. It aims to help requesting randomness as well as consuming randomness in a secure and easy way. In next sections, we will introduce how to write a consumer contract, especially for implementing the callback functions. # BasicRandcastConsumerBase `BasicRandcastConsumerBase` provides: - A set of virtual callback functions that can be overridden by the consumer contract. These functions are called by the Adapter contract when the randomness request is fulfilled. The consumer contract can implement the business logic of the DApp or Web3 game by their needs. For example, the consumer contract can override the `_fulfillRandomness` function to process the randomness result after requesting of type `Randomness`, and/or override the `_fulfillRandomWords` function to process the randomness result after requesting of type `RandomWords`, and/or override the `_fulfillShuffledArray` function to process the randomness result after requesting of type `Shuffling`. - A nonce recorder to tell the user how many times the consumer contract has requested randomness. This can be useful for debugging in perspective of the user. Please don't try to control the nonce as it will not affect the behavior of Adapter, i.e. randomness result. - `_rawRequestRandomness` function to call the Adapter contract to request randomness. The consumer contract can define its own interface to request randomness, where this function should be called. The user can set: - `subId` to specify which subscription to use. - `seed` to specify the seed of the randomness(this won't decide the randomness result, but will do help to prevent other parties from violently predicting it). - `requestConfirmations` to specify the number of confirmations required for the randomness request. - `callbackGasLimit` to specify the gas limit of the callback function. - `callbackMaxGasPrice` to specify the maximum gas price of the callback function. # Consumer Contract Examples Users can focus on the business logic of their DApp or Web3 game by using the `GeneralRandcastConsumerBase`. This base contract provides a simple interface for requesting randomness and receiving the result. When requesting randomness, we provide three types of randomness: `Randomness`, `RandomWords`, and `Shuffling`. The following is an example of each type of randomness. For experienced developers, you can directly inherits `BasicRandcastConsumerBase` contract to leverage a more flexible interface for requesting randomness. You can use this contract to request randomness in any way you want. See `AdvancedGetShuffledArrayExample` for an example. Note you need to restrict the access to the function that requests randomness in your consumer contract. Otherwise, anyone can call this function and let your subscription pay for the randomness request. ## AdvancedGetShuffledArrayExample As entrypoint of requesting randomness, you can have more flexibility in `getRandomNumberThenGenerateShuffledArray` function. For example, you can pass user seed as input parameter, and calculate the raw seed and request id of the next randomness request before actually calling `_rawRequestRandomness` function. Also, you can set `subId`, `requestConfirmations`, `callbackGasLimit`, and `callbackMaxGasPrice` specifically for each randomness request. # Randcast Utilities On receiving the randomness result, the consumer contract can use the `RandcastSDK` library to further process the randomness result. The library provides functions to convert the randomness result to different types of data that can be used in the DApp or Web3 game. The following is an example of using the library.