Use this page when you want the fastest path to a working consumer contract on Abstract.
Choose the path that matches your starting point:
New project: scaffold a Foundry starter that already includes an example consumer contract.
Existing project: wire the hosted roll.codes coordinator into your current Solidity app.
New projects
The starter CLI generates a Foundry project with:
src/DiceDuelConsumer.sol
test/DiceDuelConsumer.t.sol
script/DeployDiceDuelConsumer.s.sol
remappings and Foundry config for Abstract
The generated consumer already requests randomness with requestRandomNumberWithTraceId(...) and receives it in randomNumberCallback(...).
Build the starter CLI
The CLI source currently lives in this repository. git clone https://github.com/jarrodwatts/roll.codes.git
cd roll.codes/packages/create-roll-codes
npm ci
npm run build
Scaffold a starter project
Run the generator from the CLI package directory: node dist/index.js contract my-game
This creates ./my-game with the example consumer already wired to the public IVRFSystem interface.
Build and test the generated project
cd my-game
forge build --zksync
forge test --zksync -vvv
Configure the hosted coordinator and deploy
Abstract testnet
Abstract mainnet
cast wallet import deployer --interactive
export ROLL_OWNER = $( cast wallet address --account deployer )
export ABSTRACT_RPC_URL = https :// api . testnet . abs . xyz
export ROLL_VRF_SYSTEM = 0x5ab0e04147A2A1BCa1D06da3c1cB48ea04294B5E
forge script script/DeployDiceDuelConsumer.s.sol:DeployDiceDuelConsumer \
--account deployer \
--rpc-url " $ABSTRACT_RPC_URL " \
--zksync \
--broadcast
Mainnet deployment is not live yet. cast wallet import deployer --interactive
export ROLL_OWNER = $( cast wallet address --account deployer )
export ABSTRACT_RPC_URL =< your_abstract_mainnet_rpc_url >
export ROLL_VRF_SYSTEM =< mainnet_coordinator_address >
forge script script/DeployDiceDuelConsumer.s.sol:DeployDiceDuelConsumer \
--account deployer \
--rpc-url " $ABSTRACT_RPC_URL " \
--zksync \
--broadcast
See Deployed addresses for the current network-specific coordinator status and addresses.
If you want the full CLI surface, including --dir, --force, and --skip-install, see CLI reference .
Integrating into an existing project
Use this path if you already have a Foundry project and only need to add roll.codes support.
Install the dependency
forge install roll.codes=jarrodwatts/roll.codes@main --no-git
forge install forge-std=foundry-rs/forge-std --no-git
Add remappings
Your project should include: @rollcodes/=lib/roll.codes/src/
forge-std/=lib/forge-std/src/
Request a random number
Import the public interfaces, store the hosted coordinator address, then request randomness by paying requestFee(). // SPDX-License-Identifier: MIT
pragma solidity ^0.8.24 ;
import { IVRFSystem } from "@rollcodes/interfaces/IVRFSystem.sol" ;
import { IVRFSystemCallback } from "@rollcodes/interfaces/IVRFSystemCallback.sol" ;
contract ExampleConsumer is IVRFSystemCallback {
IVRFSystem public immutable vrfSystem;
constructor ( address coordinatorAddress ) {
vrfSystem = IVRFSystem (coordinatorAddress);
}
function play () external payable returns ( uint256 requestId ) {
uint256 fee = vrfSystem. requestFee ();
require ( msg .value == fee, "fee mismatch" );
uint256 traceId = 0 ;
requestId = vrfSystem.requestRandomNumberWithTraceId{ value : fee }(traceId);
}
function randomNumberCallback ( uint256 requestId , uint256 randomNumber ) external override {}
}
Handle the callback safely
Every request is delivered asynchronously. Restrict the callback to the hosted coordinator and settle against stored request state. function randomNumberCallback ( uint256 requestId , uint256 randomNumber ) external {
if ( msg.sender != address (vrfSystem)) revert Unauthorized ();
Pending storage request = pendingByRequestId[requestId];
if (request.player == address ( 0 )) revert RequestNotFound (requestId);
if (request.settled) revert AlreadySettled (requestId);
request.settled = true ;
request.randomNumber = randomNumber;
// settle your game or app logic here
}
Deploy only your consumer
Set the hosted coordinator address for the same network as your RPC URL: Abstract testnet
Abstract mainnet
cast wallet import deployer --interactive
export ABSTRACT_RPC_URL = https :// api . testnet . abs . xyz
export ROLL_COORDINATOR = 0x5ab0e04147A2A1BCa1D06da3c1cB48ea04294B5E
Mainnet deployment is not live yet. cast wallet import deployer --interactive
export ABSTRACT_RPC_URL =< your_abstract_mainnet_rpc_url >
export ROLL_COORDINATOR =< mainnet_coordinator_address >
Then deploy your own consumer contract with your normal Foundry script flow, using --account deployer when you broadcast.
requestRandomNumberWithTraceId is payable on roll.codes, so callers must send the exact value returned by requestFee().
Prefer a Foundry keystore account over exporting plaintext private keys. Import once with cast wallet import and reuse that account name with forge script --account ....
Requests delivered from the same drand round receive the same base randomNumber. If your app needs a unique value per request within that same round, derive one locally from requestId and randomNumber.
Verify the result
You should end with:
a deployed consumer contract
a configured roll.codes coordinator address in your deployment inputs
a request path that pays requestFee()
a callback that only accepts the hosted coordinator
Next steps
Integration pattern Copy a fuller consumer example with request storage and settlement guards.
Coordinator reference Keep the request function, fee call, and callback interface beside you while implementing.
Troubleshooting Fix fee mismatches, unauthorized callbacks, and pending requests.
CLI reference Review the starter generator flags and output files.