📝In-depth smart contract

A detailed description about the smart contract

Smart contract Deployement

The smart contract will be deployed juste one time on the Polygon Network. After that, the lottery starts and renews automatically with each winner draw

Imports

import "./ERC721A.sol";
import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";

This is all the contracts that we are gonna need:

ERC721A is popular for reduce gas price then you mint multiple items

Chainlink contracts are tools to generate a random number in a decentralized way via a chainlink oracle.

All the Constants

     ///parameters allowing the counting of nft already minted and still available
     uint public supplyLottery = 2;
     uint public alreadySupply = 0;
    
    ///Max number of NFT that 1 wallet is allow to mint
    uint public max_mint_allowed = 50;

    ///The price of 1 NFT in $MATIC
    uint public priceSale = 0.01 ether;

    ///We "define" the baseURI
    string public baseURI;
   
    ///we "set" the extension to .json
    string public baseExtension = ".json";

    ///Boolean to set if lottery is open/closed
    bool public lotteryClosed;

Constructor

    constructor(uint64 subscriptionId, string memory _theBaseURI, address _team) VRFConsumerBaseV2(vrfCoordinator) ERC721A("DefiLottery", "DFL") {
        team = payable(_team);
        COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator);
        s_owner = 0xF15130b822eE961Aa42C6a48c5966F9Eb229e865;
        s_subscriptionId = subscriptionId;
        baseURI = _theBaseURI;
    }

This is the constructor of the contract, with ERC721A and Chainlink. The constructor has some parameters:

-string memory _theBaseURI --> To link the IPFS to the collection.

ERC721A: -DeFiLottery --> The name of the token (NFT). -DFL --> The symbol of the tokens

Line 1: team = payable(_team);--> Address for the team payment

Line 2: COORDINATOR = VRFCoordinatorV2Interface(vrfCoordinator); --> Address of the Chainlik vrfCoordinator

Line 3: s_owner; --> A function to transfer the Ownership,from the Owner to the new Owner,if somebody is going wrong with our smart contract deployer...

Line 4: s_subscriptionId = subscriptionId; --> The Id of subscription on Chainlink services

Line 5: baseURI = theBaseURI; --> To redefines the name of theBaseURI to baseURI (wich is use to link the IPFS to the collection)

Functions

In the smart contract,we are using some functions to make different actions...

///To change the maxMint allowed
function changeMaxMintAllowed(uint _maxMintAllowed) external onlyOwner {
    max_mint_allowed = _maxMintAllowed;
}
///To change the price sale (if the market is too volatile)
function changePriceSale(uint _priceSale) external onlyOwner {
    priceSale = _priceSale;
}
///To change the BaseURI
function setBaseUri(string memory _newBaseURI) external onlyOwner {
    baseURI = _newBaseURI;
}
///Return the BaseURI
function _baseURI() internal view virtual override returns (string memory) {
    return baseURI;
}
///To set the base extension
function setBaseExtension(string memory _baseExtension) external onlyOwner {
    baseExtension = _baseExtension;
}
///Return the token URI from the token ID
function tokenURI(uint _tokenId) public view virtual override returns (string memory) {
    require(_exists(_tokenId), "URI query for nonexistent token");

    return string(abi.encodePacked(baseURI, _tokenId.toString(), ".json"));
}
///Return the balance of the contract
function getBalance() public view returns(uint) {
    return address(this).balance;
    }
///Define the number of available tickets for the lottery    
function changeSupplyLottery(uint _supplyLottery) external onlyOwner {
        supplyLottery = _supplyLottery;
    } 
///For update the number of tickets already sell
function changeAlreadySupply(uint _alreadySupply) external onlyOwner {
        alreadySupply = _alreadySupply;
    }
///Return the winner address for selected lottery
function getWinner(uint _idLottery) external view returns(address) {
        return winners[_idLottery];
    }

The Mint

function enter(address _account, uint256 _ammount) external payable nonReentrant {

require(!lotteryClosed, "Lottery is closed");

The lottery must be open for initiate the mint

uint price = priceSale; require(msg.value >= price * _ammount, "Not enought funds.");

The funds in your wallet must be superior than the price of 1 ou multiples tickets

require(_ammount <= max_mint_allowed, "You can't mint more than 50 tickets");

The quantity of tickets you would mint must inferior than the limit. Limit is defined by max_mint_allowed

require(totalSupply() + _ammount <= supplyLottery + alreadySupply, "Number of remaining tickets is insufficient");

the number of tickets youls purchase must be inferior than the remaining tickets of the current lottery

players.push(payable(msg.sender));

If you mint, your address is added to the table of players

if(totalSupply() + _ammount == supplyLottery + alreadySupply) { lotteryClosed = true; }

When the last ticket is minted, the lottery will be closed

_safeMint(_account, _ammount); }

Transfer the quantity of purchase's tickets in your wallet

The Draw

function requestRandomWords() external onlyOwner {
        require(lotteryClosed, "Lottery is not closed");
        s_requestId = COORDINATOR.requestRandomWords(
        keyHash,
        s_subscriptionId,
        requestConfirmations,
        callbackGasLimit,
        numWords
        );
    }

This function allows to draw a random number via chainlink oracle. Without this, it's impossible to define the winner, pay the winner and pay the team

require(lotteryClosed, "Lottery is not closed");

The lottery must be closed for initiate this function

uint winner = s_randomWords[0] % players.length;

the winner address is picked using the random number and the total of addresses register in the table of players

players[winner].transfer(address(this).balance / 100 * 80);

Automatically, the winner address receive 80% of contract's balance

team.transfer(address(this).balance);

After the first transfer, the team receive the remaing balance

winners[idLottery] = players[winner];

The winner address is indexed on the table of winner

idLottery++;

the lottery moves to a higher rank

players = new address payable[](0);

the table of players is reset

alreadySupply = totalSupply();

The number of tickets sold during the current lottery change for the number of tickets sold during all the lotteries

lotteryClosed = false;

The new lottery is now open

s_randomWords = new uint256[](0);

The table of random number is reset. By this way a new random number must be drawn for each lottery

If you still have questions about the smart contract,feel free to send us a message on our discord.

Last updated