账户
0xbc...bcce
0xbC...BCCE

0xbC...BCCE

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.6.12+commit.27d51765
语言
Solidity
合同源代码
文件 1 的 3:DoTxGameContract.sol
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;

import "https://raw.githubusercontent.com/DefiOfThrones/DOTTokenContract/master/IDotTokenContract.sol";
import "https://raw.githubusercontent.com/DefiOfThrones/DOTTokenContract/master/libs/SafeMath.sol";


interface IDoTxLib{
    function queryChainLinkPrice(string calldata _fsym, string calldata _fsymId, int256 _multiplicator, bytes4 _selector) external;
    function fetchFirstDayPrices(string calldata firstHouseTicker, string calldata secondHouseTicker, string calldata firstHouseId, string calldata secondHouseId, int256 multiplicator, uint256 warIndex) external;
    function fetchLastDayPrices(string calldata firstHouseTicker, string calldata currentSecondHouseTicker, string calldata firstHouseId, string calldata secondHouseId, int256 multiplicator, uint256 warIndex) external;
    function setDoTxGame(address gameAddress) external;
    function calculateHousePerf(int256 open, int256 close, int256 precision) external pure returns(int256);
    function calculatePercentage(uint256 amount, uint256 percentage, uint256 selecteWinnerPrecision) external pure returns(uint256);
    function calculateReward(uint256 dotxUserBalance, uint256 totalDoTxWinningHouse, uint256 totalDoTxLosingHouse) external view returns(uint256);
    function getWarIndex() external view returns(uint256);
}

contract Context {
    constructor () internal { }

    function _msgSender() internal view virtual returns (address payable) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes memory) {
        this; 
        return msg.data;
    }
}

