Best Practices
Limit gas usage on the callback
Keeping the callback function simple is crucial because the entropy providers limit gas usage. This ensures gas usage is predictable and consistent, avoiding potential issues with the callback.
For example, if you want to use entropy to generate a random number for each player in a round of game, you need to make sure that the callback function works for the maximum number of players that can be in each round. Otherwise, the callbacks will work for some rounds with fewer players, but will fail for rounds with more players.
Multiple solutions are possible to address this problem. You can store the random number received from the callback and either ask users to submit more transactions after the callback to continue the flow or run a background crank service to submit the necessary transactions.
The gas limit for each chain is listed on the contract addresses page.
Handling callback failures
While the default entropy provider is highly reliable, in rare cases a callback might not be received. This typically happens when there's an issue with your contract's callback implementation rather than with the provider itself. The most common causes are:
- The callback function is using more gas than the allowed limit
- The callback function contains logic that throws an error
If you're not receiving a callback, you can manually invoke it to identify the specific issue. This allows you to:
- See if the transaction fails and why
- Check the gas usage against the chain's callback gas limit
- Debug your callback implementation
For detailed instructions on how to manually invoke and debug callbacks, refer to the Debug Callback Failures guide.
Generating random values within a specific range
You can map the random number provided by Entropy into a smaller range using the solidity modulo operator (opens in a new tab).
Here is a simple example of how to map a random number provided by Entropy into a range between minRange
and maxRange
(inclusive).
// Maps a random number into a range between minRange and maxRange (inclusive)
function mapRandomNumber(
bytes32 randomNumber,
int256 minRange,
int256 maxRange
) internal returns (int256) {
uint256 range = uint256(maxRange - minRange + 1);
return minRange + int256(uint256(randomNumber) % range);
}
Notice that using the modulo operator can distort the distribution of random numbers if it's not a power of 2. This is
negligible for small and medium ranges, but it can be noticeable for large ranges.
For example, if you want to generate a random number between 1 and 52, the probability of having value 5 is approximately
10^-77
higher than the probability of having value 50 which is infinitesimal.
Generating multiple random values in a single transaction
If you need to generate multiple random values in a single transaction, you can hash the random input provided by Entropy with a unique identifier for each random number.
In the following example, mapRandomNumber
is used to generate 6 random attributes for a character.
function generateAttributes(bytes32 randomNumber) internal {
int256 strength = mapRandomNumber(
keccak256(abi.encodePacked(randomNumber, "strength")),
15,
20
);
int256 stamina = mapRandomNumber(
keccak256(abi.encodePacked(randomNumber, "stamina")),
10,
15
);
int256 agility = mapRandomNumber(
keccak256(abi.encodePacked(randomNumber, "agility")),
5,
15
);
int256 stealth = mapRandomNumber(
keccak256(abi.encodePacked(randomNumber, "stealth")),
0,
5
);
int256 positionX = mapRandomNumber(
keccak256(abi.encodePacked(randomNumber, "positionX")),
-100,
100
);
int256 positionY = mapRandomNumber(
keccak256(abi.encodePacked(randomNumber, "positionY")),
-100,
100
);
}