0

I'm trying to figure this out for the past days but without luck.

I have a function called BuyTickets which by default was using only native tokens. I'm trying to change it to accept any other token.

function BuyTickets(uint256 BuyTicketsNow) public returns(bool) {

    require(
        msg.value % ticketPrice == 0,
        string.concat(
            "the value must be multiple of ",
            Strings.toString(ticketPrice),
            " Ether"
        )
    );

    uint256 numOfTicketsToBuy =  msg.value / ticketPrice;

    BuyTicketsNow = numOfTicketsToBuy;

    require(BuyTicketsNow >= GetAllowance(), "Please approve tokens before transferring");

    require(BuyTicketsNow <= RemainingTickets(),"Not enough tickets available.");

    token.transferFrom(msg.sender, address(this), BuyTicketsNow);

    for (uint256 i = 0; i < numOfTicketsToBuy; i++) {
        tickets.push(msg.sender);
    }
    return true;
 }

I know that msg.value only applies to native tokens but i can't figured out how to remove it from the function without making it stop working.

Any help would be great, thanks


I think it's better if i put the all contract code so anyone who could help me can see if easily.

The way it works now it only allows me to buy tickets using the native tokens. If i select the chain id for bsc i can only buy in BNB, for ETh i can only buy in ETH.

I want to change it so i can use my own token to buy tickets. I think the problem is on the BuyTickets and WithDraw functions but i'm really new to solidity, still learning and some help would be nice.

// SPDX-License-Identifier: GPL-3.0

import '@openzeppelin/contracts/utils/Strings.sol';
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';

pragma solidity >=0.7.0 <0.9.0;

contract Lottery {


uint256 public constant ticketPrice = 0.001 ether; // Ticket lottery
uint256 public constant maxTickets = 1000; // maximum tickets per lottery
uint256 private ticketProject = 15; // commission for project
uint256 public constant duration = 1440 minutes; // The duration set for the lottery
address private project_tax = 0x123; // address of the project
uint256 public expiration; // Timeout in case that the lottery was not carried out.
address public lotteryOperator; // the creator of the lottery
uint256 public operatorTotalProject = 0; // the total commission balance
address public lastWinner; // the last winner of the lottery
uint256 public lastWinnerAmount; // the last winner amount of the lottery
uint256 public lotteryId; // get lotteryID

mapping(address => uint256) public winnings; // maps the winners to there winnings
address[] public tickets; //array of purchased Tickets

using SafeERC20 for IERC20;

// modifier to check if caller is the lottery operator
modifier isOperator() {
    require(
        (msg.sender == lotteryOperator),
        "Caller is not the lottery operator"
    );
    _;
}

// modifier to check if caller is a winner
modifier isWinner() {
    require(IsWinner(), "Caller is not a winner");
    _;
}

constructor() {
    IERC20 token = IERC20(address(0x31Cc043954bcaAaC2af1A3E6eC3caF4d45215cFA));
    lotteryOperator = msg.sender;
    expiration = block.timestamp + duration;
    lotteryId = 1;
}

// return all the tickets
function getTickets() public view returns (address[] memory) {
    return tickets;
}
// return wallet balance
function getBalance() public view returns (uint) {
    return address(this).balance;
}
function getWinningsForAddress(address addr) public view returns (uint256) {
    return winnings[addr];
}
function getLotteryId() public view returns(uint256) {
    return lotteryId;
}
function BuyTickets() public payable {
    require(
        msg.value % ticketPrice == 0,
        string.concat(
            "the value must be multiple of ",
            Strings.toString(ticketPrice),
            " Ether"
        )
    );
    uint256 numOfTicketsToBuy = msg.value / ticketPrice;

    require(
        numOfTicketsToBuy <= RemainingTickets(),
        "Not enough tickets available."
    );

    for (uint256 i = 0; i < numOfTicketsToBuy; i++) {
        tickets.push(msg.sender);
    }
 }
 function DrawWinnerTicket() public isOperator {

    require(tickets.length > 0, "No tickets were purchased");
    bytes32 blockHash = blockhash(block.number - tickets.length);
    uint256 randomNumber = uint256(
        keccak256(abi.encodePacked(block.timestamp, blockHash))
    );
    uint256 winningTicket = randomNumber % tickets.length;
    address winner = tickets[winningTicket];
    lastWinner = winner;
    winnings[winner] += (tickets.length * ticketPrice) / 2;
    lastWinnerAmount = winnings[winner];
    operatorTotalProject += (tickets.length * ticketPrice) * ticketProject / 100;
    delete tickets;
    expiration = block.timestamp + duration;
    lotteryId++;
}
function restartDraw() public isOperator {
    require(tickets.length == 0, "Cannot Restart Draw as Draw is in play");
    delete tickets;
    expiration = block.timestamp + duration;
}
function checkWinningsAmount() public view returns (uint256) {
    address payable winner = payable(msg.sender);
    uint256 reward2Transfer = winnings[winner];
    return reward2Transfer;
}
function WithdrawWinnings() public isWinner {        
    address payable winner = payable(msg.sender);
    uint256 reward2Transfer = winnings[winner];
    winnings[winner] = 0;
    winner.transfer(reward2Transfer);  
}
function WithdrawProject() public isOperator {
    address payable operator = payable(project_tax);
    uint256 project2Transfer = operatorTotalProject;
    operatorTotalProject = 0;
    operator.transfer(project2Transfer);
}
function RefundAll() public {
    require(block.timestamp >= expiration, "The lottery has not expired yet");
    for (uint256 i = 0; i < tickets.length; i++) {
        address payable to = payable(tickets[i]);
        tickets[i] = address(0);
        to.transfer(ticketPrice);
    }
    delete tickets;
}
function IsWinner() public view returns (bool) {
    return winnings[msg.sender] > 0;
} 
function CurrentWinningReward() public view returns (uint256) {
    return tickets.length * ticketPrice;
}
function RemainingTickets() public view returns (uint256) {
    return maxTickets - tickets.length;
}
}

