账户
0x0c...5851
0x0C...5851

0x0C...5851

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.8.7+commit.e28d00a7
语言
Solidity
合同源代码
文件 1 的 1:Lottery.sol
/*
https://www.stiltonmusk.com
https://t.me/stiltonmusk
*/





// File: contracts/HasRandom.sol

pragma solidity ^0.8.7;

abstract contract HasRandom {
    uint256 _randomNonce = 1;

    function _random() internal returns (uint256) {
        return
            uint256(
                keccak256(
                    abi.encodePacked(
                        msg.sender,
                        _randomNonce++,
                        block.timestamp
                    )
                )
            );
    }
}

// File: contracts/Ownable.sol

abstract contract Ownable {
    address _owner;

    modifier onlyOwner() {
        require(msg.sender == _owner);
        _;
    }

    constructor() {
        _owner = msg.sender;
    }

    function transferOwnership(address newOwner) external onlyOwner {
        _owner = newOwner;
    }
}

// File: contracts/IERC20.sol

interface IERC20 {
    function totalSupply() external view returns (uint256);

    function balanceOf(address account) external view returns (uint256);

    function transfer(address recipient, uint256 amount)
        external
        returns (bool);

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
}
// File: contracts/IRouter.sol



interface IRouter {
    function roundNumber() external view returns (uint256);

    function token() external view returns (IERC20);

    function isWithdrawInterval() external view returns (bool);

    function interwalLapsedTime() external view returns (uint256);

    function poolAddress() external view returns (address);
}

// File: contracts/Lottery.sol





struct Ticket {
    uint256 amount;
    bool isClosed;
}

