I'm developing in remix a lottery SC. I have a problem when I run buyTicket() function. On remix I fix it giving approve to SC address from erc20 function, but I don't know how to do it when I run my test. My SC:
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./Token.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
library Random {
/*
* @dev startingValue is inclusive, endingValue is inclusive
* naive implementation! Do not use in production
* ie if 1, 10, rand int can include 1-10
*/
function naiveRandInt(uint256 _startingValue, uint256 _endingValue)
internal
view
returns (uint256)
{
// hash of the given block when blocknumber is one of the 256 most recent blocks; otherwise returns zero
// create random value from block number; use previous block number just to make sure we aren't on 0
uint randomInt = uint(blockhash(block.number - 1));
// convert this into a number within range
uint range = _endingValue - _startingValue + 1; // add 1 to ensure it is inclusive within endingValue
randomInt = randomInt % range; // modulus ensures value is within range
randomInt += _startingValue; // now shift by startingValue to ensure it is >= startingValue
return randomInt;
}
}
contract Lottery {
IERC20 public erc20;
uint256 public startTimestamp;
uint256 public endTimestamp;
uint256 public ticketCost;
uint256 public ticketCounter;
uint256 public winnerTicket;
uint256 private prize;
bool internal winnerWasChosen;
address public _winner;
mapping(address => uint256) s_players;
mapping(uint256 => address) s_ticket;
mapping(address => uint256) public _balances;
constructor(uint256 _startTimestamp, uint256 _endTimestamp, uint256 _ticketCost, IERC20 _token) {
startTimestamp = _startTimestamp;
endTimestamp = _endTimestamp;
ticketCost = _ticketCost;
erc20 = IERC20(_token);
}
function buyTicket() public returns (uint256 ticketId) {
require(erc20.balanceOf(msg.sender) > ticketCost, "not enough balance");
require(block.timestamp > startTimestamp, "Lottery is not started yet");
require(endTimestamp > block.timestamp, "Lottery is over");
require(s_players[msg.sender] == 0, "Already got one ticket");
erc20.approve(address(this), 100);
erc20.transferFrom(msg.sender, address(this), ticketCost);
ticketCounter++;
s_players[msg.sender] = ticketCounter;
s_ticket[ticketCounter] = msg.sender;
return ticketCounter;
}
function chooseWinner() public returns (address winner ) {
require(ticketCounter >= 1, "no tickets are sold");
require(block.timestamp > endTimestamp, "Lottery isn't over");
require(winnerWasChosen == false, "Function was already called");
winnerTicket = Random.naiveRandInt(1, ticketCounter);
_winner = s_ticket[winnerTicket];
prize = ticketCost * ticketCounter;
erc20.transfer(_winner, prize);
return (_winner);
}
function getStartLottery() public view returns (bool) {
return (block.timestamp >= startTimestamp);
}
function getEndLottery() public view returns (bool) {
return (block.timestamp >= endTimestamp);
}
function getEntranceFee() public view returns (uint) {
return ticketCost;
}
function getTimestamp() public view returns (uint256) {
return block.timestamp;
}
function balanceOf(address _addr) public view returns (uint256) {
return _balances[_addr];
}
function getPrize() public view returns (uint256){
return ticketCost * ticketCounter;
}
}
My unit test script with Truffle:
const { expect, assert } = require('chai');
const Lottery = artifacts.require('Lottery');
const Token = artifacts.require('Token');
contract('Lottery', (accounts) => {
let test, startTimestamp, endTimestamp;
beforeEach(async () => {
token = await Token.deployed();
lottery = await Lottery.deployed();
it("records player when they buy ticket", async () => {
await lottery.buyTicket()
const contractPlayer = await lottery.getPlayer(0)
assert.equal(user.address, contractPlayer)
})
it("SC is not empty", async () =>{
await lottery.buyTicket()
assert(lottery.address.valueOf() != 0)
})
})
lottery.buyTicket() function returns me "insufficient allowance"