Generating a random number in a smart contract is very useful, especially for games.
Can we do this in Solidity? Yes!
Here is the code:
pragma solidity ^0.6.0;
contract MyContract {
function _randModulus(uint mod) internal returns(uint) {
return uint(keccak256(abi.encodePacked(
now,
block.difficulty,
msg.sender)
)) % mod;
nonce++;
return rand;
}
}
Let’s unpack everything!
First, we need a source of randomness, so we use
now, block.difficulty, msg.sender
Then we compute a hash of this – will be a bytes32 string like 0x89irwIRWOoo...
:
keccak256(abi.encodePacked(
now,
block.difficulty,
msg.sender
));
Then we cast this into an integer – this could be any (positive) integer, very small or very big:
uint(keccak256(abi.encodePacked(
now,
block.difficulty,
msg.sender
)));
The final step is to use the modulo (%
) operator to force this integer in a range:
uint(keccak256(abi.encodePacked(
now,
block.difficulty,
msg.sender
))) % mod;
However, we have a problem…miners can manipulate now
… Our random number is not safe to use…
We can fix this by importing a source of randomness outside of the control of the miner, by using an oracle (presumably controlled by someone we trust):
pragma solidity ^0.6.0;
contract Oracle {
address admin;
uint public rand;
constructor() public {
admin = msg.sender;
}
function feedRandomness(uint _rand) external {
require(msg.sender == admin);
rand = _rand;
}
}
contract MyContract {
Oracle oracle;
uint nonce; //not strictly necessary
constructor(address oracleAddress) public {
oracle = Oracle(oracleAddress);
}
function foo() external {
uint rand = _randModulus(10);
//use rand however you want
}
function _randModulus(uint mod) internal returns(uint) {
uint rand = uint(keccak256(abi.encodePacked(
nonce,
oracle.rand(),
now,
block.difficulty,
msg.sender)
)) % mod;
nonce++;
return rand;
}
}
Solidity
Leave a Reply