contract Lottery is Ownable, HasRandom {
    IERC20 public token;
    uint256 public bidSize = 100000000;
    uint256 public nextBidSize = 100000000;
    uint256 public roundNumber = 1;
    uint256 public bidsCount;
    Ticket[] public tickets;
    uint256 public ticketsRewards; // current round tickets summary reward
    mapping(address => uint256[]) ticketsByAccounts;
    mapping(address => uint256) roundNumberByAccount;
    mapping(address => bool) hasTicket;
    uint256 public playersCount;
    bool _isWithdrawInterval;
    uint256 _nextIntervalTime;
    uint256 public intervalTimerMin = 360;
    address public cAASBankAddress;
    uint256 cAASBankAddressPercent = 50;
    uint256 public jackpot;
    uint256 public lotteryFeePercent = 5;
    uint256 jackpotN = 1000;
    uint256 nextJackpotN;
    bool public isOpened;

    event OnJackpot(uint256 count, uint256 indexed roundNumber);

    constructor(address tokenAddress) {
        token = IERC20(tokenAddress);
        cAASBankAddress = address(this);
        nextJackpotN = jackpotN;
    }

    function setLotteryFeePercent(uint256 lotteryFeePercent_)
        external
        onlyOwner
    {
        require(lotteryFeePercent_ <= 90);
        lotteryFeePercent = lotteryFeePercent_;
    }

    function setOpenLottery(bool opened) external onlyOwner {
        isOpened = opened;
        _nextIntervalTime = block.timestamp + intervalTimerMin * 1 minutes;
    }

    function setToken(address tokenAddress) external onlyOwner {
        token = IERC20(tokenAddress);
    }

    function setNextJackpotN(uint256 n) external onlyOwner {
        nextJackpotN = n;
    }

    function withdrawOwner() external onlyOwner {
        token.transfer(_owner, token.balanceOf(address(this)));
    }

    function setNextBidSize(uint256 nextBidSize_) external onlyOwner {
        nextBidSize = nextBidSize_;
        if (bidsCount == 0) bidSize = nextBidSize_;
    }

    function setIntervalTimer(uint256 intervalTimerMin_) external onlyOwner {
        intervalTimerMin = intervalTimerMin_;
    }

    function setCAASBankAddress(address cAASBankAddress_) external onlyOwner {
        cAASBankAddress = cAASBankAddress_;
    }

    function setCAASBankAddressPercent(uint256 cAASBankAddressPercent_)
        external
        onlyOwner
    {
        require(cAASBankAddressPercent_ <= 100);
        cAASBankAddressPercent = cAASBankAddressPercent_;
    }

    function buyTicket() external {
        _buyTickets(msg.sender, 1);
    }

    function buyTickets(address account, uint256 count) external {
        _buyTickets(account, count);
    }

    function _buyTickets(address account, uint256 count) private {
        require(count > 0, "count is zero");
        require(isOpened, "lottery is not open");
        tryNextInterval();
        require(!_isWithdrawInterval, "only in game interval");
        if (roundNumberByAccount[account] != roundNumber) clearData(account);

        token.transferFrom(account, address(this), bidSize * count);

        roundNumberByAccount[account] = roundNumber;
        if (!hasTicket[account]) ++playersCount;
        hasTicket[account] = true;
        uint256 lastbTicketsCount = bidsCount;
        bidsCount += count;
        for (uint256 i = 0; i < count; ++i) {
            tickets.push(Ticket(bidSize, false));
            ticketsByAccounts[account].push(lastbTicketsCount + i);
            arrangeRewards(lastbTicketsCount + i);
        }
    }

    function arrangeRewards(uint256 ticketA) private {
        ticketsRewards += tickets[ticketA].amount;
        tickets[ticketA].amount -=
            (tickets[ticketA].amount * (lotteryFeePercent + 10)) /
            100; // lottery fee + 10% token tax
        uint256 random = _random();
        uint256 currentTicketsCount = ticketA + 1;
        uint256 ticketB = random % currentTicketsCount;
        if (ticketB == ticketA) ticketB = (ticketB + 1) % currentTicketsCount;

        // jackpot
        if (random % jackpotN == 0) {
            emit OnJackpot(jackpot, roundNumber);
            tickets[ticketB].amount += jackpot;
            ticketsRewards += jackpot;
            jackpot = 0;
        }

        if (ticketB == ticketA) return;
        uint256 percent = 1 + (random % 1000);
        if (random % 2 == 0) {
            uint256 delta = (tickets[ticketB].amount * percent) / 1000;
            tickets[ticketA].amount += delta;
            tickets[ticketB].amount -= delta;
        } else {
            uint256 delta = (tickets[ticketA].amount * percent) / 1000;
            tickets[ticketA].amount -= delta;
            tickets[ticketB].amount += delta;
        }
    }

    function clearData(address account) private {
        delete ticketsByAccounts[account];
        hasTicket[account] = false;
    }

    function getTicketsCount(address account) public view returns (uint256) {
        if (
            roundNumberByAccount[account] != roundNumber ||
            (_isWithdrawInterval &&
                intervalLapsedTime() == 0 &&
                playersCount > 1) ||
            (
                (!_isWithdrawInterval &&
                    block.timestamp >=
                    _nextIntervalTime + intervalTimerMin * 1 minutes &&
                    playersCount > 1)
            )
        ) return 0;

        return ticketsByAccounts[account].length;
    }

    function getTicket(address account, uint256 index)
        public
        view
        returns (Ticket memory)
    {
        require(index < getTicketsCount(account), "bad ticketIndex");
        return tickets[ticketsByAccounts[account][index]];
    }

    function getTickets(
        address account,
        uint256 startIndex,
        uint256 count
    ) external view returns (Ticket[] memory) {
        Ticket[] memory ticketsList = new Ticket[](count);
        require(
            startIndex + count <= getTicketsCount(account),
            "bad ticketIndex"
        );
        for (uint256 i = 0; i < count; ++i) {
            ticketsList[i] = tickets[
                ticketsByAccounts[account][startIndex + i]
            ];
        }

        return ticketsList;
    }

    function getAllTicketsListPage(uint256 startIndex, uint256 count)
        external
        view
        returns (Ticket[] memory)
    {
        Ticket[] memory ticketsList = new Ticket[](count);
        for (uint256 i = 0; i < count; ++i) {
            ticketsList[i] = tickets[startIndex + i];
        }

        return ticketsList;
    }

    function closeTicket(address account, uint256 index) external {
        _closeTickets(account, index, 1);
    }

    function closeTickets(
        address account,
        uint256 startIndex,
        uint256 count
    ) external {
        _closeTickets(account, startIndex, count);
    }

    function _closeTickets(
        address account,
        uint256 startIndex,
        uint256 count
    ) private {
        require(count > 0, "count is zero");
        require(isOpened, "lottery is not open");
        tryNextInterval();
        require(_isWithdrawInterval, "only in withdraw interval");
        uint256 toTransfer;
        uint256 lastIndex = startIndex + count;
        for (uint256 i = startIndex; i < lastIndex; ++i) {
            Ticket storage ticket = tickets[ticketsByAccounts[account][i]];
            if (ticket.isClosed) continue;
            ticket.isClosed = true;
            toTransfer += ticket.amount;
        }
        require(toTransfer > 0, "has no rewards");
        token.transfer(account, toTransfer);
    }

    function _newRound() private {
        if (!isOpened) return;
        _isWithdrawInterval = false;

        if (playersCount > 1 || (playersCount == 1 && tickets[0].isClosed)) {
            ++roundNumber;
            bidSize = nextBidSize;
            bidsCount = 0;
            playersCount = 0;
            delete tickets;
            ticketsRewards = 0;
            uint256 balance = token.balanceOf(address(this));
            if (balance > 0 && cAASBankAddress != address(this)) {
                uint256 toTransfer = ((token.balanceOf(address(this)) -
                    jackpot) * cAASBankAddressPercent) / 100;
                token.transfer(cAASBankAddress, toTransfer);
            }

            jackpot = token.balanceOf(address(this));
            jackpotN = nextJackpotN;
        }
    }

    function tryNextInterval() public {
        if (!isOpened) return;
        // next interval
        if (block.timestamp < _nextIntervalTime) return;

        // if skip reward interval
        if (!_isWithdrawInterval) {
            if (
                !_isWithdrawInterval &&
                block.timestamp >=
                _nextIntervalTime + intervalTimerMin * 1 minutes
            ) {
                _nextIntervalTime =
                    block.timestamp +
                    intervalTimerMin *
                    1 minutes;
                _newRound();
                return;
            }
            _nextIntervalTime = block.timestamp + intervalLapsedTime();
        } else {
            _nextIntervalTime = block.timestamp + intervalTimerMin * 1 minutes;
        }

        // next interval
        _isWithdrawInterval = !_isWithdrawInterval;
        // next round
        if (!_isWithdrawInterval) _newRound();
    }

    /// @dev current intervallapsed time in seconds
    function intervalLapsedTime() public view returns (uint256) {
        // if timer
        if (block.timestamp < _nextIntervalTime)
            return _nextIntervalTime - block.timestamp;
        // now withdraw interval (skipping withdraq interval)
        if (
            !_isWithdrawInterval &&
            block.timestamp < _nextIntervalTime + intervalTimerMin * 1 minutes
        )
            return
                _nextIntervalTime +
                intervalTimerMin *
                1 minutes -
                block.timestamp;
        // new interval
        return 0;
    }

    function isWithdrawInterval() external view returns (bool) {
        // if timer
        if (block.timestamp < _nextIntervalTime) return _isWithdrawInterval;
        // now withdraw interval (skipping withdraq interval)
        if (
            !_isWithdrawInterval &&
            block.timestamp >= _nextIntervalTime + intervalTimerMin * 1 minutes
        ) return false;
        // new interval
        return !_isWithdrawInterval;
    }
}
设置
{
  "compilationTarget": {
    "Lottery.sol": "Lottery"
  },
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"count","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"roundNumber","type":"uint256"}],"name":"OnJackpot","type":"event"},{"inputs":[],"name":"bidSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bidsCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"buyTicket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"count","type":"uint256"}],"name":"buyTickets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cAASBankAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"closeTicket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"count","type":"uint256"}],"name":"closeTickets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"count","type":"uint256"}],"name":"getAllTicketsListPage","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"isClosed","type":"bool"}],"internalType":"struct Ticket[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getTicket","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"isClosed","type":"bool"}],"internalType":"struct Ticket","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"startIndex","type":"uint256"},{"internalType":"uint256","name":"count","type":"uint256"}],"name":"getTickets","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"isClosed","type":"bool"}],"internalType":"struct Ticket[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getTicketsCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"intervalLapsedTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"intervalTimerMin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOpened","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isWithdrawInterval","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"jackpot","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lotteryFeePercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextBidSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"playersCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"roundNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"cAASBankAddress_","type":"address"}],"name":"setCAASBankAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"cAASBankAddressPercent_","type":"uint256"}],"name":"setCAASBankAddressPercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"intervalTimerMin_","type":"uint256"}],"name":"setIntervalTimer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"lotteryFeePercent_","type":"uint256"}],"name":"setLotteryFeePercent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nextBidSize_","type":"uint256"}],"name":"setNextBidSize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"n","type":"uint256"}],"name":"setNextJackpotN","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"opened","type":"bool"}],"name":"setOpenLottery","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"setToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tickets","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"isClosed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ticketsRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tryNextInterval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawOwner","outputs":[],"stateMutability":"nonpayable","type":"function"}]