# ARPA Network

## Introduction

The ARPA BLS Threshold Signature Scheme (BLS-TSS) Network is a cutting-edge decentralized cryptographic system designed to perform BLS threshold signature tasks. Randcast, our first product leveraging the ARPA Network, provides secure and reliable random number generation on multiple prevailing blockchains. Random number generation is a fundamental cryptographic primitive that plays a crucial role in various applications, including secure communications, encryption, gaming, and lotteries. By employing the principles of threshold cryptography, specifically using the Boneh–Lynn–Shacham (BLS) threshold signature scheme, the ARPA BLS-TSS Network pioneers a novel approach to produce random numbers in a distributed and tamper-resistant manner, thereby enhancing security and trust.

### Core Architecture and Standards

<figure><img src="/files/wqpODjtW66nr6v8N7mbV" alt=""><figcaption><p>ARPA Network Architecture</p></figcaption></figure>

The ARPA BLS-TSS Network follows a set of [core standards](https://github.com/ARPA-Network/BLS-TSS-Network-Standards/blob/main/standards.md) that define its architecture and interactions, providing a solid foundation for its operation. Within the network, various nodes participate in the Distributed Key Generation (DKG) process — a critical step that lays the groundwork for generating threshold signatures. The DKG process results in the creation of a group public key and individual partial keys for each member in group. These cryptographic components are then used to produce random numbers in a distributed manner, ensuring that no single entity can control or manipulate the outcome.

The network is facilitated by a set of sophisticated smart contracts, with each one serving a specific role while working together harmoniously:

* The `NodeRegistry` contract is the registration center of nodes, responsible for managing different node access methods, providing operations such as registering, activating, exiting, and slashing.
* The `Controller` contract serves as the central authority for coordinating and managing the DKG process. It is responsible for node grouping strategy, and handles the execution of DKG-related operations, and the management of group-related information.
* The `Coordinator` contract is the gateway that facilitates the interaction between the system's participants and the network during the DKG process. It manages phases in each particular DKG process.
* The `Adapter` contract is the linchpin that manages the interaction between the Randcast protocol and the threshold signature-based random number generation mechanism. It is responsible for subscription management, task assignment, payment calculation and callback calling. It ensures that the randomness results are provided securely and reliably.

In addition, we provide utilities for interacting with the ARPA network, such as the [`RandcastSDK`](/randcast/using-the-sdk/randcast-utilities) library, which provides essential tools for processing random numbers and performing operations that require randomness.&#x20;

### Key Concepts

* Node: A node is the basic entity that forms the ARPA Network. It registers, be grouped, signs and aggregates the BLS signature, and commits the signature to the `Adapter` contract.
* Group: A group is a set of nodes that are grouped together to listen to the signing request and generate BLS signatures. When a member joins or leaves, the group will be reconfigured.
* DKG Process: A DKG process is the process of forming a group. It is initiated by the grouping strategy of the `Controller` contract and coordinated by the `Coordinator` contract. Nodes listen to the `Controller` contract and participate in the DKG protocol. After up to 4 phases, the participants will commit the result to the Controller contract.
* BLS Process: A BLS process is the process of generating a BLS signature. It is initiated by the task assignment strategy of the `Adapter` contract. Nodes listen to the `Adapter` contract, then sign and send the partial signature to the committer nodes in the group. Upon receiving enough partial signatures, the committer nodes will aggregate and commit the aggregated BLS signature to the Adapter contract.

### Summary

Overall, the ARPA BLS-TSS Network is a robust and innovative system that leverages the power of threshold cryptography and blockchain technology to provide superior decentralized random number generation, setting a new standard in the field.&#x20;

By adhering to well-defined standards and utilizing a set of carefully designed smart contracts and libraries, the ARPA BLS-TSS Network ensures the integrity, security, and reliability of random number generation in a distributed environment.&#x20;

Whether it's securing cryptographic protocols, enhancing online gaming experiences, or fortifying cryptographic key generation, the ARPA BLS-TSS Network brings a new level of trust and assurance to the world of randomness.


# Becoming a node & get rewarded

### Node

The ARPA BLS-TSS Network consists of multiple groups of nodes. Within a group, each node is responsible for completing a BLS task (generating a BLS-TSS signature jointly with the other nodes of the group).&#x20;

In order to ensure timely and accurate responses from nodes, each node in the network needs to follow the mechanism of staking and slashing. Currently we are integrating Eigenlayer to leverage ETH restaking to further secure network security. Therefore there are two paths to enter the network, by participating native staking or becoming a whitelisted Eigenlayer operator.

### Reward

There are up to four types of rewards a node can earn if acted responsibly in a timely manner:

* **Randomness submission reward**
  * **1 ARPA** each time a node successfully submits a randomness result.
* **BLS-TSS task reward**
  * **1 ARPA** each time a node completes a BLS-TSS task.
* **DKG post-process reward**
  * **60 ARPA** each time a node completes the DKG post-proccess task during node grouping.
* **Community staking delegation reward (by native staking only)**
  * **5% (up to 75,000 ARPA monthly)** of the total community staking reward will be evenly distributed among all the delegated nodes. (Note: this reward **can only** be claimed when the node exits the network)


# Native Staking

### Staking

A minimum of **500,000 ARPA** is **required** to become a node. After staking the minimum amount, a node can join the ARPA Network and earn rewards by correctly performing the BLS tasks.

Note: This is similar to the ETH PoS model where 32 ether must be staked to run a validator.

### Hardware Requirements&#x20;

Using AWS EC2 as an example, to ensure a steady performance, each node should be hosted on a virtual instance that meets the following specs:

#### t2.small (\~$23/month)

* **1** vCPU
* **2G** Memory
* **30G** Storage

**IMPORTANT:** During the initial months following the release of ARPA Network on Ethereum mainnet, nodes are [invite-only](https://docs.google.com/forms/d/e/1FAIpQLSeuldK0Tz1L3t1fhgQ-r0V-SkD-E25vo1YnaX3rTGrkEIPQnw/viewform). This early-access period is designed to facilitate the identification of potential problems and expedite the implementation of any fixes. In this way, we can enhance the security, stability, and performance of the network before transitioning to a fully permissionless system.

After the initial period concludes, anyone with a computer that meets the above hardware requirements can host their own node permissionlessly.&#x20;

A [preview of the detailed instruction](https://github.com/ARPA-Network/BLS-TSS-Network/blob/main/docker/mainnet/README.md) for running a node.

You will need the following to get a node running if you do not wish to use our deployment scripts (AWS CDK):

* [Rust](https://www.rust-lang.org/learn/get-started)
* [Docker](https://www.docker.com/get-started/)
* [Foundry](https://getfoundry.sh/)
* Externally accessible IP and ports
* Stable internet connection

### Early Access

In the meantime, if you are interested in becoming a node operator during the initial release and providing valuable feedback to help us improve, please [join our waitlist](https://docs.google.com/forms/d/e/1FAIpQLSeuldK0Tz1L3t1fhgQ-r0V-SkD-E25vo1YnaX3rTGrkEIPQnw/viewform), and our team will reach out to you.


# Introduction to Staking v0.1

## Overview

Welcome to ARPA Staking  - a gateway to bolster the robustness and decentralization of the ARPA network, while also rewarding our active participants.

Staking your ARPA tokens is more than just a passive investment. It is an active contribution to the network's fortification against potential threats, promoting a resilient and distributed ecosystem. Moreover, it encourages wider participation, preventing any single entity from gaining excessive control over the network, and ensuring our commitment to a decentralized and reliable system.

Participating in staking also positions you at the heart of the ARPA community. As an early adopter, you become an integral part of our network's growth, wielding the power to shape its future.&#x20;

## Why Staking Matters

Staking is a critical cog in the machine that keeps the ARPA network humming smoothly. It offers incentives to token holders for their contribution to maintaining the network's operational excellence. Stakers, while earning rewards, simultaneously ensure network security, foster decentralization, and assist in keeping the network reliable.&#x20;

* Driving Node Operation: Operating a node is akin to being at the network's front lines, actively shaping its well-being. Node operators participate in the signature-producing process, a key element that upholds the network's security.&#x20;
* Fortifying Security and Robustness: The act of staking is an active commitment to network safety. Staked ARPA tokens serve as a guarantee for good behavior, as malicious activities will result in slashing for node operators and damage community members’ rewards as well. More staked tokens and more nodes in operation mean a more secure, reliable, and resilient ARPA network.
* Advancing Decentralization: One of ARPA Network's fundamental principles is decentralization. When ARPA token holders stake and run nodes, the network spreads its control, preventing an unhealthy concentration of power. This robust decentralization ensures resilience against attacks.
* Fueling Rewards: Staking ARPA tokens isn't a one-way street. It's a mutual relationship where token holders support the network and, in turn, get rewarded for their service. The staking rewards serve as a gratifying way to appreciate and incentivize network participation.
* Catalyzing Community Engagement: Staking paves the way for active community participation. As an early staker, you get to be part of the ARPA network's growth. You're not just an observer, but a crucial player influencing the network's future trajectory.

## Community Pool

The community pool is a major component of the ARPA Staking Program. Community staking offers an easy pathway for those who prefer not to run a node. With the community pool, staking becomes more accessible, and there's no need for hardware investment. Whether you're a novice or a pro, you can contribute and benefit from the growing ARPA network.

To make things smoother, we've introduced auto-delegation for the community pool in the v0.1 phase. As we continue to grow and refine our staking program, the goal is to make it increasingly decentralized, allowing participants to manually select the nodes they want to delegate to in future versions. This will provide additional flexibility and autonomy to our community while maintaining the simplicity and fairness offered by auto-delegation.

* Hassle-free Staking: For those who are more comfortable avoiding the intricacies of node operation, Community Pool Staking offers a simpler alternative. We've designed it to be intuitive, easy to use, and, most importantly, convenient.
* Reward Distribution: A monthly allocation of 1.5 million ARPA tokens is reserved for the community pool. These tokens are divided among stakers based on their respective contributions relative to the pool's total.
* Growing Opportunities: The pool's total cap will not remain static. In the first month, it starts with 50 million tokens, which increases by an additional 10 million tokens every subsequent month.&#x20;
* Cap per Address: We're focused on ensuring equitable opportunities, and to that end, we've limited the staking amount to 2.5 million ARPA per address. This cap makes room for more participants and prevents an undue concentration of rewards.
* Flexibility: You have the power to stake or claim rewards at your convenience, but please be mindful, however, that unstaking invokes a 14-day unlock period before you can claim your tokens.

## Running a Node

\
Operating a node is a more engaged form of participation in the ARPA network. As a node operator, you contribute significantly to the network's security and reliability. A minimum staking requirement of 500,000 ARPA applies to run a node. Node operators get to receive 5% of the community’s total staking reward due to the auto-delegation mechanism in staking v0.1, claimable upon exiting the network.

#### Slashing and Chilling in Staking v0.1

In order to maintain the network's integrity and robustness, certain measures are put in place. Slashing and chilling are two such mechanisms that regulate the behavior of node operators.

#### Slashing

Slashing is a penalty mechanism wherein a certain amount of staked ARPA tokens or received rewards are deducted from the offending node operator's address in case of malicious behavior or neglect of duties. In staking v0.1, If a node behaves maliciously during the DKG process, it will be penalized by a deduction of 50% of the auto-delegation reward it would have received for that month.

#### Chilling

Chilling refers to the cool-off period a node has to undergo once it leaves the network. In specific, if a node operator chooses to exit the network, they will have to wait for a chilling period of 7200 blocks before they can rejoin. This ensures stability in network operations and discourages erratic node behavior.

#### Staking Contract Addresses&#x20;

<table data-header-hidden><thead><tr><th width="241" align="right"></th><th></th></tr></thead><tbody><tr><td align="right">ARPA Token Address</td><td><a href="https://etherscan.io/address/0xBA50933C268F567BDC86E1aC131BE072C6B0b71a">0xBA50933C268F567BDC86E1aC131BE072C6B0b71a</a> </td></tr><tr><td align="right">Staking Address</td><td><a href="https://etherscan.io/address/0xee710f79aa85099e200be4d40cdf1bfb2b467a01">0xee710f79aa85099e200be4d40cdf1bfb2b467a01 </a></td></tr></tbody></table>

<br>


# Eigenlayer Integration

Please refer to our notion page [Node Setup Instruction for Eigenlayer | Notion](https://arpanetwork.notion.site/Node-Setup-Instruction-for-Eigenlayer-a019b489572848e89e8cb30e0d89fb7e).&#x20;


# Randcast

An Introduction

## ARPA Network vs Randcast

* **Randcast** is an on-chain verifiable random number generation **service** with an easy-to-use **Smart Contract SDK** that can be directly integrated into DApps to provide out-of-the-box functionalities like rolling dice, shuffling an array, generating in-game item attributes based on probability, generating random in-game maps or dungeons, and deciding the probability outcomes of a lottery.
* **Randcast** responds to these randomness requests by leveraging the **ARPA Network,** which consists of many nodes that **perform BLS threshold signature tasks**. A signature generated by this task can be used as the initial entropy to create a random number, as BLS threshold signatures are verifiably random.&#x20;
* The full flow that occurs when a DApp requests randomness is as follows:
  * The **Randcast Adapter Contract** receives a randomness request from a DApp or Web3 game, initiates a BLS-TSS task, and sends an on-chain event.&#x20;
  * The **ARPA Network** acknowledges this event and completes the task, then sends back the signature as the random seed.&#x20;
  * The **Randcast Adapter Contract** takes the random seed, converts it to the desired randomness type (dice roll, shuffled array, etc..), then calls back the subsequent DApp or Web3 game logic that will utilize the randomness result.

## When to use Randcast?

You can directly integrate our out-of-the-box functionality suite when building a DApp or Web3 game that requires safe, untampered, unbiased, verifiable randomness. You can also customize this functionality by extending our [SDK](/randcast/using-the-sdk).

## 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 lottery winner. 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 be easily manipulated by any party in the network. e.g., if we use a block hash or timestamp as the source of randomness, the miners can manipulate it to their 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 process of generating randomness is both transparent and verifiable. It is facilitated by a group of nodes that utilize the threshold BLS signature scheme. Before returning the requested random number to the user's smart contract, the randomness is verified on-chain by the Randcast Adapter smart contract.&#x20;

Randcast and the ARPA Network solve another major issue with random number generation. Randomness generated by a single off-chain entity could potentially be tampered with or manipulated by that entity. Randcast solves this issue by using multiple nodes in the ARPA Network to generate randomness through BLS Threshold signature tasks, which means that no single node has the ability to manipulate the final randomness result.&#x20;

## Request and Fulfillment Lifecycle

<figure><img src="/files/30Xzjv8KWwk30ZA4vw44" alt=""><figcaption><p>Randcast Lifecycle</p></figcaption></figure>

Within the ARPA Network, the randomness generation process has two phases: request and fulfillment. The request phase (initiated by the user) is to request randomness, and the fulfillment phase (initiated by the nodes) is to verify and return the randomness to the user's smart contract. The two phases are streamlined by a callback mechanism. The abstract callback function defined in the consumer contract can be overridden by the user to implement the logic beyond receiving the randomness. The callback function is 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 calls the request function in the Adapter contract, which emits a `RandomnessRequest` event for the nodes in the network to listen to. A unique request ID is then generated and returned to the user's consumer contract. The user can use this request ID to query the request status.

Upon receiving the request, the nodes generate and aggregate the 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 verifies the randomness and calls the callback function in the user's consumer contract. Whether 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 that the gas cost of the verification and callback function is directly paid by the committer node and then reimbursed by the user.

## Subscription

We provide an intuitive subscription mechanism to handle payments for the fulfillment phase.

**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**.

<figure><img src="/files/p2H4FHw1RWPMTtfLBlNZ" alt=""><figcaption><p>Randcast Fund Flow</p></figcaption></figure>

Useres can interact with subscriptions in the following ways:&#x20;

* 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 subscription status.)
* 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. The consumer contract is part of your DApp or Web3 game implementation that handles the subsequent logic after receiving the randomness result from Randcast.)
* 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 consumer 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 inform the user 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 do not 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 invocation of the callback function will fail. If the current gas price on-chain is greater than `callbackMaxGasPrice`, the committer nodes will not commit the randomness to the Adapter contract.

Users need to correctly calculate the `callbackGasLimit` and `callbackMaxGasPrice` to ensure that 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 tool, the user can estimate the gas cost before the first request so that the user can fund the subscription with just enough funds to complete the request. The only thing the user needs to do is to implement business logic in the callback function. Experienced users can also manually set these two parameters by calling the `setCallbackGasConfig` function in the consumer contract.


# Getting Started

{% embed url="<https://drive.google.com/file/d/1DbyRUUe3O6U-W03PYl5uNrf8Tw82wQ2_/view?usp=sharing>" %}
Step-by-step Randcast Tutorial
{% endembed %}

## To integrate Randcast into your DApp:

1. Create a Subscription
2. Fund the Subscription
3. Build and Deploy the Consumer Contract
4. Add Consumer to your Subscription
5. Request Randomness from the Consumer
6. Process the Randomness Result in your Consumer Contract

***Note**:*&#x20;

* *Randcast requests are paid via Subscription; you should fund your Subscription in advance to avoid service interruptions.*
* *A Consumer Contract is part of your DApp and is used to request and "consume" the randomness from Randcast; any subsequent logic on how to utilize the randomness result can be defined in the callback function within your Consumer Contract.*
* [*Randcast SDK*](/randcast/using-the-sdk) *provides* [*a base implementation of the Consumer Contract*](/randcast/using-the-sdk/generalrandcastconsumerbase)*, which you can extend to implement your custom logic, like in* [*this simple example*](/randcast/using-the-sdk/consumer-contract-examples/example-get-random-number)*.*

***

In this tutorial, we will demonstrate three ways to integrate Randcast into your DApp.

### 1. [Use Web GUI ](/randcast/getting-started/use-web-gui)

Pros:&#x20;

* Zero local development environment setup
* Minimum command-line experience required
* Intuitive to less experienced developers

Cons:

* Less flexible
* Hard to debug for complex DApps
* Less verbose output

***

### 2. [Use Randcast CLI](/randcast/getting-started/use-randcast-cli)

Pros:

* More verbose output
* More flexible
* Easy to debug

Cons:

* Requires local development environment setup using Docker
* Requires command-line experience
* Requires basic Solidity development experience

***

### 3. [Use Solidity Development Tools](/randcast/getting-started/use-solidity-development-tools) (e.g. [Foundry](https://book.getfoundry.sh/))

Pros:

* Most verbose output
* Most flexible
* Full programmatic access, easy to automate

Cons:

* Requires local development environment setup
* Requires advanced Solidity development experience
* Requires experience in [Foundry](https://book.getfoundry.sh/)


# Use Web GUI

For those who want to quickly test out the functionalities of Randcast without having to set up a local Solidity development environment, we recommend the following steps.

Use our [Subscription Management Web Portal](https://randcast.arpanetwork.io) to:

* Create, fund, and cancel subscriptions.
* Add and delete consumer contracts for an existing subscription.
* View and analyze historical and pending requests and events.

***Note**:* Due to connection issues with RPC endpoint, Redstone Holesky Testnet and Taiko Katla Testnet are not supported yet.

Use [Remix](https://remix.ethereum.org/) to:

* Code, build, debug, and deploy your consumer contract.
* Request randomness via your consumer contract.

***Note**: A browser extension wallet is required. (We use Metamask in our examples.)*

***

In [the next section](/randcast/getting-started/use-web-gui/subscription-management-portal), we will navigate you through the [Subscription Management Web Portal](https://randcast.arpanetwork.io) functionalities.

You can [jump to the Remix section](/randcast/getting-started/use-web-gui/remix) for a step-by-step guide to customizing and deploying your consumer contract.


# Subscription Management Portal

The [Randcast Subscription Management Portal](https://randcast.arpanetwork.io/) is an intuitive GUI to check important info, manage subscriptions and consumer contracts, and view historical randomness requests and events.&#x20;

***

### Randcast General Info

<figure><img src="/files/5hO1gSaFimrEHxMsQEJH" alt=""><figcaption><p>Adapter Contract Information</p></figcaption></figure>

The top of the page displays the following information:

* **Adapter address:** Your Consumer contract relies on the Adapter contract to request randomness from the ARPA BLS-TSS network. The address of the Adapter contract is needed during the deployment of the Consumer contract.
* **Minimal request confirmations:** The minimum number of blocks to fulfill a request.
* **Max gas limit:** The maximum gas limit for the callback of randomness requests.

***

### My Subscriptions

<figure><img src="/files/I7C9m2ItkIbu6IdaMCaX" alt=""><figcaption></figcaption></figure>

Each subscription displays the following details:

* **ID**: The unique subscription identifier.
* **Admin address**: The admin address of the subscription.
* **Fulfillments**: The number of successfully fulfilled randomness requests(including the execution of the callback function).
* **Consumers**: The number of Consumer contracts under this subscription. Approved Consumer contracts can spend funds from this subscription to pay for randomness requests.
* **Balance**: The ETH balance of the subscription.
* **Created/Canceled:** The time of the creation/cancelation of this subscription.

***

### Actions

<figure><img src="/files/M40FnjR9xQkmkOPNtiyk" alt=""><figcaption></figcaption></figure>

The **Actions** drop-down contains the actions you can take for subscriptions you own:

* **Fund subscription**: Deposit funds into your subscription from your connected wallet.
* **Cancel subscription**: You must provide the account address upon cancelation to receive the remaining balance.

***Note**: You can fund subscriptions that you do not own. In that case, the Actions menu displays only the **Fund subscription** option. Make sure you are funding the intended subscription.*

***

### Consumers

<figure><img src="/files/T4G04oaAlExnEhFR4bvW" alt=""><figcaption></figcaption></figure>

A list of **Consumer** contracts bound to a specific subscription.

* **Address**: The address of the Consumer contract.
* **Added**: The time when the Consumer contract was added to the subscription.
* **Last fulfillment**: The last time a Randcast request was fulfilled for the Consumer.
* **Total spend**: The total amount of ETH the Consumer contract has spent.

***Note**: Consumers can be added to or deleted from the existing subscription.*

***

### Pending

<figure><img src="/files/VR3p8A21zlou3l4hOM6S" alt=""><figcaption></figcaption></figure>

A list of **Pending** requests that are currently being processed.

* **Time**: The time when the pending request was made.
* **Consumer**: The address of the Consumer contract.
* **Request ID:** The unique request identifier.
* **Transaction hash (Request)**: The transaction hash of the pending request.
* **Status**: How long the request has been pending. *Pending requests fail after 24 hours.*
* **Callback gas limit:** The maximum amount of gas you are willing to spend to execute the callback. *(This is automatically calculated by Randcast before the first request if not manually specified.)*
* **Callback max gas price:** The maximum gas price you are willing to accept to execute the callback. *(This is automatically calculated by Randcast before the first request if not manually specified.)*
* **Estimated max payment:** This is an approximate amount of ETH kept aside from the subscription, provided there is sufficient balance. This attempts to effectively complete the callback using the best effort, although success is not always guaranteed.

***Note**: When the provided gas limit is less than the actual gas needed for the callback, the ARPA network can still successfully fulfill the randomness request, but the callback will fail(revert).*

If a pending request fails, it will show up under "Failed Requests":

<figure><img src="/files/Tf26dWMajScRYcuhGoGO" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/wCd02LUdWFy2c0q6Qy2p" alt=""><figcaption></figcaption></figure>

There are some additional field in this view (Scroll right to see additional columns)

**Transaction Hash (fulfillment)**: The transaction hash of the randomness fulfillment. *This will only show when the request fails with the error:* `Callback gas limit set too low/Callback reverted`

**Status**: The reason why the request failed. Requests fail for one of the following reasons:

* Pending for over 24 hours
* Callback gas limit set too low/Callback reverted

*If the request fails because it has been pending for over 24 hours, you can manually cancel it here. Usually, this is because the **callback max gas price** for your request is set too low or there is a gas price surge on-chain.*

***

### History

#### Recent fulfillments

<figure><img src="/files/1i7xaFj4uZ8cL7McVry2" alt=""><figcaption></figcaption></figure>

The **Recent fulfillments** tab shows the details of successful Randcast fulfillments.

* **Request Time:** The time when the request was made.
* **Fulfillment** **Time**: The time when the request was fulfilled.
* **Consumer**: The address of the Consumer contract that made the request.
* **Request ID:** The unique request identifier.
* **Transaction Hash (request)**: The transaction hash of the randomness request.
* **Transaction Hash (fulfillment)**: The transaction hash of the randomness fulfillment.
* **Payment:** The total amount of ETH spent to fulfill the request.
* **Flat fee:** Randcast service fee.
* **Balance**: The ETH balance of the subscription after the request was fulfilled.

#### Events

<figure><img src="/files/VPrcMgtmo81dFZyMHMHK" alt=""><figcaption></figcaption></figure>

The **Events** tab displays events of the subscription. There are five types of events:

1. Subscription created
2. Subscription funded
3. Consumer added
4. Consumer removed
5. Subscription canceled

Details of **Events** section:

* **Time**: The time when the event happened.
* **Event**: The type of the event.
* **Transaction Hash**: The hash for the transaction emitting the event.
* **Consumer**: The address of the Consumer contract. *Only Consumer added and Consumer canceled events are shown.*
* **Amount**:
  * For *Subscription funded* events, this shows the ETH balance of the subscription.
  * For *Subscription canceled* events, this shows the withdrawn ETH amount of the subscription.
* **Balance**: The ETH balance of the subscription after the event.

#### Failed requests

<figure><img src="/files/ADQdpxaOJroM2C6wRe1p" alt=""><figcaption></figcaption></figure>


# Remix

### Deploy Consumer Contract Example

1. Open [`Remix`](https://remix.ethereum.org/) and clone the following repo: <https://github.com/ARPA-Network/Randcast-User-Contract>

<img src="/files/k9rpcj0HAe320esPg6zb" alt="" data-size="original">

2. Open one of the examples (We will use the roll dice example here):  `contracts/user/examples/RollDiceExample.sol`&#x20;
3. Click `Compile`.
4. Click `Deploy & run transactions` on the left sidebar, connect wallet and select `Injected Provider` as environment. You'll see the connected account if it's all set up.

![](/files/9hDWHHjBctwe8zq83N6Y)

5. Pass in the [`Adapter` address](/randcast/supported-networks-and-parameters), click `Deploy`, and approve the transaction in your wallet (Metamask). Note: Next time you open [`Remix`](https://remix.ethereum.org/), you can enable the deployed contract by compiling, passing the contract address then clicking `At Address`.
6. After the contract deployment has completed,  you will see the `ROLLDICEEXAMPLE` in the below `Deployed Contracts` section.&#x20;
7. Copy the address of deployed consumer contract and bind it to your subscription via the subscription portal.

<figure><img src="/files/PcPUhvy9DTbkhHZW3rza" alt=""><figcaption></figcaption></figure>

### Request Randomness to Roll Dice

1. Make sure you have created an active subscription with enough funds and the consumer contract has been added to the subscription. (see [Subscription Management Portal - Consumers](/randcast/getting-started/use-web-gui/subscription-management-portal#consumers))
2. Click `ROLLDICEEXAMPLE` to expand the available functions.

![](/files/4O94v315a3AmoIkxRsEv)

3. Specify "bunch" (the number of dice you want to roll) and then click `rollDice.`

***Note**: A `Gas estimation failed` notification will pop up when the ETH balance in your wallet is too low to send the transaction, or if the consumer contract is not bound to any subscription.*

![](/files/57pR4mTImqbSe6XOdmTz)

4. Call `lengthOfDiceResults()` to see whether the output is the same as the `bunch` value you passed. Call `diceResults` with the index up to the `bunch` to check the output of each dice cast.

Congratulations on successfully requesting randomness from Randcast! Randcast provides a set of tools to help you write your custom consumer contract and manage your subscription. Please continue reading to learn more.


# Use Randcast CLI

Randcast users can leverage our [Randcast CLI](https://github.com/ARPA-Network/BLS-TSS-Network/tree/main/crates/user-cli) tool to manage their 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**: This is our recommended method to interact with Randcast, but we also offer additional methods, such as direct contract interaction, in the* [*following section*](/randcast/getting-started/use-solidity-development-tools)*.*&#x20;

### Prerequisites&#x20;

* Smart Contract development experience in Solidity
* Experience with [Foundry](https://github.com/foundry-rs/foundry) Solidity development toolkit

### Installation

* (**Recommended**) Install from Docker image
  * `docker pull arpachainio/user-shell:latest`
* (Optional) Build from the [codebase](https://github.com/ARPA-Network/BLS-TSS-Network/tree/main/crates/user-cli)

### Usage&#x20;

```bash
docker run -it -v <WORK_DIR>:/data ghcr.io/arpa-network/user-shell:latest "user-shell -c /data/user_config.yml -H /data/user-shell.history"
```

* create a \<WORK\_DIR> as the directory for Randcast CLI on your host, e.g. /home/ubuntu/randcast-cli
* prepare a `user_config.yml` under \<WORK\_DIR> of your host. Please see the [example](https://github.com/ARPA-Network/BLS-TSS-Network/blob/main/crates/user-cli/conf/user_config.yml.example) for reference.&#x20;

***Note**: we recommend directly setting account identity in `user_config.yml` rather than passing an env variable to docker.*

* use `-H` to set a file to save history commands.
* `--network=host`(optional config for local chains) \
  If you want the Randcast CLI container to access a local chain on the host (ex: Anvil), you can use the above flag. This will allow the container to access ports on the localhost such as  "[http://127.0.0.1:8545](http://127.0.0.1:8545/)".&#x20;

### Steps

***Note**: Always use `[COMMAND] -h` to get help.*

#### Create a subscription:

```bash
ARPA User CLI〉send cs <CHAIN_ID>
```

***

#### Get subscriptions(\<SUB\_ID>):

```bash
ARPA User CLI〉randcast mss <CHAIN_ID>
```

```
my subscriptions: [
  1,
]
```

***

#### Write and deploy a consumer contract. Here we use `forge` from `Foundry` to complete this step.&#x20;

Note that `--rpc-url` is needed unless you are performing tests using a local chain:

*(Reference:* [*forge create*](https://book.getfoundry.sh/forge/deploying)*)*

<pre class="language-bash"><code class="lang-bash"><strong>git clone https://github.com/ARPA-Network/Randcast-User-Contract
</strong><strong>cd Randcast-User-Contract
</strong><strong>forge create contracts/user/examples/GetRandomNumberExample.sol:GetRandomNumberExample --rpc-url &#x3C;RPC_URL> --private-key &#x3C;USER_PRIVATE_KEY> --constructor-args &#x3C;ADAPTER_CONTRACT_ADDRESS>
</strong></code></pre>

<pre><code>[⠢] Compiling...
No files changed, compilation skipped
Enter private key:
Deployer: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
<a data-footnote-ref href="#user-content-fn-1">Deployed to: 0x712516e61C8B383dF4A63CFe83d7701Bce54B03e</a>
Transaction hash: 0xbf63e615506fb89fee3fd53a04f09335eafcb826b7283d2997db86eb7fa852c0
</code></pre>

***

#### Bind the consumer contract to the subscription:

```bash
ARPA User CLI〉send add-consumer <CHAIN_ID> <SUB_ID> <CONSUMER_CONTRACT_ADDRESS>
```

***

#### Check a specific subscription to make sure that a consumer contract has been added:

```bash
ARPA User CLI〉randcast s <CHAIN_ID> <SUB_ID>
```

```
owner: 0x70997970c51812dc3a010c7d01b50e0d17dc79c8, consumers: [
  0x712516e61C8B383dF4A63CFe83d7701Bce54B03e,
], 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 <CHAIN_ID> <CONSUMER_CONTRACT_ADDRESS> <USER_ADDRESS> "getRandomNumber()" ""
```

```
estimate_callback_gas_limit_res: 97191
```

```bash
ARPA User CLI〉randcast estimate-payment-amount <CHAIN_ID> 97191
```

```
payment_amount_in_eth_wei: 2883418467348405 in 3 times of current gas price: 3 * 1425618985
```

***

#### Fund the subscription with a little more than the estimated payment amount to cover the surge in gas price:

```bash
ARPA User CLI〉send fs <CHAIN_ID> <SUB_ID> 3000000000000000
Fund subscription successfully, transaction hash: 0x1301eb5d3751aaed2fe224ed96f6fa31a210d55a05e0e759eb34a4cd577398cb
```

***

#### The subscription should be ready to use now, double-check it:

```bash
ARPA User CLI〉randcast s <CHAIN_ID> <SUB_ID>
```

```
owner: 0x70997970c51812dc3a010c7d01b50e0d17dc79c8, consumers: [
  0x712516e61C8B383dF4A63CFe83d7701Bce54B03e,
], 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 randomness in any way that you need. Here we use `cast` from `Foundry` (note that `--rpc-url` is needed unless you are using a local chain):

*(Reference:* [*cast send*](https://book.getfoundry.sh/reference/cast/cast-send)*)*

```bash
cast send <CONSUMER_CONTRACT_ADDRESS> "getRandomNumber()" --rpc-url <RPC_URL> --private-key <USER_PRIVATE_KEY>
```

***

#### Query historical randomness requests paid by the subscription

Note: it is possible to add an optional filter to filter by consumer address or by status (pending / success / failed). You can view these options by running `randcast rs --help`.

<pre class="language-bash"><code class="lang-bash"><strong>ARPA User CLI〉randcast rs &#x3C;CHAIN_ID> &#x3C;SUB_ID>
</strong></code></pre>

```
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,
          },
      ),
  },
]
```

[^1]: ConsumerContractAddress

    &#x20;


# Use Solidity Development Tools

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](https://remix.ethereum.org/), [Etherscan](https://etherscan.io/address/0xbd57b868bb3374faa88722d2ee7ba3023c744e05), or programmatically, we recommend using [Cast ](https://book.getfoundry.sh/cast/)from the [Foundry](https://github.com/foundry-rs/foundry) framework.

### Prerequisites&#x20;

* Smart Contract development experience in Solidity&#x20;
* Experience with [Foundry](https://github.com/foundry-rs/foundry) Solidity development toolkit

### Installation

* Install [Foundry](https://book.getfoundry.sh/getting-started/installation)

### Steps

The instructions below outline the steps a user needs to take in order to request randomness from ARPA Randcast.

***

#### Export your Environment Variables

You can export several environment variables to streamline the execution of the subsequent commands.&#x20;

Note: the adapter contract address for the chain you are using can be found on the [Supported Networks & Parameters](/randcast/supported-networks-and-parameters).

```bash
export ADAPTER_CONTRACT=0xbd57b868bb3374faa88722d2ee7ba3023c744e05 # mainnet adapter contract
export RPC_URL= # Mainnet Alchemy / Infura RPC URL Here
export USER_PUBLIC_KEY= # Eth User You are using to deploy consumer / user contract
export USER_PRIVATE_KEY= # Corresponding Private Key
```

***

#### Create a Subscription

In this step, a subscription is created on the ADAPTER\_CONTRACT using the `createSubscription` method. Save this subscription ID as it will be used later on. The `cast send` command broadcasts a transaction to the Ethereum network.

*(Reference:* [*cast send*](https://book.getfoundry.sh/reference/cast/cast-send)*)*

```bash
cast block-number --rpc-url $RPC_URL # This will be useful for step 3
cast send $ADAPTER_CONTRACT "createSubscription()(uint64)" --private-key $USER_PRIVATE_KEY --rpc-url $RPC_URL  # returns subid
```

***

#### Gather Subscription Details

The subscription id can be retrieved from the contract event logs for subsequent steps.

You can provide the block number from Step 2 to speed up the event search.

*(Reference:* [*cast logs*](https://book.getfoundry.sh/reference/cast/cast-logs)*)*

```bash
cast logs --from-block 174270 --to-block latest 'SubscriptionCreated(uint64 indexed subId, address indexed owner)' "" $USER_PUBLIC_KEY --address $ADAPTER_CONTRACT --rpc-url $RPC_URL

#   topics: [ . # Sample response topics
#       0x464722b4166576d3dcbba877b999bc35cf911f4eaf434b7eba68fa113951d0bf # event sig
#       0x0000000000000000000000000000000000000000000000000000000000000001 # subId
#       0x00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8 # user public key
#   ]
export SUB_ID= # export subid for your newly created subscription
```

***

#### Fund the Subscription

Next, fund your subscription with Ethereum. These funds will be used to pay for your subsequent randomness requests.&#x20;

Note: Recommended eth amounts:

* OP Goerli / Mainnet: 0.01 ETH
* Sepolia: 0.1 ETH

If needed, you can use the [Randcast CLI dryrun](/randcast/getting-started/use-randcast-cli#estimate-gas-cost-for-the-fulfillment-of-the-first-request) to estimate how much ETH each randomness request will cost.&#x20;

```bash
cast send $ADAPTER_CONTRACT "fundSubscription(uint64)" $SUB_ID --value 1ether --private-key $USER_PRIVATE_KEY --rpc-url $RPC_URL
```

***

#### Deploy the Consumer Contract

The user needs to deploy a contract that will consume the randomness provided by Randcast. "forge create" is used to compile and deploy the contract.

*(Reference:* [*forge create*](https://book.getfoundry.sh/forge/deploying)*)*

```bash
git clone https://github.com/ARPA-Network/Randcast-User-Contract
cd Randcast-User-Contract
forge create --rpc-url $RPC_URL --private-key $USER_PRIVATE_KEY contracts/user/examples/GetRandomNumberExample.sol:GetRandomNumberExample --constructor-args $ADAPTER_CONTRACT

export USER_CONTRACT= # export address for your newly deployed user contract
```

***

#### Add Consumer to Adapter

In this step, the created consumer contract is added to the adapter contract and linked to your existing subscription via the adapter's `addConsumer` method.

```bash
cast send $ADAPTER_CONTRACT "addConsumer(uint64, address)" $SUB_ID $USER_CONTRACT --private-key $USER_PRIVATE_KEY --rpc-url $RPC_URL
```

***

#### Request Randomness

We can now request randomness via the user contract with the method `getRandomNumber`.

```bash
cast send $USER_CONTRACT "getRandomNumber()" --private-key $USER_PRIVATE_KEY --rpc-url $RPC_URL
```

***

#### Check the Last Randomness

Finally, retrieve the last random number generated with the `getLastRandomness` method.

```bash
cast call $ADAPTER_CONTRACT "getLastRandomness()(uint256)" --rpc-url $RPC_URL
```

After completing the entire process, you should have a user contract capable of requesting randomness from the Randcast adapter contract.


# SharedConsumer: An Out-of-the-Box Randomness Consumer Contract for Lottery and Gaming Demonstration

https\://github.com/ARPA-Network/Randcast-User-Contract

### Overview

`SharedConsumer` is a comprehensive, out-of-the-box smart contract built on top of Randcast that provides ready-to-use randomness solutions for various lottery and gaming scenarios. It serves as both a practical tool for communities seeking immediate randomness functionality and a reference implementation for developers looking to integrate Randcast into their projects.

The contract has been deployed across multiple major blockchain networks as part of the [**Randcast Playground**](https://www.arpanetwork.io/play), demonstrating its reliability and versatility in real-world applications.

### Key Features

#### 🎯 Three Built-in Play Types

`SharedConsumer` supports three distinct randomness use cases:

1. **Draw Tickets** - Fair lottery ticket drawing from a pool
2. **Roll Dice** - Multiple dice rolls with customizable dice sizes
3. **Gacha** - Weighted random selection with rarity tiers (perfect for NFT drops, loot boxes, and card games)

#### 💰 Free Response Callbacks

One of the standout features of `SharedConsumer` is that **response callbacks from trial subscription are completely free**. Users only pay for the initial randomness request, making it cost-effective for lottery and gaming demonstrations.

#### 📚 Developer Reference

As a demonstration project, `SharedConsumer` showcases best practices for:

* Integrating with Randcast Adapter
* Implementing randomness callbacks
* Managing subscriptions and fees
* Handling different randomness request types
* Gas optimization strategies

### Deployment Status

The `SharedConsumer` contract has been deployed as part of the [**Randcast Playground**](https://www.arpanetwork.io/play) on multiple major blockchain networks, including:

* **Ethereum Mainnet**(<https://etherscan.io/address/0x8acfc64bF976488E9B83c517D4185Fd4D8A9D683>)
* **BSC Mainnet**(<https://bscscan.com/address/0x9a599D28907780289bB980ddFA38A17B1176FC29>)
* **Base Mainnet**(<https://basescan.org/address/0xC9519853F9E9576303dB70a054d320aCA82005Ad>)
* **Optimism Mainnet**(<https://optimistic.etherscan.io/address/0x9B9b0ea8b7a565dB81D3C78129626f077D47f7B9>)
* **Taiko Mainnet**(<https://taikoscan.io/address/0x9A6E06aa83eBF588c136dD7991d9002DF1E181CF>)
* **Ethereum Hoodi Testnet**(<https://hoodi.etherscan.io/address/0x1D2c2d06e6d923B2B88B1CDDb0955d418fae48a8>)
* And more...

The Playground serves as:

1. **Live Deployment**: Real-world testing and usage by communities across multiple networks
2. **Reference Implementation**: Code examples and integration patterns
3. **Cost Demonstration**: Showcasing the efficiency of Randcast's subscription model

### Integration Guide

#### For Communities (Quick Start)

1. **Connect to Playground**: Open the Playground website at <https://www.arpanetwork.io/play>
2. **Select a Network**: Choose the network you want to use from the dropdown menu
3. **Read the Rules**: Read the rules(`help` command) for the play type and the parameters you want to use
4. **Send the Request**: In a simulated cmd window, enter the command `cast/draw/gacha` to send the request
5. **Wait for the result**: The result will be displayed in the console
6. **Pay Only for Requests**: Response callbacks are free!

#### For Developers (Custom Integration)

1. **Study the Contract**: Review `SharedConsumer.sol` as a reference implementation

```bash
forge install ARPA-Network/Randcast-User-Contract
```

2. **Understand Patterns**: Learn how to:
   * Integrate with `BasicRandcastConsumerBase`
   * Implement `_fulfillRandomWords` and `_fulfillRandomness` callbacks
   * Calculate gas limits dynamically
   * Manage subscriptions
3. **Adapt to Your Needs**: Customize the contract for your specific use case
4. **Deploy Your Own**: Use the patterns learned to build your own consumer contract

### Contract Architecture

#### Inheritance Structure

```solidity
contract SharedConsumer is
    RequestIdBase,
    BasicRandcastConsumerBase,
    UUPSUpgradeable,
    OwnableUpgradeable
```

* **RequestIdBase**: Provides request ID generation utilities
* **BasicRandcastConsumerBase**: Base contract for Randcast integration
* **UUPSUpgradeable**: Allows contract upgrades via UUPS proxy pattern
* **OwnableUpgradeable**: Provides access control for administrative functions

#### Core Components

**Play Types**

```solidity
enum PlayType {
    Draw,    // Lottery ticket drawing
    Roll,     // Dice rolling
    Gacha     // Weighted random selection
}
```

**Subscription Management**

The contract:

* Supports trial subscription for demo purposes
* Allows users to use their own subscriptions
* Allows users to cancel their subscriptions

### Functionality Details

#### 1. Draw Tickets (`drawTickets`)

Fairly selects winners from a pool of tickets.

**Use Cases:**

* Community giveaways
* Airdrop distribution
* Contest winner selection
* Token distribution lotteries

**Parameters:**

* `totalNumber`: Total number of tickets in the pool (max 1000)
* `winnerNumber`: Number of winners to select
* `subId`: Subscription ID (0 for auto-creation)
* `seed`: Random seed for request
* `requestConfirmations`: Number of block confirmations (0 for default)
* `message`: Optional message (often merkle root of ticket list)

**Example:**

```solidity
// Draw 10 winners from 1000 tickets
bytes32 requestId = sharedConsumer.drawTickets(
    1000,  // totalNumber
    10,    // winnerNumber
    0,     // subId (auto-create)
    123,   // seed
    0,     // requestConfirmations
    ""     // message
);
```

**Events:**

* `DrawTicketsRequest`: Emitted when request is made
* `DrawTicketsResult`: Emitted with winner ticket numbers

#### 2. Roll Dice (`rollDice`)

Performs multiple dice rolls with customizable dice sizes.

**Use Cases:**

* Gaming mechanics
* Random number generation
* Decision making tools
* Multi-outcome randomness

**Parameters:**

* `bunch`: Number of dice rolls (max 100)
* `size`: Number of sides on each die
* `subId`: Subscription ID (0 for auto-creation)
* `seed`: Random seed for request
* `requestConfirmations`: Number of block confirmations (0 for default)
* `message`: Optional message

**Example:**

```solidity
// Roll 5 dice, each with 6 sides
bytes32 requestId = sharedConsumer.rollDice(
    5,   // bunch
    6,   // size
    0,   // subId
    456, // seed
    0,   // requestConfirmations
    ""   // message
);
```

**Events:**

* `RollDiceRequest`: Emitted when request is made
* `RollDiceResult`: Emitted with dice roll results (1-indexed)

#### 3. Gacha (`gacha`)

Performs weighted random selection with rarity tiers and upper limits.

**Use Cases:**

* NFT drops with rarity tiers
* Loot box mechanics
* Card pack opening
* Item rarity distribution

**Parameters:**

* `count`: Number of items to draw (max 100)
* `weights`: Array of weights for each rarity tier
* `upperLimits`: Array of upper limits for each tier (index range)
* `subId`: Subscription ID (0 for auto-creation)
* `seed`: Random seed for request
* `requestConfirmations`: Number of block confirmations (0 for default)
* `message`: Optional message

**Example:**

```solidity
// Draw 10 items with 4 rarity tiers
uint256[] memory weights = new uint256[](4);
weights[0] = 7;  // Common
weights[1] = 5;  // Uncommon
weights[2] = 3;  // Rare
weights[3] = 1;  // Legendary

uint256[] memory upperLimits = new uint256[](4);
upperLimits[0] = 500;  // Common: items 1-500
upperLimits[1] = 200;  // Uncommon: items 1-200
upperLimits[2] = 50;  // Rare: items 1-50
upperLimits[3] = 10;  // Legendary: items 1-10

bytes32 requestId = sharedConsumer.gacha(
    10,           // count
    weights,      // weights
    upperLimits,  // upperLimits
    0,            // subId
    789,          // seed
    0,            // requestConfirmations
    ""            // message
);
```

**Events:**

* `GachaRequest`: Emitted when request is made
* `GachaResult`: Emitted with weight results (tier indices) and index results (item IDs)

**Result Interpretation:**

* `weightResults`: Array indicating which rarity tier was selected for each draw
* `indexResults`: Array indicating the specific item ID within the selected tier

### Events Reference

#### Request Events

* `DrawTicketsRequest`: Lottery ticket drawing request
* `RollDiceRequest`: Dice rolling request
* `GachaRequest`: Gacha/loot box request

#### Result Events

* `DrawTicketsResult`: Winner ticket numbers
* `RollDiceResult`: Dice roll results
* `GachaResult`: Weight and index results

All events include `requestId` for easy tracking and correlation.

### Limitations

* **Draw Tickets**: Maximum 1000 tickets, winner count must be ≤ total tickets
* **Roll Dice**: Maximum 100 rolls per request
* **Gacha**: Maximum 100 items per draw, weights and upperLimits arrays must match length

### Disclaimer

**Important**: While `SharedConsumer` is designed to be functional and has been tested in various scenarios, **users are strongly advised to conduct their own research (DYOR) before deploying or using this contract in production environments**.

* This contract is provided as-is without warranties of any kind
* Users should review the code, understand the risks, and assess suitability for their specific use case
* The contract authors and maintainers are not responsible for any losses or damages resulting from the use of this contract
* Always audit smart contracts before deploying to mainnet with significant value
* Test thoroughly in testnet environments before production use

### Conclusion

`SharedConsumer` represents an out-of-the-box solution for randomness needs in Web3 applications. Whether you're a community looking for immediate lottery functionality or a developer seeking integration patterns, `SharedConsumer` provides a robust foundation built on Randcast's decentralized randomness infrastructure.

The contract's deployment across multiple networks in the Randcast Playground demonstrates its reliability and versatility, while its free response callbacks from trial subscription make it economically viable for lottery and gaming demonstrations.

**Remember**: Always conduct your own research and security audits before deploying to production environments with significant value.

For more information, code examples, and deployment addresses, visit the [Randcast Documentation](https://docs.arpanetwork.io/) and [Randcast Playground](https://www.arpanetwork.io/play).


# Using the SDK

## Introduction

Randcast SDK is a set of smart contracts, libraries, scripts, 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 request randomness as well as consume randomness in a secure and easy way. In the next sections, we will introduce how to write a consumer contract, especially for implementing the callback functions.

We recommend using [Foundry](https://github.com/foundry-rs/foundry) to manage the dependencies, compile the project, run tests, and deploy.

## Installation

```bash
forge install ARPA-Network/Randcast-User-Contract
```

## Remapping Dependencies

You can customize these remappings by creating a `remappings.txt` file in the root of your project.

```
ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/
randcast-user-contract/=lib/Randcast-User-Contract/contracts/
```

## Usage

Now, we can import any of the contracts in `Randcast-User-Contract/contracts` like so:

```solidity
import {
    GeneralRandcastConsumerBase,
    BasicRandcastConsumerBase
} from "randcast-user-contract/user/GeneralRandcastConsumerBase.sol";
```

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 results.

When requesting randomness, we provide three types of randomness: `Randomness`, `RandomWords`, and `Shuffling`.

For experienced developers, you can directly inherit the `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.

***WARNING:** You **MUST** restrict access to the function that requests randomness in your consumer contract. Otherwise, anyone can call this function and use your subscription to pay for randomness requests.*

## Examples

See [Consumer Contract Examples](/randcast/using-the-sdk/consumer-contract-examples) for more details.


# Adapter

### Overview

The `Adapter` is designed to facilitate a range of functionalities, including subscription management, randomness generation, payment calculation, task assignment, rewards distribution, and data retrieval. Developed using the Solidity programming language, the contract operates on the Ethereum blockchain and employs safety mechanisms to ensure secure and reliable interactions. With a focus on extensibility and adaptability, the contract is well-suited for applications that require dynamic interactions, payment calculations, and randomness generation within a decentralized environment.

* **Subscription Management:**
  * The contract allows creating and deleting subscriptions, each identified by a unique ID.
  * Each subscription can have multiple consumers, with a limit defined in the configuration.
  * Subscriptions can be funded by sending Ether to the contract.
* **Randomness Generation:**
  * The contract includes functionality for requesting and fulfilling randomness.
  * Randomness requests have three types and are identified by request IDs.
  * The Adapter contract verifies the randomness and calls the callback function in the user's consumer contract.
* **Payment Calculation:**
  * The contract includes functions to estimate and calculate payment amounts based on gas consumption.
  * Payments are calculated using various configurable fee tiers and gas parameters.
* **Data Retrieval:**
  * The contract includes views and events to query data such as a consumer's last subscription, subscription details, pending requests, and historical callback and payment results.
* **Task Assignment:**
  * The task is assigned to available groups using an algorithm that resembles round-robin scheduling.
* **Rewards Distribution:**
  * The contract includes functionality for rewarding participants in the group based on the configuration.
* **Safety Mechanisms:**
  * The contract includes `nonReentrant` and `notPaused` modifiers to prevent reentrancy attacks and ensure operations are only performed when the contract is not paused.

***Note**: The descriptions provided above are high-level summaries of the functionalities present in the `Adapter.sol` smart contract. For a detailed understanding of the code and its implementation, please refer to the full* [*source code*](https://github.com/ARPA-Network/BLS-TSS-Network/blob/main/contracts/src/Adapter.sol)[ ](https://github.com/ARPA-Network/BLS-TSS-Network/blob/main/contracts/src/Adapter.sol)*of the smart contract.*


# BasicRandcastConsumerBase

`BasicRandcastConsumerBase` contract provides:

* A set of virtual callback functions that the consumer contract can override. These functions are called by the Adapter contract when the randomness request is fulfilled. The consumer contract can override the below functions to implement the subsequent logic on how to utilize the randomness results in their DApp or Web3 game, specifically:&#x20;
  * override `_fulfillRandomness` for requesting `Randomness`
  * override `_fulfillRandomWords` for requesting `RandomWords`
  * override `_fulfillShuffledArray` for requesting `Shuffling`
* A nonce recorder keeps track of the number of random requests that the consumer contract has initiated through a specific subscription. This can be useful for debugging. Please do not try to control the nonce as it will not affect the behavior of the Adapter, i.e., randomness result.
* `A _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 help prevent other parties from maliciously predicting it).
  * `requestConfirmations` to specify the number of blocks required between the randomness request and the randomness fulfillment.
  * `callbackGasLimit` to specify the gas limit you want for the callback function.
  * `callbackMaxGasPrice` to specify the maximum amount of gas you are willing to spend on the callback request.

***Note**:  To learn more about the implementation, please refer to the complete* [*source code*](https://github.com/ARPA-Network/Randcast-User-Contract/blob/main/contracts/user/BasicRandcastConsumerBase.sol) *of the smart contract.*


# GeneralRandcastConsumerBase

The `GeneralRandcastConsumerBase` contract aims to simplify the process of requesting and consuming randomness provided by Randacst. This contract combines the following aspects: `requestId` generation, gas estimation, callback handling, and gas limit/gas fee configuration.

***

Here is a detailed breakdown of what the `GeneralRandcastConsumerBase` contract does:

#### Imports the necessary contracts and libraries:

```solidity
import "../utils/RequestIdBase.sol";
import "../utils/GasEstimationBase.sol";
import "./BasicRandcastConsumerBase.sol";
import "openzeppelin-contracts/contracts/access/Ownable.sol";
```

`RequestIdBase` helps generate unique request IDs for randomness requests.&#x20;

`GasEstimationBase` aids in calculating the gas needed for the callback functions.&#x20;

`BasicRandcastConsumerBase` is the base contract for handling randomness requests and fulfillment.&#x20;

Lastly, OpenZeppelin's `Ownable` contract is used for access control.

***

#### Inherits the imported contracts and libraries:

```solidity
abstract contract GeneralRandcastConsumerBase is
    BasicRandcastConsumerBase,
    RequestIdBase,
    GasEstimationBase,
    Ownable
{ ... }
```

***

#### Sets important constants:

```solidity
uint256 private constant USER_SEED_PLACEHOLDER = 0;
uint256 private constant GAS_FOR_CALLBACK_OVERHEAD = 30_000;
uint256 private constant RANDOMNESS_PLACEHOLDER = 103921425973949831153159651530394295952228049817797655588722524414385831936256;
```

The variables represent the user seed placeholder, the gas overhead for callback execution, and a placeholder for initial entropy.

***

#### Defines public variables for callback gas limit, max gas fee, and request confirmations(blocks):

```solidity
uint256 public callbackGasLimit;
uint256 public callbackMaxGasFee;
uint16 public requestConfirmations;
```

***Note**: These variables can be set manually or calculated automatically.*

***

#### Provides the `setCallbackGasConfig()` function and `setRequestConfirmations()` function to allow the owner to set the `callbackGasLimit`, `callbackMaxGasFee` and `requestConfirmations` manually:

```solidity
function setCallbackGasConfig(uint256 _callbackGasLimit, uint256 _callbackMaxGasFee) external onlyOwner {
    callbackGasLimit = _callbackGasLimit;
    callbackMaxGasFee = _callbackMaxGasFee;
}

function setRequestConfirmations(uint16 _requestConfirmations) external onlyOwner {
    requestConfirmations = _requestConfirmations;
}
```

***

#### Implements the `requestRandomness()` function for requesting randomness from the Adapter:

```solidity
function _requestRandomness(RequestType requestType, bytes memory params)
    internal
    returns (bytes32)
{ ... }
```

This function takes `requestType` and `params` as arguments and estimates the required gas for the user-implemented callback function when `callbackGasLimit` is not set.

***

#### Estimates the required gas for the callback function:

```solidity
function _dryRunCallbackToEstimateGas(RequestType requestType, bytes memory params, uint64 subId)
    internal
    isDryRun
    returns (uint256)
{
    // Prepares the message call of the callback function according to the request type
    bytes memory data;
    ...
}
```

When the `callbackGasLimit` is not set, this function will estimate the required gas for the callback function based on the request type.

***

#### Sends a raw randomness request to the Adapter:

```solidity
return _rawRequestRandomness(
      requestType,
      params,
      subId,
      _USER_SEED_PLACEHOLDER,
      requestConfirmations,
      callbackGasLimit,
      callbackMaxGasFee == 0 ? tx.gasprice * 3 : callbackMaxGasFee
);   
```

The `rawRequestRandomness()` function sends the randomness request to the Adapter with the proper parameters, including the request type, params, subscription ID, user seed, request confirmations, callback gas limit, and callback max gas price. **If the `callbackMaxGasFee` is not set, it defaults to three times the transaction gas price.** If `requestConfirmations` is not set, it defaults to `minimumRequestConfirmations` in the Adapter config.

***

In summary, the `GeneralRandcastConsumerBase` contract provides an extendable base for contracts that need to request randomness from Randcast. It streamlines requesting and receiving randomness by automating nonce management, gas estimation, and callback handling.&#x20;

Additionally, it allows manual configuration of `requestConfirmations`, callback gas limits and fees for flexibility.

***Note**: To know more about the implementation, please refer to the full* [*source code*](https://github.com/ARPA-Network/Randcast-User-Contract/blob/main/contracts/user/GeneralRandcastConsumerBase.sol) *of the smart contract.*

Next, we will look at an example of requesting a random number by extending `GeneralRandcastConsumerBase`.


# Randcast Utilities

On receiving the randomness result, the consumer contract can use the `RandcastSDK` library to process the randomness result further. The [library](https://github.com/ARPA-Network/Randcast-User-Contract/blob/main/contracts/user/RandcastSDK.sol) provides functions to convert the randomness result to different types that can be used in a DApp or Web3 game.

We introduce five utility functions in `RandcastSDK.sol` :

**shuffle**: This function generates a shuffled array of uniformly distributed numbers within a specified range. You can use this method for randomizing the ordering of a sequence of numbers. Examples of such usage might include shuffling cards, generating lottery numbers,  etc.

```solidity
function shuffle(uint256 upper, uint256 randomness) pure returns (uint256[] memory)
```

**draw**: This function returns a subset of randomly chosen elements from an array. You can use this function for selecting one or more elements from an existing array with equal possibility. It can be used for [drawing a lottery](/randcast/using-the-sdk/consumer-contract-examples/example-draw-a-lottery), drawing cards, selecting airdrop winners, etc.

<pre class="language-solidity"><code class="lang-solidity"><strong>function draw(uint256 seed, uint256[] memory indices, uint256 count) pure returns (uint256[] memory)
</strong></code></pre>

**roll**: This function performs a simple random roll to pick a number within a specified size. You can use this function for selecting a number with equal possibility in a given range. For example, rolling a dice, determining the outcome of a game, [randomly generating game character attributes](/randcast/using-the-sdk/consumer-contract-examples/example-pick-a-property), etc.

```solidity
function roll(uint256 randomness, uint256 size) pure returns (uint256 number)
```

**pickByWeights**: This function chooses an index based on the given weights. It can be used when the outcome is probability-based. For example, drawing cards, minting NFTs [with different rarity](/randcast/using-the-sdk/consumer-contract-examples/example-pick-a-rarity), or picking winners in a lottery.

```solidity
function pickByWeights(uint256 randomness, uint256[] memory valueWeights) pure returns(uint256 chosenIndex)
```

**batch**: This function generates a batch of random numbers based on a given seed. You can choose this function for generating a set of random values through a single request. For example, generating a set of lottery tickets, verification codes, etc.

```solidity
function batch(uint256 seed, uint256 length) pure returns (uint256[] memory)
```

These functions essentially cover most of the usage scenarios for random numbers. You can use them based on your needs or modify them according to your requirements.


# Consumer Contract Examples


# Example - Get Random Number

The `GetRandomNumberExample` contract is an example of how to use the `GeneralRandcastConsumerBase` to request and consume randomness of type `Randomness`.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import {
    GeneralRandcastConsumerBase,
    BasicRandcastConsumerBase
} from "randcast-user-contract/user/GeneralRandcastConsumerBase.sol";

contract GetRandomNumberExample is GeneralRandcastConsumerBase {
    /* requestId -> randomness */
    mapping(bytes32 => uint256) public randomResults;
    uint256[] public randomnessResults;

    constructor(address adapter) BasicRandcastConsumerBase(adapter) {}

    /**
     * Requests randomness
     */
    function getRandomNumber() external returns (bytes32) {
        bytes memory params;
        return requestRandomness(RequestType.Randomness, params);
    }

    /**
     * Callback function used by Randcast Adapter
     */
    function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
        randomResults[requestId] = randomness;
        randomnessResults.push(randomness);
    }

    function lengthOfRandomnessResults() public view returns (uint256) {
        return randomnessResults.length;
    }

    function lastRandomnessResult() public view returns (uint256) {
        return randomnessResults[randomnessResults.length - 1];
    }
}
```

Here's a step-by-step explanation of the `GetRandomNumberExample` contract:

1. Inherit the `GeneralRandcastConsumerBase` contract:

```solidity
contract GetRandomNumberExample is GeneralRandcastConsumerBase { ... }
```

2. Define a mapping `randomResults` to store randomness associated with each request ID:

```solidity
mapping(bytes32 => uint256) public randomResults;
```

3. Define an array `randomnessResults` to store all received randomness values:

```solidity
uint256[] public randomnessResults;
```

4. Implement the constructor to initialize the `adapter` address from the `GeneralRandcastConsumerBase` contract:

```solidity
constructor(address adapter) BasicRandcastConsumerBase(adapter) {}
```

5. Provide the `getRandomNumber()` function to request randomness:

```solidity
function getRandomNumber() external returns (bytes32) {
    bytes memory params;
    return requestRandomness(RequestType.Randomness, params);
}
```

This function calls the `requestRandomness()` function from the `GeneralRandcastConsumerBase` contract, which sends the randomness request to the `adapter`.

6. Override the `fulfillRandomness()` callback function:

```solidity
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
    randomResults[requestId] = randomness;
    randomnessResults.push(randomness);
}
```

When the `adapter` fulfills the randomness request, this function is called with the `requestId` and the generated `randomness`. The randomness value is stored in both the `randomResults` mapping and the `randomnessResults` array.

7. Provides helper functions to retrieve the length of the `randomnessResults` array and the last randomness result:

```solidity
function lengthOfRandomnessResults() public view returns (uint256) {
    return randomnessResults.length;
}

function lastRandomnessResult() public view returns (uint256) {
    return randomnessResults[randomnessResults.length - 1];
}
```

These functions allow users to query the length of the `randomnessResults` array and retrieve the last received randomness value.

Next, we will look at another example that uses the received random number as an entropy to throw a dice.


# Example - Roll a Dice

The `RollDiceExample` contract is an example of how to use the `GeneralRandcastConsumerBase` to request and consume randomness of type `RandomWords` simulating dice rolls.&#x20;

````solidity
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import {
    GeneralRandcastConsumerBase,
    BasicRandcastConsumerBase
} from "randcast-user-contract/user/GeneralRandcastConsumerBase.sol";
// solhint-disable-next-line no-global-import
import "randcast-user-contract/user/RandcastSDK.sol" as RandcastSDK;

contract RollDiceExample is GeneralRandcastConsumerBase {
    /* requestId -> randomness */
    mapping(bytes32 => uint256[]) public randomResults;
    uint256[] public diceResults;

    // solhint-disable-next-line no-empty-blocks
    constructor(address adapter) BasicRandcastConsumerBase(adapter) {}

    /**
     * Requests randomness
     */
    function rollDice(uint32 bunch) external returns (bytes32) {
        bytes memory params = abi.encode(bunch);
        return _requestRandomness(RequestType.RandomWords, params);
    }

    /**
     * Callback function used by Randcast Adapter
     */
    function _fulfillRandomWords(bytes32 requestId, uint256[] memory randomWords) internal override {
        randomResults[requestId] = randomWords;
        diceResults = new uint256[](randomWords.length);
        for (uint32 i = 0; i < randomWords.length; i++) {
            diceResults[i] = RandcastSDK.roll(randomWords[i], 6) + 1;
        }
    }

    function lengthOfDiceResults() public view returns (uint256) {
        return diceResults.length;
    }
}
````

Here's a step-by-step explanation of the `RollDiceExample` contract and RandcastSDK:

1. Import `GeneralRandcastConsumerBase` contract:

```solidity
import {GeneralRandcastConsumerBase, BasicRandcastConsumerBase} from "../GeneralRandcastConsumerBase.sol";
import "src/user/RandcastSDK.sol" as RandcastSDK;
```

2. Inherit the `GeneralRandcastConsumerBase` contract:

```solidity
contract RollDiceExample is GeneralRandcastConsumerBase { ... }
```

3. Defines two mappings to store the random results for each requestId and the processed dice roll results:

```solidity
mapping(bytes32 => uint256[]) public randomResults;
uint256[] public diceResults;
```

4. Implement the constructor, which takes the `adapter` address as an argument and passes it to the base contract:

```solidity
constructor(address adapter) BasicRandcastConsumerBase(adapter) {}
```

5. Provide the `rollDice()` function that takes a `bunch` argument, representing the number of random words (dice rolls) to request, and sends the randomness request using the \_requestRandomness`()` function:

```solidity
function rollDice(uint32 bunch) external returns (bytes32) {
    bytes memory params = abi.encode(bunch);
    return _requestRandomness(RequestType.RandomWords, params);
}
```

6. Override the `_fulfillRandomWords()` function from `BasicRandcastConsumerBase` to handle the received random words and use`RandcastSDK.roll` to process them into dice roll results:

```solidity
function _fulfillRandomWords(bytes32 requestId, uint256[] memory randomWords) internal override {
    randomResults[requestId] = randomWords;
    diceResults = new uint256[](randomWords.length);
    for (uint32 i = 0; i < randomWords.length; i++) {
        diceResults[i] = RandcastSDK.roll(randomWords[i], 6) + 1;
    }
}
```

7. Implement a `lengthOfDiceResults()` function to return the length of the `diceResults` array:

```solidity
function lengthOfDiceResults() public view returns (uint256) {
    return diceResults.length;
}
```

The `RollDiceExample` contract showcases how to request randomness and process the received random words to simulate dice rolls. The contract leverages the capabilities provided by the `GeneralRandcastConsumerBase` contract to interact with the randomness `Adapter`.

In the next example, we will try to shuffle a deck of cards using the entropy retrieved.&#x20;


# Example - Shuffle an Array

The `GetShuffledArrayExample` is an example of how to use the `GeneralRandcastConsumerBase` to request and consume randomness of type `Shuffling` to request a shuffled array.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import {
    GeneralRandcastConsumerBase,
    BasicRandcastConsumerBase
} from "randcast-user-contract/user/GeneralRandcastConsumerBase.sol";

contract GetShuffledArrayExample is GeneralRandcastConsumerBase {
    /* requestId -> randomness */
    mapping(bytes32 => uint256[]) public randomResults;
    uint256[] public shuffleResults;

    constructor(address adapter) BasicRandcastConsumerBase(adapter) {}

    /**
     * Requests randomness
     */
    function getShuffledArray(uint32 upper) external returns (bytes32) {
        bytes memory params = abi.encode(upper);
        return requestRandomness(RequestType.Shuffling, params);
    }

    /**
     * Callback function used by Randcast Adapter
     */
    function fulfillShuffledArray(bytes32 requestId, uint256[] memory array) internal override {
        randomResults[requestId] = array;
        shuffleResults = array;
    }

    function lengthOfShuffleResults() public view returns (uint256) {
        return shuffleResults.length;
    }
}
```

Here's a breakdown of the `GetShuffledArrayExample` contract:

1. Imports the necessary contract:

```solidity
import "../GeneralRandcastConsumerBase.sol";
```

2. Inherits the imported contract:

```solidity
contract GetShuffledArrayExample is GeneralRandcastConsumerBase { ... }
```

3. Defines a mapping and an array to store the randomness results:

```solidity
mapping(bytes32 => uint256[]) public randomResults;
uint256[] public shuffleResults;
```

`randomResults` maps request IDs to the corresponding shuffled arrays received from the `adapter`. `shuffleResults` holds the latest shuffled array.

4. Initializes the contract with the `adapter` address:

```solidity
constructor(address adapter) BasicRandcastConsumerBase(adapter) {}
```

5. Implements the `getShuffledArray()` function to request a shuffled array:

```solidity
function getShuffledArray(uint32 upper) external returns (bytes32) {
    bytes memory params = abi.encode(upper);
    return requestRandomness(RequestType.Shuffling, params);
}
```

The function takes an `upper` parameter to define the upper bound of the array elements (0 to `upper` - 1) and requests a shuffled array from the `adapter` using the `requestRandomness` function inherited from `GeneralRandcastConsumerBase`.

6. Overrides the `fulfillShuffledArray()` function to handle the shuffled array returned by the `adapter`:

```solidity
function fulfillShuffledArray(bytes32 requestId, uint256[] memory array) internal override {
    randomResults[requestId] = array;
    shuffleResults = array;
}
```

This function stores the shuffled array in the `randomResults` mapping and updates the `shuffleResults` array with the latest shuffled array.

7. Implements the `lengthOfShuffleResults()` function to return the length of the latest shuffled array:

```solidity
function lengthOfShuffleResults() public view returns (uint256) {
    return shuffleResults.length;
}
```

In summary, the `GetShuffledArrayExample` contract demonstrates how to use the `GeneralRandcastConsumerBase` to request a shuffled array of elements from a randomness `Adapter` and store the received shuffled arrays.


# Example - Draw a Lottery

The `DrawLotteryExample` illustrates the utilization of `GeneralRandcastConsumerBase` and [`RandcastSDK`](/randcast/using-the-sdk/randcast-utilities) to construct a straightforward lottery system.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import {
    GeneralRandcastConsumerBase,
    BasicRandcastConsumerBase
} from "randcast-user-contract/user/GeneralRandcastConsumerBase.sol";
// solhint-disable-next-line no-global-import
import "randcast-user-contract/user/RandcastSDK.sol" as RandcastSDK;

contract DrawLotteryExample is GeneralRandcastConsumerBase {
    /* requestId -> randomness */
    mapping(bytes32 => uint256[]) public randomResults;
    uint256[] public winnerResults;
    uint32 public ticketNumber;
    uint32 public winnerNumber;

    // solhint-disable-next-line no-empty-blocks
    constructor(address adapter) BasicRandcastConsumerBase(adapter) {}
    event LotteryTicketGenerated(uint256[] ticketResults);
    /**
     * Requests randomness
     */
    function getTickets(uint32 ticketNum, uint32 winnerNum) external returns (bytes32) {
        ticketNumber = ticketNum;
        winnerNumber = winnerNum;
        bytes memory params = abi.encode(ticketNumber);
        return _requestRandomness(RequestType.RandomWords, params);
    }

    /**
     * Callback function used by Randcast Adapter
     */
    function _fulfillRandomWords(bytes32 requestId, uint256[] memory randomWords) internal override {
        randomResults[requestId] = randomWords;
        emit LotteryTicketGenerated(randomWords);
        winnerResults = RandcastSDK.draw(randomWords[randomWords.length - 1], randomWords, winnerNumber);
    }

    function lengthOfWinnerResults() public view returns (uint256) {
        return winnerResults.length;
    }

    function getTicketResults(bytes32 requestId) public view returns (uint256[] memory) {
        return randomResults[requestId];
    }
}

```

The contract structure is similar to the previous example:

It first issues a random number request, setting the request type to`RequestType.RandomWord`, and sets `params` for the length of the returned array (the total number of lottery tickets). Upon receiving the tickets in the form of an array, in the `_fulfillRandomWords` function, we call the `RandcastSDK.draw` method to randomly pick a subset of elements based on their index positions in the array representing the winners.

If you want to draw a subset of elements from an existing array, consider requesting a single randomness by using `_requestRandomness(RequestType.Randomness, params)` and then calling `draw(randomness, indices, winnerNumber)`.


# Example - Pick a Property

The `PickPropertyExample`contract illustrates the utilization of `GeneralRandcastConsumerBase` and [`RandcastSDK`](/randcast/using-the-sdk/randcast-utilities) to pick one of several predefined properties.&#x20;

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import {
    GeneralRandcastConsumerBase,
    BasicRandcastConsumerBase
} from "randcast-user-contract/user/GeneralRandcastConsumerBase.sol";
// solhint-disable-next-line no-global-import
import "randcast-user-contract/user/RandcastSDK.sol" as RandcastSDK;

contract PickPropertyExample is GeneralRandcastConsumerBase {
    /* requestId -> randomness */
    mapping(bytes32 => uint256) public randomResults;
    mapping(uint256 => string) public propertyValue;
    uint256 public indexResult;
    event PropertyValueResult(string);
    
    constructor(address controller) BasicRandcastConsumerBase(controller) {
        propertyValue[0] = "fire";
        propertyValue[1] = "wind";
        propertyValue[2] = "water";
    }

    /**
     * Requests randomness
     */
    function getProperty() external returns (bytes32) {
        bytes memory params;
        return _requestRandomness(RequestType.Randomness, params);
    }

    /**
     * Callback function used by Randcast Adapter
     */
    // solhint-disable-next-line
    function _fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
        randomResults[requestId] = randomness;
        uint256 propertyIndex = RandcastSDK.roll(randomness, 3);
        indexResult = propertyIndex;
        emit PropertyValueResult(propertyValue[propertyIndex]);
    }
}

```

The contract structure is similar to the previous example. The main components include the `constructor`, the function `getProperty` which is used to request random numbers, and the callback function `_fulfillRandomness` which is triggered after generating the random number.

In the `constructor`, define each index to represent the properties of 'fire,' 'wind,' and 'water.'

The function `getProperty`  requests the random number to be used in the callback function.

In the callback function `_fulfillRandomness`we call `RandcastSDK.roll` to pick the index of the three properties. We can then get the property value by index from the `propertyValue` mapping.

The reason to choose this [`RandcastSDK`](/randcast/using-the-sdk/randcast-utilities) function in this example is that we need to select one of the three properties with equal probability randomly, and `RandcastSDK.roll` is designed for randomly picking a number within a specified range.


# Example - Pick a Rarity

The `PickRarityExample` contract, by extending `GeneralRandcastConsumerBase` and calling functions from `RandcastSDK`, demonstrates a system for drawing a rarity level.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import {
    GeneralRandcastConsumerBase,
    BasicRandcastConsumerBase
} from "randcast-user-contract/user/GeneralRandcastConsumerBase.sol";
// solhint-disable-next-line no-global-import
import "randcast-user-contract/user/RandcastSDK.sol" as RandcastSDK;

contract PickRarityExample is GeneralRandcastConsumerBase {
    /* requestId -> randomness */
    mapping(bytes32 => uint256) public randomResults;
    mapping(uint256 => string) public rarityValue;
    uint256 public indexResult;
    uint256[] public rarityWeights = new uint256[](5);
    event RarityValueResult(string);
    constructor(address controller) BasicRandcastConsumerBase(controller) {
        rarityValue[0] = "SSSR";
        rarityValue[1] = "SSR";
        rarityValue[2] = "SR";
        rarityValue[3] = "R";
        rarityValue[4] = "C";

        rarityWeights[0] = 1;  // SSSR(1%)
        rarityWeights[1] = 4;  // SSR(4%)
        rarityWeights[2] = 10; // SR(10%)
        rarityWeights[3] = 20; // R(20%)
        rarityWeights[4] = 65; // C(65%)
    }

    /**
     * Requests randomness
     */
    function getRarity() external returns (bytes32) {
        bytes memory params;
        return _requestRandomness(RequestType.Randomness, params);
    }

    /**
     * Callback function used by Randcast Adapter
     */
    // solhint-disable-next-line
    function _fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
        randomResults[requestId] = randomness;
        uint256 rarityIndex = RandcastSDK.pickByWeights(randomness, rarityWeights);
        indexResult = rarityIndex;
        emit RarityValueResult(rarityValue[rarityIndex]);
    }
}

```

This contract also has three important parts: the `constructor` function, `getRarity` function, and `_fulfillRandomWords` function.

In the `constructor` , the `rarityValue` is being initialized with five string elements. These values correspond to the rarity levels of certain items or characters: 'SSSR,' 'SSR,' 'SR,' 'R,' and 'C,' each with an index from 0 to 4. The `rarityWeights` array is initialized with five integer elements representing the weights associated with the rarity levels defined earlier.

The function `getRarity` is used to request a random number.

`_fulfillRandomness` calls the `RandcastSDK.pickByWeights` function which returns an index based on a predefined `rarityWeights` to determine the rarity of the card drawn in this round. The lower weight corresponds to a lower probability and, therefore, a higher rarity. In this example, 'SSR' is the rarest, with the lowest probability(weight), while 'C' is the most common, with the highest probability(weight).


# Example - Pick a Winner

The `PickWinnerExample` contract, inheriting `GeneralRandcastConsumerBase` and calling functions in [RandcastSDK](/randcast/using-the-sdk/randcast-utilities), implements the functionality to pick a winner between two players.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import {
    GeneralRandcastConsumerBase,
    BasicRandcastConsumerBase
} from "randcast-user-contract/user/GeneralRandcastConsumerBase.sol";
// solhint-disable-next-line no-global-import
import "randcast-user-contract/user/RandcastSDK.sol" as RandcastSDK;

contract PickWinnerExample is GeneralRandcastConsumerBase {
    /* requestId -> randomness */
    mapping(bytes32 => uint256) public randomResults;
    uint256 public indexResult;
    mapping(uint256 => string) public indexToName;
    event WinnerResult(string);

    constructor(address controller) BasicRandcastConsumerBase(controller) {
        indexToName[0] = "player1 win";
        indexToName[1] = "player2 win";
        indexToName[2] = "tie";
    }

    /**
     * Requests randomness
     */
    function getWinner() external returns (bytes32) {
        bytes memory params;
        return _requestRandomness(RequestType.Randomness, params);
    }

    /**
     * Callback function used by Randcast Adapter
     */
    // solhint-disable-next-line
    function _fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
        randomResults[requestId] = randomness;
        uint256 winnerIndex = RandcastSDK.roll(randomness, 3);
        indexResult = winnerIndex;
        emit WinnerResult(indexToName[winnerIndex]);
    }
}

```

The three important components of the contract are the `constructor` function, `getWinner` function and the callback function `_fulfillRandomness`.

Inside the constructor, we set the value of `indexToName`, this variable maps the indices to strings denoting the outcome of a game (Player 1 wins, player 2 wins, or tie).&#x20;

The function `getWinner` is used to request a random number.

In the callback function `_fulfillRandomness`, we use the random number requested earlier to call the `RandcastSDK.roll` function. This gives us a random index between 0 and 2. This index corresponds to the win/loss result defined in `indexToName`.

In this example, the probability of player 1 and player 2 winning, losing, or tying are the same. Therefore, we can use the `RandcastSDK.roll` function to decide the outcome of the game randomly.


# Example - Advanced Request

The `AdvancedGetShuffledArrayExample` shows how to request and consume randomness of type `Shuffling` without extending `GeneralRandcastConsumerBase`.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import {BasicRandcastConsumerBase} from "randcast-user-contract/user/BasicRandcastConsumerBase.sol";
import {RequestIdBase} from "randcast-user-contract/utils/RequestIdBase.sol";
// solhint-disable-next-line no-global-import
import "randcast-user-contract/user/RandcastSDK.sol" as RandcastSDK;

contract AdvancedGetShuffledArrayExample is RequestIdBase, BasicRandcastConsumerBase, RandomnessHandler {
    mapping(bytes32 => uint256) public shuffledArrayUppers;
    uint256[][] public shuffleResults;

    // solhint-disable-next-line no-empty-blocks
    constructor(address adapter) BasicRandcastConsumerBase(adapter) {}

    /**
     * Requests randomness
     */
    function getRandomNumberThenGenerateShuffledArray(
        uint256 shuffledArrayUpper,
        uint64 subId,
        uint256 seed,
        uint16 requestConfirmations,
        uint32 callbackGasLimit,
        uint256 callbackMaxGasPrice
    ) external returns (bytes32) {
        bytes memory params;

        uint256 rawSeed = _makeRandcastInputSeed(seed, address(this), nonce);
        // This should be identical to controller generated requestId.
        bytes32 requestId = _makeRequestId(rawSeed);
        shuffledArrayUppers[requestId] = shuffledArrayUpper;

        return _rawRequestRandomness(
            RequestType.Randomness, params, subId, seed, requestConfirmations, callbackGasLimit, callbackMaxGasPrice
        );

        // These equals to following code(recommended):
        // bytes32 requestId = rawRequestRandomness(
        //    RequestType.Randomness,
        //    params,
        //    subId,
        //    seed,
        //    requestConfirmations,
        //    callbackGasLimit,
        //    callbackMaxGasPrice
        // );

        // shuffledArrayUppers[requestId] = shuffledArrayUpper;
    }

    /**
     * Callback function used by Randcast Adapter
     */
    function _fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
        shuffleResults.push(RandcastSDK.shuffle(shuffledArrayUppers[requestId], randomness));
    }

    function lengthOfShuffleResults() public view returns (uint256) {
        return shuffleResults.length;
    }
}
```

In `getRandomNumberThenGenerateShuffledArray` function, before requesting randomness, you can have more flexibility.  For example, you can pass the user seed as input parameter, and calculate the raw seed and request id of the next randomness request before actually calling the `_rawRequestRandomness` function.&#x20;

Also, you can set `subId`, `requestConfirmations`, `callbackGasLimit`, and `callbackMaxGasPrice` specifically for each randomness request.


# Supported Networks & Parameters

***Note**:* If you have a demand for usage, please contact us <contact@arpanetwork.io> for a package discount.

## Production Environment

### Ethereum Mainnet:

* Block Explorer = <https://etherscan.io/>
* ARPA Address = 0xBA50933C268F567BDC86E1aC131BE072C6B0b71a
* Staking Address = 0xee710f79aa85099e200be4d40cdf1bfb2b467a01
* NodeRegistry Address = 0x58e39879374901e17A790af039DC9Ac06baCf25B
* ServiceManager Address = 0x1DE75EaAb2df55d467494A172652579E6FA4540E
* Controller Address = 0xBcA1a9cA6B460E6B265DBcf7249b45BDdC381Dfd
* Adapter Address = 0x4363154E1eC107F81239A4F0b1CB3AB5161129Ca
* Chain Id = 1
* Flat Fee = 0.000002 ETH
* Minimum Confirmations = 1
* Maximum Confirmations = 200
* Maximum Gas Limit = 3000000
* Default Minimum Threshold = 3

### BSC Mainnet:

* Block Explorer = <https://bscscan.com/>
* ARPA Address = 0x6F769E65c14Ebd1f68817F5f1DcDb61Cfa2D6f7e
* ControllerOracle Address = 0x2D9DB6d188BF9431458C3c89cbCcb8126820d790
* Adapter Address = 0x3bA2dbD7466919affea5Fe34cc642d7d669eF6aa
* Chain Id = 56
* Flat Fee = 0.000006 BNB
* Minimum Confirmations = 1
* Maximum Confirmations = 200
* Maximum Gas Limit = 3000000
* Default Minimum Threshold = 3

### ARPA Chain Mainnet:

* Block Explorer = <https://arpachain.calderaexplorer.xyz/>
* ARPA Address = 0x3292df712deCc662208B59e810f1fA1F061F585b
* ControllerOracle Address = 0x59c6995aBcdA50c1c26e095628f0Bbd02a468f0C
* Adapter Address = 0xBd57b868BB3374faA88722D2ee7bA3023C744e05
* Chain Id = 4224
* Flat Fee = 0.000001 ETH
* Minimum Confirmations = 1
* Maximum Confirmations = 200
* Maximum Gas Limit = 3000000
* Default Minimum Threshold = 3

### OP Mainnet:

* Block Explorer = <https://optimistic.etherscan.io/>
* ARPA Address = 0x334cc734866E97D8452Ae6261d68Fd9bc9BFa31E
* ControllerOracle Address = 0x7Be7DAe177a2488fc63c0561D6aa9c6c8607c4EE
* Adapter Address = 0x12Dd67e8DeFd6D0a4f5B648ACb5222713bfd6A2f
* Chain Id = 10
* Flat Fee = 0.000002 ETH
* Minimum Confirmations = 1
* Maximum Confirmations = 200
* Maximum Gas Limit = 3000000
* Default Minimum Threshold = 3

### TON Mainnet:

We collaborate with TonRandcast to deliver verifiable random numbers for games and lotteries on TON, please refer to <https://tonrandcast.gitbook.io/tonrandcast> for details.

### Base Mainnet:

* Block Explorer = <https://basescan.org/>
* ARPA Address = 0x1C9Fa01e87487712706Fb469a13bEb234262C867
* ControllerOracle Address = 0x5D7bb19fC0856f5bc74b66f2c7b0258c1aeafD7f
* Adapter Address = 0xDBa5dE33511b4b8549994d30E73b47fedc8cC2C2
* Chain Id = 8453
* Flat Fee = 0.000002 ETH
* Minimum Confirmations = 1
* Maximum Confirmations = 200
* Maximum Gas Limit = 3000000
* Default Minimum Threshold = 3

### Redstone Mainnet:

* Block Explorer = <https://explorer.redstone.xyz/>
* ARPA Address = 0xf18BB8A4Ca69F1A645BF2e968C69DCa72172C175
* ControllerOracle Address = 0x1499803d9116d1112753f2d8bF3389245Ddce8b2
* Adapter Address = 0x5D7bb19fC0856f5bc74b66f2c7b0258c1aeafD7f
* Chain Id = 690
* Flat Fee = 0.000002 ETH
* Minimum Confirmations = 1
* Maximum Confirmations = 200
* Maximum Gas Limit = 3000000
* Default Minimum Threshold = 3

### Taiko Mainnet

* Block Explorer = <https://taikoscan.io/>
* ARPA Address = 0xA3B4A1838293A42B87b47b3078AF6595676eB53D
* ControllerOracle Address = 0x3eA3e8EC047A3D7aDBc3070DEd26f3d7d8BEF692
* Adapter Address = 0x4AE06EBEd7dCCc58769a49c189A5DCC6F79dDF1A
* Chain Id = 167000
* Flat Fee = 0.000002 ETH
* Minimum Confirmations = 1
* Maximum Confirmations = 200
* Maximum Gas Limit = 3000000
* Default Minimum Threshold = 3

## Test Environment

### Ethereum Hoodi Testnet:

* Block Explorer = <https://hoodi.etherscan.io/>
* ARPA Address = 0x2dA62FaCbDc3Aa7Afdf32A55Ecd395F5C82111ad
* Staking Address = 0xEDE8ecce977f7962fB613c4BF772296E16167639
* NodeRegistry Address = 0x1bf8731f27E121B2E07d53ecAA75f99D68f3f6E8
* ServiceManager Address = 0xD3037aFc7a1f742e678bbCcEEBE8c2561C6EcE48
* Controller Address = 0x52071980a3a5A77611A301D5453a739aeAe0E482
* Adapter Address = 0x632551b24Da9F51FdAa59baef9fbeB6c000F377d
* Chain Id = 560048
* Flat Fee = 0
* Minimum Confirmations = 1
* Maximum Confirmations = 200
* Maximum Gas Limit = 3000000
* Default Minimum Threshold = 3

### Ethereum Sepolia Testnet:

* Block Explorer = <https://sepolia.etherscan.io/>
* ARPA Address = 0x066700ca8C13A3693a8E070abBEC2fb7C60a2b77
* Staking Address = 0x7f964db58decce1742B0b2f74F709612d6c5A8e4
* NodeRegistry Address = 0xbE430838250b5CA7808257864b40228F1ec1eb8b
* ServiceManager Address = 0x0dA5B8D91b085cF2bE4Abf1F58C2D3c841A710D4
* Controller Address = 0x813d10FeBAec0cb5471FDb6d1D06fCF09F1cb4d5
* Adapter Address = 0x46d29642cB0d57ae27b18b4aa5f746c918F3D6F8
* Chain Id = 11155111
* Flat Fee = 0
* Minimum Confirmations = 1
* Maximum Confirmations = 200
* Maximum Gas Limit = 3000000
* Default Minimum Threshold = 3

### OP Sepolia Testnet:

* Block Explorer = <https://sepolia-optimism.etherscan.io/>
* ARPA Address = 0x7cafBF5b24b6eaD87Fdfb94331b7c9756456E9bc
* ControllerOracle Address = 0x1529bC3148944e516C483a92f4681cb335bc85CB
* Adapter Address = 0x25Aed37669a783Bb5dE1D40279C7Fe5339C13F5D
* Chain Id = 11155420
* Flat Fee = 0
* Minimum Confirmations = 1
* Maximum Confirmations = 200
* Maximum Gas Limit = 3000000
* Default Minimum Threshold = 3

### Taiko Hoodi Testnet:

* Coming soon


# Randcast Playground

https\://www\.arpanetwork.io/play

### `SharedConsumer` contract

* **Ethereum Mainnet**
  * <https://etherscan.io/address/0x8acfc64bF976488E9B83c517D4185Fd4D8A9D683>
* **BSC Mainnet**
  * <https://bscscan.com/address/0x9a599D28907780289bB980ddFA38A17B1176FC29>
* **Base Mainnet**
  * <https://basescan.org/address/0xC9519853F9E9576303dB70a054d320aCA82005Ad>
* **Optimism Mainnet**
  * <https://optimistic.etherscan.io/address/0x9B9b0ea8b7a565dB81D3C78129626f077D47f7B9>
* **Taiko Mainnet**
  * <https://taikoscan.io/address/0x9A6E06aa83eBF588c136dD7991d9002DF1E181CF>
* **Ethereum Hoodi Testnet**
  * [https://hoodi.etherscan.io/address/0x1D2c2d06e6d923B2B88B1CDDb0955d418fae4](https://hoodi.etherscan.io/address/0x1D2c2d06e6d923B2B88B1CDDb0955d418fae48a8)[8a8](https://hoodi.etherscan.io/address/0x1D2c2d06e6d923B2B88B1CDDb0955d418fae48a8)


# Archive of Fate

A Verifiable AI Life Simulation Game Powered by Randcast

### Introduction

Archive of Fate (命运档案馆) is an AI-driven text life-simulation game on BNB Smart Chain. Each playthrough is a complete, randomly-generated life: players are born into one of three worlds, draw talents, allocate starting attributes, then watch a deterministic rules engine carry their character through decades of procedurally-selected events, occasional "mythic" turning points, and a final AI-written life summary and fate grade. Players who like the outcome can permanently inscribe that life as an ERC-721 NFT and publish it to a public Fate Archive, where anyone can inspect the record and verify onchain that it wasn't faked.

### Gameplay Demo

{% embed url="<https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FDAMqVaf96VXudHYNn7sZ%2Fuploads%2FMJ7Ai1cUexIL9O1cYe3J%2Fgameplay%20demo.mp4?alt=media&token=420e897b-6ab8-4efc-8540-5e12212b4792>" %}

### How to Play

Whether or not you connect a wallet, a full life takes just a few minutes:

1. **Choose how to play**: Connect a wallet to unlock persistent cross-run progression and on-chain inscription, or tap **Play as Guest** for an instant, no-signup start (guests get up to 10 free lives).
2. **Get your random seed:** Wallet players sign one transaction (a small BNB fee) to request a verifiable seed from Randcast; guest players are issued a seed automatically, sponsored by the game's backend, at zero cost.
3. **Draw and pick 3 talents:** chosen from a candidate pool that grows the more a wallet player has played.
4. **Allocate starting attributes**: Charm, Intellect, Strength, Wealth, and Spirit, spent from a point budget that also widens with wallet progression.
5. **Watch the life play out**: the deterministic rules engine advances year by year, firing random events and occasional rare "mythic" moments, with every outcome determined entirely by the seed and your choices.
6. **Get your fate summary**: peak attributes, an overall score, a fate grade (D through SSS), and a one-line AI-written life summary.
7. **(Optional) Inscribe your fate**: connected-wallet players can mint the life as an ERC-721 NFT (50 ARPA + gas), publishing it to the public Fate Archive board.
8. **(Optional) Share or verify:** export a shareable PNG fate card, or browse the Fate Archive to inspect any inscribed life and verify it on-chain against the smart contract in one click.

### Why On-Chain Randomness Matters Here

Every meaningful outcome in the game, which world you're born into, which events fire each year, whether a rare "mythic" moment triggers, how the life ultimately scores, is derived deterministically from a single seed. That design choice makes randomness the single most important trust primitive in the whole product: if the seed can be predicted or manipulated by the player, the backend, or anyone else, the entire "fate" premise collapses, and the on-chain inscriptions become meaningless. Archive of Fate needed a seed that is:

* Unpredictable before the player commits to it
* Unbiased, not influenced by the requester, a miner, or the game's own backend
* Verifiable after the fact, so a public "verify this life" feature can hold up

Randcast was a direct fit for all three requirements without the team having to build or operate any randomness infrastructure themselves.

### How Archive of Fate Integrates Randcast

The game's `FateRandomnessConsumer` contract extends Randcast's `GeneralRandcastConsumerBase`, and the integration supports two distinct player paths:

**Wallet players pay for their own randomness.** When a connected-wallet player starts a new life, the frontend calls `requestSeed()` directly on the consumer contract, attaching a small amount of BNB to cover the Randcast fee. This produces a `requestId`, and once the ARPA Network fulfills it, the game's backend reads the result and starts the life using that verified seed.

**Guest players are sponsored.** To keep the barrier to entry at zero, anonymous/guest sessions (capped at 10 free plays) are requested by the backend's `operator` wallet via `requestSeedFor(gameSessionId)`, funded out of a project-owned Randcast subscription rather than the player's own funds. This lets the game offer a genuinely free, wallet-less trial experience while still guaranteeing every single life, guest or not, is seeded by real, verifiable on-chain randomness rather than a client-side or server-side PRNG.

**Seed derivation.** On fulfillment, the raw randomness is folded down into a compact 6-digit game seed (`seed = randomness % 900000 + 100000`), which is what actually drives the deterministic life-simulation engine (a seeded `mulberry32` generator). The mapping is simple and public, so anyone can independently confirm that a displayed life's seed derives correctly from the on-chain randomness value.

**Verification before the life even starts.** Before the backend will simulate a wallet player's life, it independently re-reads `getSeed(requestId)` and `requesters(requestId)` from the consumer contract and checks that the on-chain seed and requester match what the client submitted, the backend never trusts a client-supplied seed at face value. Guest sessions are read the same way via polling. This closes off the most obvious attack: a player claiming a different (more favorable) seed than the one Randcast actually fulfilled.

**Carried through to permanent inscription.** If a player chooses to mint their life as an NFT, the on-chain fate record stores the seed together with the original Randcast request transaction hash, `talentIds`, and starting attribute allocation. Combined with the deterministic simulation engine, this means anyone, not just the game's own backend, can take a published inscription, re-run the same inputs through the (open) rules engine, and confirm the result matches exactly what's displayed. Randcast's verifiable randomness is what makes that whole audit chain possible in the first place: the game's own EIP-712 anti-forgery signing and receipt verification stop fabricated inscriptions from being minted, but it's the randomness underneath that stops the outcome itself from having been rigged.

### Why This Matters for Web3 Games

Archive of Fate is a good illustration of a broader pattern: any game where the entire value proposition is "this outcome is fair and this record is real" needs randomness that can survive public scrutiny, not just internal trust. By offloading seed generation to Randcast's BLS threshold-signature network, and by sponsoring guest randomness through a simple subscription rather than building custom infrastructure, the team was able to ship a fully verifiable, dual-mode (self-paid + sponsored) randomness flow with a comparatively small amount of integration code, while keeping the door open for any player, at any time, to independently check that their fate (or anyone else's) was never tampered with.

### Links

To play the game: <https://aof.arpanetwork.io/>

Source code: <https://github.com/ARPA-Network/Archive-Of-Fate>


# GitHub Repositories

* [BLS-TSS-Network](https://github.com/ARPA-Network/BLS-TSS-Network)
* [Staking-v0.1](https://github.com/ARPA-Network/Staking-v0.1)
* [Arpa Node](https://github.com/ARPA-Network/BLS-TSS-Network/tree/main/crates/arpa-node)
* [Arpa User CLI](https://github.com/ARPA-Network/BLS-TSS-Network/tree/main/crates/user-cli)
* [Randcast Solidity Contracts](https://github.com/ARPA-Network/BLS-TSS-Network/tree/main/contracts)
* [Randcast-User-Contract](https://github.com/ARPA-Network/Randcast-User-Contract)
* [Archive of Fate](https://github.com/ARPA-Network/Archive-Of-Fate/)


