Get a Random Number from SKALE RNG
SKALE provides native random number generation (RNG) through a precompiled contract. This guide will show you how to get random numbers in your smart contracts on SKALE.
Prerequisites
- Basic understanding of Solidity
- A SKALE Chain endpoint
- Understanding of smart contract development
Overview
SKALE’s RNG is:
- Native: Built into every SKALE Chain
- Free: No gas costs (zero gas fees on SKALE)
- Instant: Available immediately, no waiting
- Secure: Based on BLS threshold signatures from 11+ nodes
Method 1: Direct Precompiled Contract Call
Use inline assembly to call the precompiled contract at address 0x18:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract GetRandomNumber {
function getRandom() public view returns (bytes32) {
bytes32 randomValue;
assembly {
let freemem := mload(0x40)
let start_addr := add(freemem, 0)
if iszero(staticcall(gas(), 0x18, 0, 0, start_addr, 32)) {
invalid()
}
randomValue := mload(freemem)
}
return randomValue;
}
function getRandomUint256() public view returns (uint256) {
return uint256(getRandom());
}
}
Method 2: Using SKALE RNG Library
The easiest way is to use the SKALE RNG library:
Step 1: Install the Library
npm install @dirtroad/skale-rng
Step 2: Import and Use
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@dirtroad/skale-rng/contracts/RNG.sol";
contract MyRandomContract is RNG {
function generateRandom() external view returns (uint256) {
return getRandomNumber();
}
function generateRandomInRange(uint256 min, uint256 max) external view returns (uint256) {
return getNextRandomRange(min, max);
}
}
Method 3: Helper Contract
Create a reusable helper contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract RandomHelper {
function getRandomBytes32() public view returns (bytes32) {
bytes32 randomValue;
assembly {
let freemem := mload(0x40)
let start_addr := add(freemem, 0)
if iszero(staticcall(gas(), 0x18, 0, 0, start_addr, 32)) {
invalid()
}
randomValue := mload(freemem)
}
return randomValue;
}
function getRandomUint256() public view returns (uint256) {
return uint256(getRandomBytes32());
}
function getRandomInRange(uint256 min, uint256 max) public view returns (uint256) {
require(max > min, "Invalid range");
uint256 random = getRandomUint256();
return min + (random % (max - min + 1));
}
}
Usage Examples
Example 1: Simple Random Number
contract SimpleRandom {
RandomHelper public randomHelper;
constructor(address _randomHelper) {
randomHelper = RandomHelper(_randomHelper);
}
function getRandomValue() external view returns (uint256) {
return randomHelper.getRandomUint256();
}
}
Example 2: Random Selection
contract RandomSelector {
function selectRandom(uint256[] memory options) public view returns (uint256) {
uint256 random = getRandomUint256();
uint256 index = random % options.length;
return options[index];
}
function getRandomUint256() private view returns (uint256) {
bytes32 randomBytes;
assembly {
let freemem := mload(0x40)
if iszero(staticcall(gas(), 0x18, 0, 0, freemem, 32)) {
invalid()
}
randomBytes := mload(freemem)
}
return uint256(randomBytes);
}
}
Example 3: Weighted Random Selection
contract WeightedRandom {
function weightedSelect(uint256[] memory weights) public view returns (uint256) {
uint256 totalWeight = 0;
for (uint256 i = 0; i < weights.length; i++) {
totalWeight += weights[i];
}
uint256 random = getRandomInRange(1, totalWeight);
uint256 cumulative = 0;
for (uint256 i = 0; i < weights.length; i++) {
cumulative += weights[i];
if (random <= cumulative) {
return i;
}
}
return weights.length - 1;
}
function getRandomInRange(uint256 min, uint256 max) private view returns (uint256) {
bytes32 randomBytes;
assembly {
let freemem := mload(0x40)
if iszero(staticcall(gas(), 0x18, 0, 0, freemem, 32)) {
invalid()
}
randomBytes := mload(freemem)
}
uint256 random = uint256(randomBytes);
return min + (random % (max - min + 1));
}
}
Important Notes
RNG is native to SKALE and relies on SKALE’s Consensus. In local testing or on other chains, this will always return 0.
Random numbers are generated per block. Multiple calls in the same block will return the same value.
Best Practices
- Use Library: Prefer the SKALE RNG library for easier integration
- Range Functions: Use range functions for bounded randomness
- Block Awareness: Remember randomness is per-block
- Testing: Test on SKALE testnet, not locally
- Documentation: Document your randomness requirements
Security Considerations
- Randomness is cryptographically secure
- No single node can influence the result
- Based on threshold signatures from 11+ nodes
- Suitable for gaming, lotteries, and other applications
Resources