contract Ownable is Context {
    address private _owner;
    address private _owner2;
    address public dotxLibAddress;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    constructor () internal {
        address msgSender = _msgSender();
        _owner = msgSender;
        emit OwnershipTransferred(address(0), msgSender);
    }

    function owner() public view returns (address) {
        return _owner;
    }
    
    function owner2() public view returns (address) {
        return _owner2;
    }
    
    function setOwner2(address ownerAddress) public onlyOwner {
        _owner2 = ownerAddress;
    }

    modifier onlyOwner() {
        require(_owner == _msgSender() || _owner2 == _msgSender(), "Ownable: caller is not the owner");
        _;
    }
    
    modifier onlyOwnerOrDoTxLib() {
        require(_owner == _msgSender() || dotxLibAddress == _msgSender() || _owner2 == _msgSender(), "Ownable: caller is not the owner or the lib");
        _;
    }
    
    modifier onlyDoTxLib() {
        require(dotxLibAddress == _msgSender(), "Ownable: caller is not the owner or the lib");
        _;
    }

    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

/**
 * @title DeFi Of Thrones Game Contract
 * @author Maxime Reynders - DefiOfThrones (https://github.com/DefiOfThrones/DOTTokenContract)
 */
contract DoTxGameContract is Ownable {
    using SafeMath for uint256;
    //Burn x% of the losing house tickets to Vitalik Address
    address constant public BURN_ADDRESS = 0x0000000000000000000000000000000000000001;

    //War struct, for each war a War variable will be created 
    struct War {
        uint256 startTime;
        uint256 duration;
        uint256 ticketPrice;
        uint256 purchasePeriod;
        bytes32 winningHouse;
        uint256 warFeesPercent;
        int256 multiplicator;
        uint256 burnPercentage;
        uint256 stakingPercentage;
        House firstHouse;
        House secondHouse;
        mapping(address => User) users;
    }
    //House struct, each war contains an map of 2 houses
    struct House {
        bytes32 houseTicker;
        bytes32 houseId;
        uint256 openPrice;
        uint256 closePrice;
        uint256 ticketsBought;
    }
    //User struct, each war contains a map of users
    //Each user can pledge allegiance to a house, buy tickets and switch house by paying fees (x% of its bought tickets)
    struct User {
        bytes32 houseTicker;
        uint256 ticketsBought;
        bool rewardClaimed;
    }
    
    //BURN STAKING INFORMATION
    struct BurnStake {
        uint256 firstHouseBurnDoTx;
        uint256 firstHouseStakingDoTx;
        uint256 secondHouseBurnDoTx;
        uint256 secondHouseStakingDoTx;
    }
    
    struct WarHouses {
        uint256 index;
        bytes32 firstHouse;
        bytes32 secondHouse;
        uint256 startTime;
        uint256 duration;
    }
    
    //DOTX Token Contract Address
    IDotTokenContract private dotxToken;
    //DOTX Game lib
    IDoTxLib private dotxLib;
    //x% of the losing house tickets are sent to the throne vault
    address stakingAddress = address(0x1c2206f3115CaC3750acCb899d18d50b774C2f21);
    
    //Map of Wars 
    mapping(uint256 => War) public wars;
    
    //GENERAL VARS
    //Total fees paid by users
    uint256 public totalFees;
    //Precision for the select winner calculation
    uint256 public selecteWinnerPrecision = 100000;
    
    uint256 public burnPercentage = 5;
    uint256 public stakingPercentage = 5;
    int256 public multiplicator = 10000;
    
    //EVENTS
    event WarStarted(uint256 warIndex);
    event TicketBought(uint256 warIndex, string house, uint256 valueInDoTx, address sender, string txType);
    event ClaimReward(uint256 warIndex, uint256 reward, uint256 balance, address sender, string txType);
    event SwitchHouse(uint256 warIndex, string from, string to, address sender, uint256 valueInDoTx);
    event openPriceFetched(uint256 warIndex);
    event closePriceFetched(uint256 warIndex);
    event StakeBurn(uint256 warIndex, uint256 burnValue, uint256 stakeValue);

    //MODIFIERS
    modifier onlyIfCurrentWarFinished(uint256 warIndex) {
        require(wars[warIndex].startTime.add(wars[warIndex].duration) <= now || warIndex == 0, "Current war not finished");
        _;
    }
    
    modifier onlyIfCurrentWarNotFinished(uint256 warIndex) {
        require(wars[warIndex].startTime.add(wars[warIndex].duration) > now, "Current war finished");
        _;
    }
    
    modifier onlyIfTicketsPurchasable(uint256 warIndex) {
        require(now.sub(wars[warIndex].startTime) < wars[warIndex].purchasePeriod,
        "Purchase tickets period ended");
        _;
    }
    
    modifier onlyIfPricesFetched(uint256 warIndex){
        require(wars[warIndex].firstHouse.openPrice != 0 && wars[warIndex].secondHouse.openPrice != 0, "Open prices not fetched");
        require(wars[warIndex].firstHouse.closePrice != 0 && wars[warIndex].secondHouse.closePrice != 0, "Close prices not fetched");
        _;
    }
    
    /**
     * Game contract constructor
     * Just pass the DoTx token contract address in parameter
     **/
    constructor(address dotxTokenAddress, address dotxLibAddr, bool setupAddressInLib) public {
        //Implement DoTx contract interface by providing address
        dotxToken = IDotTokenContract(dotxTokenAddress);
        
        setDoTxLib(dotxLibAddr, setupAddressInLib);
    }
    
    /**************************
            WAR METHODS
    ***************************/
    
    /**
     * Start a war only if the previous is finished
     * Parameters :
     * _firstHouseTicker First house ticker 
     * _secondHouseTicker Second house ticker 
     * _duration Duration of the war in seconds 
     * _ticketPrice Ticket price : Number of DoTx needed to buy a ticket (in wei precision)
     * purchasePeriod Number of seconds where the users can buy tickets from the starting date
     * _warFeesPercent How many % fees it will cost to user to switch house 
     * _warIndex Index of the war in mapping
     **/
    function startWar(string memory _firstHouseTicker, string memory _secondHouseTicker, string memory _firstHouseId, string memory _secondHouseId, 
    uint256 _duration, uint256 _ticketPrice, uint256 _purchasePeriod, uint256 _warFeesPercent, uint256 _warIndex) 
    public onlyOwner returns(bool) {
        //Just prevent to replace a war
        require(_warIndex > dotxLib.getWarIndex(), "War index already exists");
        
        //Create war  
        wars[_warIndex] = War(now, _duration, _ticketPrice, _purchasePeriod, 0, _warFeesPercent, multiplicator, burnPercentage, stakingPercentage, 
        House(stringToBytes32(_firstHouseTicker), stringToBytes32(_firstHouseId), 0, 0, 0),
        House(stringToBytes32(_secondHouseTicker), stringToBytes32(_secondHouseId), 0, 0, 0));
        
        emit WarStarted(_warIndex);
        
        fetchFirstDayPrices(_warIndex);
        
        return true;
    }
    
     /**
     * Buy ticket(s) for the current war - only if tickets are purchasable -> purchasePeriod
     * Parameters :
     * _houseTicker House ticker 
     * _numberOfTicket The number of tickets the user wants to buy
     **/
    function buyTickets(string memory _houseTicker, uint _numberOfTicket, uint256 warIndex) public onlyIfTicketsPurchasable(warIndex) {
        bytes32 houseTicker = stringToBytes32(_houseTicker);
        //Get house storage
        House storage userHouse = getHouseStg(houseTicker, warIndex);
        
        //Allow user to only buy tickets for one single House and the one passed in parameter
        bytes32 userHouseTicker = wars[warIndex].users[msg.sender].houseTicker;
        require(userHouse.houseTicker == houseTicker && (userHouseTicker == houseTicker || userHouseTicker == 0), "You can not buy tickets for the other house");

        wars[warIndex].users[msg.sender].houseTicker = userHouse.houseTicker;

        //Update user tickets
        wars[warIndex].users[msg.sender].ticketsBought = wars[warIndex].users[msg.sender].ticketsBought.add(_numberOfTicket);
        
        //Increase tickets bought for the house
        userHouse.ticketsBought = userHouse.ticketsBought.add(_numberOfTicket);
        
        uint256 valueInDoTx = wars[warIndex].ticketPrice.mul(_numberOfTicket);
        
        //Propagate TicketBought event
        emit TicketBought(warIndex, _houseTicker, valueInDoTx, msg.sender, "BOUGHT");
        
        //Transfer DoTx
        dotxToken.transferFrom(msg.sender, address(this), valueInDoTx);
    }
    
    /**
     * Switch house for the user - only if tickets are purchasable -> purchasePeriod
     * Parameters :
     * _fromHouseTicker Current house user pledged allegiance 
     * _toHouseTicker the house user wants to join
     **/
    function switchHouse(string memory _fromHouseTicker, string memory _toHouseTicker, uint256 warIndex) public onlyIfTicketsPurchasable(warIndex) {

        bytes32 fromHouseTicker = stringToBytes32(_fromHouseTicker);
        bytes32 toHouseTicker = stringToBytes32(_toHouseTicker);
        
        //Check if toHouse is in competition && different of fromHouse
        require(checkIfHouseInCompetition(toHouseTicker, warIndex) && fromHouseTicker != toHouseTicker, "House not in competition");
        //Check if user belongs to _fromHouse 
        require(wars[warIndex].users[msg.sender].houseTicker == fromHouseTicker, "User doesn't belong to fromHouse");
        
        House storage fromHouse = getHouseStg(fromHouseTicker, warIndex);
        House storage toHouse = getHouseStg(toHouseTicker, warIndex);
        
        //Switch house for user
        wars[warIndex].users[msg.sender].houseTicker = toHouseTicker;
        
        //Update fromHouse tickets
        uint256 ticketsBoughtByUser = wars[warIndex].users[msg.sender].ticketsBought;
        fromHouse.ticketsBought = fromHouse.ticketsBought.sub(ticketsBoughtByUser);
        
        //Update toHouse tickets
        toHouse.ticketsBought = toHouse.ticketsBought.add(ticketsBoughtByUser);
        
        //Get fees
        uint256 feesToBePaid = getFeesForSwitchHouse(msg.sender, warIndex);
        //Update total fees
        totalFees = totalFees.add(feesToBePaid);
        
        emit SwitchHouse(warIndex, _fromHouseTicker, _toHouseTicker, msg.sender, feesToBePaid);
        
        //Get fees from user wallet
        dotxToken.transferFrom(msg.sender, address(this), feesToBePaid);
    }
    
    /**
     * Allow users who pledged allegiance to the winning house to claim bought tickets + reward
     * Parameters :
     **/
    function claimRewardAndTickets(uint256 warIndex) public onlyIfCurrentWarFinished(warIndex) returns(bool) {
        //Only claim reward one times
        require(wars[warIndex].users[msg.sender].rewardClaimed == false, "You already claimed your reward");
        
        //Check if user belongs to winning house
        require(wars[warIndex].users[msg.sender].ticketsBought > 0 && wars[warIndex].users[msg.sender].houseTicker == wars[warIndex].winningHouse, "User doesn't belong to winning house");
        
        //Set rewardClaimed to true
        wars[warIndex].users[msg.sender].rewardClaimed = true;
        
        //DoTx in user balance (tickets bought) & reward
        uint256 reward = getCurrentReward(wars[warIndex].winningHouse, msg.sender, warIndex);
        uint256 balance = getUserDoTxInBalance(warIndex, msg.sender);
        
        dotxToken.transfer(msg.sender, reward.add(balance));
        
        emit ClaimReward(warIndex, reward, balance, msg.sender, "CLAIM");
    }

    
    /*****************************
            PRICES METHODS
    ******************************/
    
    /**
     * Fetch the prices for the 2 houses the first day for the current war
     **/
    function fetchFirstDayPrices(uint256 warIndex) public onlyOwner {
        require(wars[warIndex].firstHouse.openPrice == 0 && wars[warIndex].secondHouse.openPrice == 0, "Open prices already fetched");
        
        string memory firstHouse = bytes32ToString(wars[warIndex].firstHouse.houseTicker);
        string memory secondHouse = bytes32ToString(wars[warIndex].secondHouse.houseTicker);
        
        dotxLib.fetchFirstDayPrices(firstHouse, secondHouse, bytes32ToString(wars[warIndex].firstHouse.houseId), bytes32ToString(wars[warIndex].secondHouse.houseId), wars[warIndex].multiplicator, warIndex);
    }

    /**
     * Fetch the prices for the 2 houses the last day for the current war
     **/
    function fetchLastDayPrices(uint256 warIndex) public onlyOwner onlyIfCurrentWarFinished(warIndex) {
        require(wars[warIndex].firstHouse.closePrice == 0 && wars[warIndex].secondHouse.closePrice == 0, "Close prices already fetched");
        
        string memory firstHouse = bytes32ToString(wars[warIndex].firstHouse.houseTicker);
        string memory secondHouse = bytes32ToString(wars[warIndex].secondHouse.houseTicker);
        
        dotxLib.fetchLastDayPrices(firstHouse, secondHouse, bytes32ToString(wars[warIndex].firstHouse.houseId), bytes32ToString(wars[warIndex].secondHouse.houseId), wars[warIndex].multiplicator, warIndex);
    }
    
    /**
     * Elect the winner based on open prices & close prices
     **/
    function selectWinner(uint256 warIndex) public onlyOwner onlyIfCurrentWarFinished(warIndex) onlyIfPricesFetched(warIndex) {
        require(wars[warIndex].winningHouse == 0, "Winner already selected");
        
        int256 precision = int256(selecteWinnerPrecision);
        
        int256 firstHousePerf =  dotxLib.calculateHousePerf(int256(wars[warIndex].firstHouse.openPrice), int256(wars[warIndex].firstHouse.closePrice), precision);
        int256 secondHousePerf = dotxLib.calculateHousePerf(int256(wars[warIndex].secondHouse.openPrice), int256(wars[warIndex].secondHouse.closePrice), precision);
        
        //Set winner house
        wars[warIndex].winningHouse = (firstHousePerf > secondHousePerf ? wars[warIndex].firstHouse : wars[warIndex].secondHouse).houseTicker;
        House memory losingHouse = (firstHousePerf > secondHousePerf ? wars[warIndex].secondHouse : wars[warIndex].firstHouse);
        
        /*
        BURN X% OF LOSING HOUSE'S DOTX
        */
        uint256 burnValue = calculateBurnStaking(losingHouse, true, warIndex);
        dotxToken.transfer(BURN_ADDRESS, burnValue);
        
        /*
        SEND X% OF LOSING HOUSE'S DOTX TO STAKING ADDRESS
        */
        uint256 stakingValue = calculateBurnStaking(losingHouse, true, warIndex);
        dotxToken.transfer(stakingAddress, stakingValue);
        
        emit StakeBurn(warIndex, burnValue, stakingValue);
    }

    
    /*******************************
            CHAINLINK METHODS
    ********************************/
    
    /**
     * Handler method called by Chainlink for the first house open price 
     **/
    function firstHouseOpen(uint256 _price, uint256 warIndex) external onlyDoTxLib {
        wars[warIndex].firstHouse.openPrice = _price;
        openPriceEvent(warIndex);
    }
    /**
     * Handler method called by Chainlink for the second house open price 
     **/
    function secondHouseOpen(uint256 _price, uint256 warIndex) external onlyDoTxLib {
        wars[warIndex].secondHouse.openPrice = _price;
        openPriceEvent(warIndex);
    }
    /**
     * Handler method called by Chainlink for the first house close price 
     **/
    function firstHouseClose(uint256 _price, uint256 warIndex) external onlyDoTxLib {
        wars[warIndex].firstHouse.closePrice = _price;
        closePriceEvent(warIndex);
    }
    /**
     * Handler method called by Chainlink for the second house close price 
     **/
    function secondHouseClose(uint256 _price, uint256 warIndex) external onlyDoTxLib {
        wars[warIndex].secondHouse.closePrice = _price;
        closePriceEvent(warIndex);
    }
    /**
     * Emit openPriceFetched event if needed
     **/
    function openPriceEvent(uint256 warIndex) private {
        if(wars[warIndex].firstHouse.openPrice != 0 && wars[warIndex].secondHouse.openPrice != 0){
            emit openPriceFetched(warIndex);
        }
    }
    /**
     * Emit closePriceFetched event if needed
     **/
    function closePriceEvent(uint256 warIndex) private {
        if(wars[warIndex].firstHouse.closePrice != 0 && wars[warIndex].secondHouse.closePrice != 0){
            emit closePriceFetched(warIndex);
        }
    }
    
    /*****************************
            GETTER METHODS
    ******************************/
    
    /**
     * Get user (ticketsBought * ticketPrice) - user fees
     **/
    function getUserDoTxInBalance(uint256 _warIndex, address userAddress) public view returns(uint256){
        return wars[_warIndex].users[userAddress].ticketsBought.mul(wars[_warIndex].ticketPrice);
    }
    
    
    function getFeesForSwitchHouse(address userAddress, uint256 warIndex) public view returns(uint256){
        return (getUserDoTxInBalance(warIndex, userAddress).mul(wars[warIndex].warFeesPercent)).div(100);
    }
    
    /**
     * Returns the current user
     **/
    function getUser(uint256 _warIndex, address userAddress) public view returns(User memory){
        return wars[_warIndex].users[userAddress];
    }
    
    /**
     * Return house for a specific war
     **/
    function getHouse(uint256 _warIndex, string memory houseTicker) public view returns(House memory){
        bytes32 ticker = stringToBytes32(houseTicker);
        return wars[_warIndex].firstHouse.houseTicker == ticker ? wars[_warIndex].firstHouse : wars[_warIndex].secondHouse;
    }
    /**
     * Return burn stake information
     **/
    function getBurnStake(uint256 warIndex) public view returns(BurnStake memory){
        return BurnStake(calculateBurnStaking(wars[warIndex].firstHouse, true, warIndex), calculateBurnStaking(wars[warIndex].firstHouse, false, warIndex),
        calculateBurnStaking(wars[warIndex].secondHouse, true, warIndex), calculateBurnStaking(wars[warIndex].secondHouse, false, warIndex));
    }
    
    function getWarsHouses(uint256 min, uint256 max) public view returns (WarHouses[20] memory){
        WarHouses[20] memory houses;
        uint256 i = min;
        uint256 index = 0;
        while(index < 20 && i <= (max - min) + 1){
            houses[index] = (WarHouses(i, wars[i].firstHouse.houseTicker, wars[i].secondHouse.houseTicker, wars[i].startTime, wars[i].duration));
            i++;
            index++;
        }
        return houses;
    }
    /*****************************
            ADMIN METHODS
    ******************************/
    
    /**
     * Set the select winner precision used for calculate the best house's performance
     **/
    function setSelectWinnerPrecision(uint256 _precision) public onlyOwner{
        selecteWinnerPrecision = _precision;
    }

     /**
     * Set staking % of the losing house's DoTx to send for the currentWar
     **/
    function setStakingBurnPercentageWar(uint256 _burnPercentage, uint256 _stakingPercentage, uint256 warIndex) public onlyOwner{
        wars[warIndex].burnPercentage = _burnPercentage;
        wars[warIndex].stakingPercentage = _stakingPercentage;
    }
    
    /**
     * Set staking % of the losing house's DoTx to send
     **/
    function setStakingBurnPercentage(uint256 _burnPercentage, uint256 _stakingPercentage) public onlyOwner{
        burnPercentage = _burnPercentage;
        stakingPercentage = _stakingPercentage;
    }
    
    /**
     * Precision of the prices receive from WS
     **/
    function setMultiplicatorWar(int256 _multiplicator, uint256 warIndex) public onlyOwner{
        wars[warIndex].multiplicator = _multiplicator;
    }
    
    /**
     * Precision of the prices receive from WS
     **/
    function setMultiplicator(int256 _multiplicator) public onlyOwner{
        multiplicator = _multiplicator;
    }
    
    /**
     * Let owner withdraw DoTx fees (in particular to pay the costs generated by Chainlink)
     **/
    function withdrawFees() public onlyOwner {
        //Fees from switch house
        dotxToken.transfer(owner(), totalFees);
        
        totalFees = 0;
    }
    
    /**
     * Let owner set the DoTxLib address
     **/
    function setDoTxLib(address dotxLibAddr, bool setupAddressInLib) public onlyOwner {
        //DOTX lib mainly uses for Chainlink
        dotxLibAddress = dotxLibAddr;
        dotxLib = IDoTxLib(dotxLibAddress);
        if(setupAddressInLib){
            dotxLib.setDoTxGame(address(this));
        }
    }
    
    /**
     * Let owner update stakingAddress
     **/
    function setStakingAddress(address stakingAdr) public onlyOwner {
        stakingAddress = stakingAdr;
    }

    /****************************
            UTILS METHODS
    *****************************/
    
    function getHouseStg(bytes32 ticker, uint256 warIndex) private view returns(House storage){
        return wars[warIndex].firstHouse.houseTicker == ticker ? wars[warIndex].firstHouse : wars[warIndex].secondHouse;
    }
    
    /**
     * Check if the house passed in parameter is in the current war
     **/
    function checkIfHouseInCompetition(bytes32 _houseTicker, uint256 warIndex) private view returns(bool){
        return wars[warIndex].firstHouse.houseTicker == _houseTicker || wars[warIndex].secondHouse.houseTicker == _houseTicker;
    }
    
    /**
     * Calculate the reward for the current user
     **/
    function getCurrentRewardString(string memory _winningHouse, address userAddress, uint256 warIndex) public view returns(uint256){
        bytes32 winningHouseTicker = stringToBytes32(_winningHouse);
        return getCurrentReward(winningHouseTicker, userAddress, warIndex);
    } 
    
    function getCurrentReward(bytes32 _winningHouse, address userAddress, uint256 warIndex) public view returns(uint256){
        //Losing house
        House memory losingHouse = wars[warIndex].firstHouse.houseTicker == _winningHouse ? wars[warIndex].secondHouse : wars[warIndex].firstHouse;
        
        //Total DoTx in house's balance
        uint256 totalDoTxWinningHouse = getHouseStg(_winningHouse, warIndex).ticketsBought.mul(wars[warIndex].ticketPrice);
        uint256 totalDoTxLosingHouse = losingHouse.ticketsBought.mul(wars[warIndex].ticketPrice).sub(calculateBurnStaking(losingHouse, true, warIndex)).sub(calculateBurnStaking(losingHouse, false, warIndex));
        
        return dotxLib.calculateReward(getUserDoTxInBalance(warIndex, userAddress), totalDoTxWinningHouse, totalDoTxLosingHouse);
    }
    
    function calculateBurnStaking(House memory house, bool isBurn, uint256 warIndex) public view returns(uint256){
        uint256 ticketsBoughtValueDoTx = house.ticketsBought.mul(wars[warIndex].ticketPrice);
        uint256 percentage =  isBurn ? wars[warIndex].burnPercentage : wars[warIndex].stakingPercentage;
        //Calculate tickets remaining after burn
        return dotxLib.calculatePercentage(ticketsBoughtValueDoTx, percentage, selecteWinnerPrecision);
    }
    
    /**
     * Convert string to bytes32
     **/
    function stringToBytes32(string memory source) public pure returns (bytes32 result) {
        bytes memory tempEmptyStringTest = bytes(source);
        if (tempEmptyStringTest.length == 0) {
            return 0x0;
        }
    
        assembly {
            result := mload(add(source, 32))
        }
    }
    
    /**
     * Convert bytes32 to string
     **/
    function bytes32ToString(bytes32 x) public pure returns (string memory) {
        bytes memory bytesString = new bytes(32);
        uint charCount = 0;
        for (uint j = 0; j < 32; j++) {
            byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
            if (char != 0) {
                bytesString[charCount] = char;
                charCount++;
            }
        }
        bytes memory bytesStringTrimmed = new bytes(charCount);
        for (uint256 j = 0; j < charCount; j++) {
            bytesStringTrimmed[j] = bytesString[j];
        }
        return string(bytesStringTrimmed);
    }
}
合同源代码
文件 2 的 3:IDotTokenContract.sol
合同源代码
文件 3 的 3:SafeMath.sol
设置
{
  "compilationTarget": {
    "browser/tests/DoTxGameContract.sol": "DoTxGameContract"
  },
  "evmVersion": "istanbul",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"address","name":"dotxTokenAddress","type":"address"},{"internalType":"address","name":"dotxLibAddr","type":"address"},{"internalType":"bool","name":"setupAddressInLib","type":"bool"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"warIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"balance","type":"uint256"},{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"string","name":"txType","type":"string"}],"name":"ClaimReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"warIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"burnValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"stakeValue","type":"uint256"}],"name":"StakeBurn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"warIndex","type":"uint256"},{"indexed":false,"internalType":"string","name":"from","type":"string"},{"indexed":false,"internalType":"string","name":"to","type":"string"},{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"valueInDoTx","type":"uint256"}],"name":"SwitchHouse","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"warIndex","type":"uint256"},{"indexed":false,"internalType":"string","name":"house","type":"string"},{"indexed":false,"internalType":"uint256","name":"valueInDoTx","type":"uint256"},{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"string","name":"txType","type":"string"}],"name":"TicketBought","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"WarStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"closePriceFetched","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"openPriceFetched","type":"event"},{"inputs":[],"name":"BURN_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"burnPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_houseTicker","type":"string"},{"internalType":"uint256","name":"_numberOfTicket","type":"uint256"},{"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"buyTickets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"x","type":"bytes32"}],"name":"bytes32ToString","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"houseTicker","type":"bytes32"},{"internalType":"bytes32","name":"houseId","type":"bytes32"},{"internalType":"uint256","name":"openPrice","type":"uint256"},{"internalType":"uint256","name":"closePrice","type":"uint256"},{"internalType":"uint256","name":"ticketsBought","type":"uint256"}],"internalType":"struct DoTxGameContract.House","name":"house","type":"tuple"},{"internalType":"bool","name":"isBurn","type":"bool"},{"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"calculateBurnStaking","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"claimRewardAndTickets","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dotxLibAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"fetchFirstDayPrices","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"fetchLastDayPrices","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"firstHouseClose","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"firstHouseOpen","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"getBurnStake","outputs":[{"components":[{"internalType":"uint256","name":"firstHouseBurnDoTx","type":"uint256"},{"internalType":"uint256","name":"firstHouseStakingDoTx","type":"uint256"},{"internalType":"uint256","name":"secondHouseBurnDoTx","type":"uint256"},{"internalType":"uint256","name":"secondHouseStakingDoTx","type":"uint256"}],"internalType":"struct DoTxGameContract.BurnStake","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_winningHouse","type":"bytes32"},{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"getCurrentReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_winningHouse","type":"string"},{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"getCurrentRewardString","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"userAddress","type":"address"},{"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"getFeesForSwitchHouse","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_warIndex","type":"uint256"},{"internalType":"string","name":"houseTicker","type":"string"}],"name":"getHouse","outputs":[{"components":[{"internalType":"bytes32","name":"houseTicker","type":"bytes32"},{"internalType":"bytes32","name":"houseId","type":"bytes32"},{"internalType":"uint256","name":"openPrice","type":"uint256"},{"internalType":"uint256","name":"closePrice","type":"uint256"},{"internalType":"uint256","name":"ticketsBought","type":"uint256"}],"internalType":"struct DoTxGameContract.House","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_warIndex","type":"uint256"},{"internalType":"address","name":"userAddress","type":"address"}],"name":"getUser","outputs":[{"components":[{"internalType":"bytes32","name":"houseTicker","type":"bytes32"},{"internalType":"uint256","name":"ticketsBought","type":"uint256"},{"internalType":"bool","name":"rewardClaimed","type":"bool"}],"internalType":"struct DoTxGameContract.User","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_warIndex","type":"uint256"},{"internalType":"address","name":"userAddress","type":"address"}],"name":"getUserDoTxInBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"min","type":"uint256"},{"internalType":"uint256","name":"max","type":"uint256"}],"name":"getWarsHouses","outputs":[{"components":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"bytes32","name":"firstHouse","type":"bytes32"},{"internalType":"bytes32","name":"secondHouse","type":"bytes32"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"internalType":"struct DoTxGameContract.WarHouses[20]","name":"","type":"tuple[20]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"multiplicator","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner2","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"secondHouseClose","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_price","type":"uint256"},{"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"secondHouseOpen","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"selectWinner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"selecteWinnerPrecision","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"dotxLibAddr","type":"address"},{"internalType":"bool","name":"setupAddressInLib","type":"bool"}],"name":"setDoTxLib","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"_multiplicator","type":"int256"}],"name":"setMultiplicator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"_multiplicator","type":"int256"},{"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"setMultiplicatorWar","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"ownerAddress","type":"address"}],"name":"setOwner2","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_precision","type":"uint256"}],"name":"setSelectWinnerPrecision","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"stakingAdr","type":"address"}],"name":"setStakingAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_burnPercentage","type":"uint256"},{"internalType":"uint256","name":"_stakingPercentage","type":"uint256"}],"name":"setStakingBurnPercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_burnPercentage","type":"uint256"},{"internalType":"uint256","name":"_stakingPercentage","type":"uint256"},{"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"setStakingBurnPercentageWar","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakingPercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_firstHouseTicker","type":"string"},{"internalType":"string","name":"_secondHouseTicker","type":"string"},{"internalType":"string","name":"_firstHouseId","type":"string"},{"internalType":"string","name":"_secondHouseId","type":"string"},{"internalType":"uint256","name":"_duration","type":"uint256"},{"internalType":"uint256","name":"_ticketPrice","type":"uint256"},{"internalType":"uint256","name":"_purchasePeriod","type":"uint256"},{"internalType":"uint256","name":"_warFeesPercent","type":"uint256"},{"internalType":"uint256","name":"_warIndex","type":"uint256"}],"name":"startWar","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"source","type":"string"}],"name":"stringToBytes32","outputs":[{"internalType":"bytes32","name":"result","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"_fromHouseTicker","type":"string"},{"internalType":"string","name":"_toHouseTicker","type":"string"},{"internalType":"uint256","name":"warIndex","type":"uint256"}],"name":"switchHouse","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"wars","outputs":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"ticketPrice","type":"uint256"},{"internalType":"uint256","name":"purchasePeriod","type":"uint256"},{"internalType":"bytes32","name":"winningHouse","type":"bytes32"},{"internalType":"uint256","name":"warFeesPercent","type":"uint256"},{"internalType":"int256","name":"multiplicator","type":"int256"},{"internalType":"uint256","name":"burnPercentage","type":"uint256"},{"internalType":"uint256","name":"stakingPercentage","type":"uint256"},{"components":[{"internalType":"bytes32","name":"houseTicker","type":"bytes32"},{"internalType":"bytes32","name":"houseId","type":"bytes32"},{"internalType":"uint256","name":"openPrice","type":"uint256"},{"internalType":"uint256","name":"closePrice","type":"uint256"},{"internalType":"uint256","name":"ticketsBought","type":"uint256"}],"internalType":"struct DoTxGameContract.House","name":"firstHouse","type":"tuple"},{"components":[{"internalType":"bytes32","name":"houseTicker","type":"bytes32"},{"internalType":"bytes32","name":"houseId","type":"bytes32"},{"internalType":"uint256","name":"openPrice","type":"uint256"},{"internalType":"uint256","name":"closePrice","type":"uint256"},{"internalType":"uint256","name":"ticketsBought","type":"uint256"}],"internalType":"struct DoTxGameContract.House","name":"secondHouse","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawFees","outputs":[],"stateMutability":"nonpayable","type":"function"}]