The way the contract works is easy to explain. User select the amount of tickets he wants to buy, pays the amount in currency and tickets are stored in address memory. Once the countdown ends, a winner is draw, countdown and tickets get reset and the winner gets a percentage of the winnings.

So, the only function the token has is to collect the amount of tickets price. No tokens are transfer to the user except if the user is a winner.

I just need to change from a native token (BNB) to my token (LTT) to make Buy and Withdraw work with it.

I'm really new to solidity so any help would be great. Thanks

1 Answer 1

2

First of all, your code doesn't make sense, you are expecting BuyTicketsNow and then overriding it. Either you should check if amount is a multiplier of ticket price, or expect number of tickets and calculate the amount

Consider this solution for token amount:

pragma solidity ^0.8.16;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Strings.sol";


contract MockERC20 is ERC20 {
    constructor() ERC20('test', 'test'){
        _mint(msg.sender, 100 ether);
    }
}

contract Tickets {
    using SafeERC20 for MockERC20;

    uint public ticketPrice = 1 ether;
    address[] public tickets;

    function BuyTickets(MockERC20 token, uint amount) public returns(bool) {

    require(
        amount % ticketPrice == 0,
        string.concat(
            "the value must be multiple of ",
            Strings.toString(ticketPrice),
            token.name()
        )
    );

    require(token.allowance(msg.sender, address(this)) >= amount, "Please approve tokens before transferring");

    uint256 BuyTicketsNow =  amount / ticketPrice;

    require(BuyTicketsNow <= RemainingTickets(),"Not enough tickets available.");

    for (uint256 i = 0; i < BuyTicketsNow; i++) {
        tickets.push(msg.sender);
    }

    token.safeTransferFrom(msg.sender, address(this), amount);

    return true;
 }
 function RemainingTickets() public returns(uint) {
     return 10000000;
 }
}

Consider this solution for number of tickets (better option IMHO):

pragma solidity ^0.8.16;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Strings.sol";


contract MockERC20 is ERC20 {
    constructor() ERC20('test', 'test'){
        _mint(msg.sender, 100 ether);
    }
}

contract Tickets {
    using SafeERC20 for MockERC20;

    uint public ticketPrice = 1 ether;
    address[] public tickets;

    function BuyTickets(MockERC20 token, uint ticketAmount) public returns(bool) {

    uint amount = ticketAmount * ticketPrice;

    require(token.allowance(msg.sender, address(this)) >= amount, "Please approve tokens before transferring");

    require(ticketAmount <= RemainingTickets(),"Not enough tickets available.");

    for (uint256 i = 0; i < ticketAmount; i++) {
        tickets.push(msg.sender);
    }

    token.safeTransferFrom(msg.sender, address(this), amount);

    return true;
 }
 function RemainingTickets() public returns(uint) {
     return 10000000;
 }
}

⚠️ This is just example, ITS NOT COMPLETE. To improve the contract you should either fixate token address in constructor or create multiple pricing for different tokens.

⚠️ From Solidity 0.8.12, you can use string.concat() to concatenate strings.

2
  • If you have any questions regarding any part of the code, don't hesitate to ask :) Jan 20, 2023 at 7:35
  • Thanks for your answer. The goal for me here is to make this function work with another token besides any native token.
    – katano
    Jan 20, 2023 at 14:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.