Loading...
Hello, World!
Today, im going to explain how make a lottery smart contract, the winner will the user that choose the correct number, for default the owner is the winner. I divided the code in four parts:
I used the Chainlink Api to get the ether price.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract PriceConsumerV3 {
AggregatorV3Interface internal priceFeed;
constructor() {
//Rinkeby network
priceFeed = AggregatorV3Interface(0x8A753747A1Fa494EC906cE90E9f37563A8AF630e);
}
function getPriceRate() public view returns (uint) {
(, int price,,,) = priceFeed.latestRoundData();
uint adjust_price = uint(price) * 1e10;
uint usd = 50 * 1e18;
uint rate = (usd * 1e18) / adjust_price;
return rate;
}
}
More information here:
Actually this isn't randomness, the best way is using Chainlink Api: https://docs.chain.link/docs/get-a-random-number/ but to easier deploy code i choose this form:
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Randomness {
function number() internal view returns (uint) {
uint random = uint(
keccak256(
abi.encodePacked
(
block.timestamp * 9999,
msg.sender,
block.number + 1
)
)
);
return random % 100;
}
}
This code has just function called number; abi.encodePacked: Perfomes packed encoding of the given arguments, the arguments are: block timestamp, msg.sender and block number, to avoid data type problems i transformed result of encode with keccak256, this return the Keccak-256 hash, then i made modulo operator.
This is user section:
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./Price.sol";
contract usuario is PriceConsumerV3 {
uint public time;
address[] internal Users;
uint[] internal numeros;
mapping (address => uint) internal pagos;
mapping (uint => address) internal boleto;
function pay(uint _numero) public payable {
require(pagos[msg.sender] == 0, "One ticket each user.");
require(msg.value == getPriceRate(), "Verify pay.");
require(Users.length < 3, "Max users");
Users.push(msg.sender);
numeros.push(_numero);
pagos[msg.sender] += msg.value;
boleto[_numero] = msg.sender;
if (Users.length == 3) {
set_time();
}
}
function set_time() internal returns (uint) {
time = block.timestamp;
return time;
}
}
First i called price contract, set storage variables and i used two mappings: "pagos" follow an address with his pay, "boleto" track the number chosen by the user. Pay function recieve a number from user, first it verify all conditions then set arrays: Users (address) and numeros (number chosen by user). When Users.length == 3 or in other words, when three users bought his tickets, set_time() function saves block timestamp, i'll use it in main code.
Finally the main code:
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./User.sol";
import "./Random.sol";
contract Lottery is usuario, Randomness {
address internal genesis;
address public winner;
constructor() {
winner = msg.sender;
genesis = 0x0000000000000000000000000000000000000000;
}
function pago() public payable {
require(address(this).balance > 0);
comprobar();
for (uint i=0; i < Users.length; i++) {
address index_direccion = Users[i];
uint index_numerico = numeros[i];
pagos[index_direccion] = 0;
delete boleto[index_numerico];
delete time;
Users = new address[](0);
numeros = new uint[](0);
}
payable(winner).transfer(address(this).balance);
}
function comprobar() internal returns (address) {
require(Users.length == 3, "Dont enough users.");
require(block.timestamp >= time + 10 seconds, "Wait ten seconds");
uint random_number = number();
if (boleto[random_number] != genesis) {
winner = boleto[random_number];
}
return winner;
}
}
So, i imported secondary contracts, constructor set two address: one is winner (owner) and genesis to avoid problems when i will verify the winner. It has two functions: comprobar() and pago(); comprobar() needs that pass ten seconds after set_time() has been activated, when this is true, random number is called by the contract then verify if there is a winner, last function is pago that any user can be call it. I want to stop in this part for clarify :
for (uint i=0; i < Users.length; i++) {
address index_direccion = Users[i];
uint index_numerico = numeros[i];
pagos[index_direccion] = 0;
delete boleto[index_numerico];
delete time;
Users = new address[](0);
numeros = new uint[](0);
}
After verify one condition and call the other function, i am going to delete the whole data because how you know, these information is saved on blockchain, i created two index depending on the key mappings value then i init the arrays with zero value. To finish, im going to pay to winner address.
Sorry if i dont write clear, my native lenguage is spanish and i'm learning english. This code is just for practise and you dont have to use it in real life, if you have any question or i made some mistake, you can ask me in comments section or :