Documentation Randomizer Quick Start

Funding your contract

  1. Open the Dashboard, connect with Metamask to the desired network and wallet.
  2. Under Fund Contract, paste your contract address.
  3. Enter an amount of ETH you'd like to fund the contract with and click Deposit.

Make sure your contract remains funded so it can make request calls. You can also have contract end-users pay for the fee by implementing estimateFee. To withdraw funds from Randomizer, implement the clientWithdrawTo function.

Making a request

NOTE: zkSync v2 Testnet version is coming back soon.

To make a request, make sure your contract is funded with ETH and then implement the function randomizer.request(uint256 callbackGasLimit) in an external function within your smart contract. This function will return a unique uint256 ID that your contract can store and retrieve once it receives the callback.

The Randomizer protocol will then call your randomizerCallback(uint256 id, bytes32 value) function with the request id and random value.

Contract addresses

Implement any address below to make calls to Randomizer.

  • Arbitrum One: 0x5b8bB80f2d72D0C85caB8fB169e8170A05C94bAF
  • Arbitrum Goerli: 0x923096Da90a3b60eb7E12723fA2E1547BA9236Bc

Contract Example

// Randomizer protocol interface
	interface IRandomizer {
		function request(uint256 callbackGasLimit) external returns (uint256);
		function request(uint256 callbackGasLimit, uint256 confirmations) external returns (uint256);
		function clientWithdrawTo(address _to, uint256 _amount) external;
	}
	
	// Coinflip contract
	contract CoinFlip {
		// Arbitrum goerli
		IRandomizer public randomizer = IRandomizer(0x923096Da90a3b60eb7E12723fA2E1547BA9236Bc);
		address public owner;
		
		// Stores each game to the player
		mapping(uint256 => address) public flipToAddress;
		
		// Events
		event Win(address winner);
		event Lose(address loser);
		
		modifier onlyOwner() {
			require(msg.sender == owner, "Sender is not owner");
			_;
		}
		
		// The coin flip containing the random request
		function flip() external returns (uint256) {
			// Get the latest randomizer contract from the testnet proxy
			// Request a random number from the randomizer contract (50k callback limit)
			uint256 id = randomizer.request(50000);
			// You can also do randomizer.request(50000, 20) to get a callback after 20 confirmations for increased finality security (you can do 1-40 confirmations).
			// Store the flip ID and the player address
			flipToAddress[id] = msg.sender;
			// Return the flip ID
			return id;
		}
		
		// Callback function called by the randomizer contract when the random value is generated
		function randomizerCallback(uint256 _id, bytes32 _value) external {
			//Callback can only be called by randomizer
			require(msg.sender == address(randomizer), "Caller not Randomizer");
			// Get the player address from the flip ID
			address player = flipToAddress[_id];
			// Convert the random bytes to a number between 0 and 99
			uint256 random = uint256(_value) % 100;
			// If the random number is less than 50, the player wins
			if (random < 50) {
				emit Win(player);
			} else {
				emit Lose(player);
			}
		}
		
		// Allows the owner to withdraw their deposited randomizer funds
		function randomizerWithdraw(uint256 amount)
		external
		onlyOwner
		{
						randomizer.clientWithdrawTo(msg.sender, amount);
		}
	}
	

Withdrawing Funds

To withdraw funds, implement the function randomizer.clientWithdrawTo(address to, uint256 amount) into your smart contract. Make sure it's only callable by pre-approved accounts.

You can also require the fees to be covered by users by using one of the following functions in your smart contract:

  • randomizer.estimateFee(uint256 callbackGasLimit)
  • randomizer.estimateFee(uint256 callbackGasLimit, uint256 confirmations)

User Funding & Refunds

Having users pay for the VRF request and refunding any excess paid fees after is simple.

Refer to the CoinFlip.sol example which handles user funding and refunding of previously paid excess fees on every new game.

To calculate the ETH to attach to the transaction you can do the following (in Javascript):

const providerGasPrice = await provider.getGasPrice();
// Add some extra since the fee can increase in a next block
const feeEstimate = ethers.BigNumber.from(
	await randomizer.estimateFeeUsingGasPrice(100000, providerGasPrice)
).mul(135).div(100);
// Attach the fee to the transaction
const tx = await contract.play(playerInput, { value: feeEstimate });

You can use the estimateFee functions to get the contract user to pay for the gas fee before making the request, by making your request-making function "payable" and adding require(msg.value >= estimateFee).

You can also use these special views in your front-end for fee estimates, using a gas price given by a web3 provider, which you can use to calculate how much ETH users should attach to their transaction to pay for the request:

  • await randomizer.estimateFeeUsingGasPrice(uint256 callbackGasLimit, uint256 gasPrice)
  • await randomizer.estimateFeeUsingConfirmationsAndGasPrice(uint256 callbackGasLimit, uint256 confirmations, uint256 gasPrice)

Real-time Results

You can implement Randomizer's Real-Time npm package to give users a real-time experience. For example, you can display the results of a coinflip directly after a "flip" transaction, and update the user balance immediately in the front-end, all before the result is on-chain and your contract's callback function is called.

Front-end source code + a smart contract for an example coinflip game is available here.

How it works: Beacons send their VRF data to the sequencer before their transactions are verified on-chain. This lets the sequencer process the data off-chain and send the expected result to your front-end using websockets.

Expected Result Ensurance: In the case where a beacon sent VRF data to the sequencer but hasn't submitted the data on-chain (e.g. due to inability to pay for gas fees), the sequencer can submit the beacon's VRF data on their behalf after half the request's expiration period has passed. This helps to ensure that the expected real-time result matches the eventual on-chain result. This time window is enforced in the smart contract, so the Sequencer can never front-run any beacons. In the case neither the beacon or the sequencer were able to submit the request, the request will be renewed. In the case of a renewal, the initial value returned to your front-end by the sequencer will not be the same as the final result. Therefore if you integrate the Real-Time package, your frontend should always clarify that the real-time result is the "expected" result and you should update the front-end on a final on-chain result.

Roadmap

  1. ✅ Launch alpha versions Layer 2 testnets.
  2. ✅ Onboard community-operated beacons.
  3. ✅ Complete code audits.
  4. ✅ Deploy platform under a Business Service License, a.k.a. a time-delayed GPL-2.0-or-later license (BUSL-1.1).
  5. ✅ Launch platform to mainnet.
  6. Distribute original digital assets.

Beacons

Beacons are in the process of being operated by independent projects and ecosystem developers on mainnet.

Governance

The protocol will be governed in a decentralized manner, TBA.