Solidity

創建 SmartBillions 出錯:交易執行失敗

  • April 3, 2019

我正在嘗試在 remix 上部署 smartBillions 合約以用於學習目的。並且持續存在以下錯誤

creation of SmartBillions errored: transaction execution failed

以下是程式碼

pragma solidity ^0.4.17;

library SafeMath {
 function sub(uint a, uint b) internal pure returns (uint) {
   assert(b <= a);
   return a - b;
 }
 function add(uint a, uint b) internal pure returns (uint) {
   uint c = a + b;
   assert(c >= a);
   return c;
 }
}

contract ERC20Basic {
 uint public totalSupply;
 address public owner; //owner
 address public animator; //animator
 function balanceOf(address who) constant public returns (uint);
 function transfer(address to, uint value) public;
 event Transfer(address indexed from, address indexed to, uint value);
 function commitDividend(address who) internal; // pays remaining dividend
}

contract ERC20 is ERC20Basic {
 function allowance(address owner, address spender) constant public returns (uint);
 function transferFrom(address from, address to, uint value) public;
 function approve(address spender, uint value) public;
 event Approval(address indexed owner, address indexed spender, uint value);
}

contract BasicToken is ERC20Basic {
 using SafeMath for uint;
 mapping(address => uint) balances;

 modifier onlyPayloadSize(uint size) {
    assert(msg.data.length >= size + 4);
    _;
 }

 function transfer(address _to, uint _value) public onlyPayloadSize(2 * 32) {
   commitDividend(msg.sender);
   balances[msg.sender] = balances[msg.sender].sub(_value);
   if(_to == address(this)) {
       commitDividend(owner);
       balances[owner] = balances[owner].add(_value);
       Transfer(msg.sender, owner, _value);
   }
   else {
       commitDividend(_to);
       balances[_to] = balances[_to].add(_value);
       Transfer(msg.sender, _to, _value);
   }
 }

 function balanceOf(address _owner) constant public returns (uint balance) {
   return balances[_owner];
 }
}

contract StandardToken is BasicToken, ERC20 {
 mapping (address => mapping (address => uint)) allowed;


 function transferFrom(address _from, address _to, uint _value) public onlyPayloadSize(3 * 32) {
   var _allowance = allowed[_from][msg.sender];
   commitDividend(_from);
   commitDividend(_to);
   allowed[_from][msg.sender] = _allowance.sub(_value);
   balances[_from] = balances[_from].sub(_value);
   balances[_to] = balances[_to].add(_value);
   Transfer(_from, _to, _value);
 }

 function approve(address _spender, uint _value) public {
   //  https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
   assert(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
   allowed[msg.sender][_spender] = _value;
   Approval(msg.sender, _spender, _value);
 }

 function allowance(address _owner, address _spender) constant public returns (uint remaining) {
   return allowed[_owner][_spender];
 }
}


contract SmartBillions is StandardToken {

   // metadata
   string public constant name = "SmartBillions Token";
   string public constant symbol = "Smart"; // changed due to conflicts
   uint public constant decimals = 0;

   // contract state
   struct Wallet {
       uint208 balance; // current balance of user
       uint16 lastDividendPeriod; // last processed dividend period of user's tokens
       uint32 nextWithdrawTime; // next withdrawal possible after this timestamp
   }
   mapping (address => Wallet) wallets;
   struct Bet {
       uint192 value; // bet size
       uint32 betHash; // selected numbers
       uint32 blockNum; // blocknumber when lottery runs
   }
   mapping (address => Bet) bets;

   uint public walletBalance = 0; // sum of funds in wallets

   // investment parameters
   uint public investStart = 1; // investment start block, 0: closed, 1: preparation
   uint public investBalance = 0; // funding from investors
   uint public investBalanceGot = 0; // funding collected
   uint public investBalanceMax = 200000 ether; // maximum funding
   uint public dividendPeriod = 1;
   uint[] public dividends; // dividens collected per period, growing array

   // betting parameters
   uint public maxWin = 0; // maximum prize won
   uint public hashFirst = 0; // start time of building hashes database
   uint public hashLast = 0; // last saved block of hashes
   uint public hashNext = 0; // next available bet block.number
   uint public hashBetSum = 0; // used bet volume of next block
   uint public hashBetMax = 5 ether; // maximum bet size per block
   uint[] public hashes; // space for storing lottery results

   // constants
   uint public constant hashesSize = 16384 ; // 30 days of blocks
   uint public coldStoreLast = 0 ; // timestamp of last cold store transfer

   // events
   event LogBet(address indexed player, uint bethash, uint blocknumber, uint betsize);
   event LogLoss(address indexed player, uint bethash, uint hash);
   event LogWin(address indexed player, uint bethash, uint hash, uint prize);
   event LogInvestment(address indexed investor, address indexed partner, uint amount);
   event LogRecordWin(address indexed player, uint amount);
   event LogLate(address indexed player,uint playerBlockNumber,uint currentBlockNumber);
   event LogDividend(address indexed investor, uint amount, uint period);

   modifier onlyOwner() {
       assert(msg.sender == owner);
       _;
   }

   modifier onlyAnimator() {
       assert(msg.sender == animator);
       _;
   }

   // constructor
   function SmartBillions() public {
       owner = msg.sender;
       animator = msg.sender;
       wallets[owner].lastDividendPeriod = uint16(dividendPeriod);
       dividends.push(0); // not used
       dividends.push(0); // current dividend
   }


   function hashesLength() constant external returns (uint) {
       return uint(hashes.length);
   }


   function walletBalanceOf(address _owner) constant external returns (uint) {
       return uint(wallets[_owner].balance);
   }


   function walletPeriodOf(address _owner) constant external returns (uint) {
       return uint(wallets[_owner].lastDividendPeriod);
   }


   function walletTimeOf(address _owner) constant external returns (uint) {
       return uint(wallets[_owner].nextWithdrawTime);
   }

   function betValueOf(address _owner) constant external returns (uint) {
       return uint(bets[_owner].value);
   }


   function betHashOf(address _owner) constant external returns (uint) {
       return uint(bets[_owner].betHash);
   }


   function betBlockNumberOf(address _owner) constant external returns (uint) {
       return uint(bets[_owner].blockNum);
   }

   function dividendsBlocks() constant external returns (uint) {
       if(investStart > 0) {
           return(0);
       }
       uint period = (block.number - hashFirst) / (10 * hashesSize);
       if(period > dividendPeriod) {
           return(0);
       }
       return((10 * hashesSize) - ((block.number - hashFirst) % (10 * hashesSize)));
   }


   function changeOwner(address _who) external onlyOwner {
       assert(_who != address(0));
       commitDividend(msg.sender);
       commitDividend(_who);
       owner = _who;
   }


   function changeAnimator(address _who) external onlyAnimator {
       assert(_who != address(0));
       commitDividend(msg.sender);
       commitDividend(_who);
       animator = _who;
   }


   function setInvestStart(uint _when) external onlyOwner {
       require(investStart == 1 && hashFirst > 0 && block.number < _when);
       investStart = _when;
   }

   function setBetMax(uint _maxsum) external onlyOwner {
       hashBetMax = _maxsum;
   }


   function resetBet() external onlyOwner {
       hashNext = block.number + 3;
       hashBetSum = 0;
   }


   function coldStore(uint _amount) external onlyOwner {
       houseKeeping();
       require(_amount > 0 && this.balance >= (investBalance * 9 / 10) + walletBalance + _amount);
       if(investBalance >= investBalanceGot / 2){ // additional jackpot protection
           require((_amount <= this.balance / 400) && coldStoreLast + 60 * 60 * 24 * 7 <= block.timestamp);
       }
       msg.sender.transfer(_amount);
       coldStoreLast = block.timestamp;
   }

   /**
    * @dev Move funds to contract jackpot
    */
   function hotStore() payable external {
       walletBalance += msg.value;
       wallets[msg.sender].balance += uint208(msg.value);
       houseKeeping();
   }

/* housekeeping functions */

   /**
    * @dev Update accounting
    */
   function houseKeeping() public {
       if(investStart > 1 && block.number >= investStart + (hashesSize * 5)){ // ca. 14 days
           investStart = 0; // start dividend payments
       }
       else {
           if(hashFirst > 0){
               uint period = (block.number - hashFirst) / (10 * hashesSize );
               if(period > dividends.length - 2) {
                   dividends.push(0);
               }
               if(period > dividendPeriod && investStart == 0 && dividendPeriod < dividends.length - 1) {
                   dividendPeriod++;
               }
           }
       }
   }

/* payments */

   /**
    * @dev Pay balance from wallet
    */
   function payWallet() public {
       if(wallets[msg.sender].balance > 0 && wallets[msg.sender].nextWithdrawTime <= block.timestamp){
           uint balance = wallets[msg.sender].balance;
           wallets[msg.sender].balance = 0;
           walletBalance -= balance;
           pay(balance);
       }
   }

   function pay(uint _amount) private {
       uint maxpay = this.balance / 2;
       if(maxpay >= _amount) {
           msg.sender.transfer(_amount);
           if(_amount > 1 finney) {
               houseKeeping();
           }
       }
       else {
           uint keepbalance = _amount - maxpay;
           walletBalance += keepbalance;
           wallets[msg.sender].balance += uint208(keepbalance);
           wallets[msg.sender].nextWithdrawTime = uint32(block.timestamp + 60 * 60 * 24 * 30); // wait 1 month for more funds
           msg.sender.transfer(maxpay);
       }
   }

/* investment functions */

   function investDirect() payable external {
       invest(owner);
   }


   function invest(address _partner) payable public {
       //require(fromUSA()==false); // fromUSA() not yet implemented :-(
       require(investStart > 1 && block.number < investStart + (hashesSize * 5) && investBalance < investBalanceMax);
       uint investing = msg.value;
       if(investing > investBalanceMax - investBalance) {
           investing = investBalanceMax - investBalance;
           investBalance = investBalanceMax;
           investBalanceGot = investBalanceMax;
           investStart = 0; // close investment round
           msg.sender.transfer(msg.value.sub(investing)); // send back funds immediately
       }
       else{
           investBalance += investing;
           investBalanceGot += investing;
       }
       if(_partner == address(0) || _partner == owner){
           walletBalance += investing / 10;
           wallets[owner].balance += uint208(investing / 10);} // 10% for marketing if no affiliates
       else{
           walletBalance += (investing * 5 / 100) * 2;
           wallets[owner].balance += uint208(investing * 5 / 100); // 5% initial marketing funds
           wallets[_partner].balance += uint208(investing * 5 / 100);} // 5% for affiliates
       wallets[msg.sender].lastDividendPeriod = uint16(dividendPeriod); // assert(dividendPeriod == 1);
       uint senderBalance = investing / 10**15;
       uint ownerBalance = investing * 16 / 10**17  ;
       uint animatorBalance = investing * 10 / 10**17  ;
       balances[msg.sender] += senderBalance;
       balances[owner] += ownerBalance ; // 13% of shares go to developers
       balances[animator] += animatorBalance ; // 8% of shares go to animator
       totalSupply += senderBalance + ownerBalance + animatorBalance;
       Transfer(address(0),msg.sender,senderBalance); // for etherscan
       Transfer(address(0),owner,ownerBalance); // for etherscan
       Transfer(address(0),animator,animatorBalance); // for etherscan
       LogInvestment(msg.sender,_partner,investing);
   }


   function disinvest() external {
       require(investStart == 0);
       commitDividend(msg.sender);
       uint initialInvestment = balances[msg.sender] * 10**15;
       Transfer(msg.sender,address(0),balances[msg.sender]); // for etherscan
       delete balances[msg.sender]; // totalSupply stays the same, investBalance is reduced
       investBalance -= initialInvestment;
       wallets[msg.sender].balance += uint208(initialInvestment * 9 / 10);
       payWallet();
   }


   function payDividends() external {
       require(investStart == 0);
       commitDividend(msg.sender);
       payWallet();
   }

   function commitDividend(address _who) internal {
       uint last = wallets[_who].lastDividendPeriod;
       if((balances[_who]==0) || (last==0)){
           wallets[_who].lastDividendPeriod=uint16(dividendPeriod);
           return;
       }
       if(last==dividendPeriod) {
           return;
       }
       uint share = balances[_who] * 0xffffffff / totalSupply;
       uint balance = 0;
       for(;last<dividendPeriod;last++) {
           balance += share * dividends[last];
       }
       balance = (balance / 0xffffffff);
       walletBalance += balance;
       wallets[_who].balance += uint208(balance);
       wallets[_who].lastDividendPeriod = uint16(last);
       LogDividend(_who,balance,last);
   }

/* lottery functions */

   function betPrize(Bet _player, uint24 _hash) constant private returns (uint) { // house fee 13.85%
       uint24 bethash = uint24(_player.betHash);
       uint24 hit = bethash ^ _hash;
       uint24 matches =
           ((hit & 0xF) == 0 ? 1 : 0 ) +
           ((hit & 0xF0) == 0 ? 1 : 0 ) +
           ((hit & 0xF00) == 0 ? 1 : 0 ) +
           ((hit & 0xF000) == 0 ? 1 : 0 ) +
           ((hit & 0xF0000) == 0 ? 1 : 0 ) +
           ((hit & 0xF00000) == 0 ? 1 : 0 );
       if(matches == 6){
           return(uint(_player.value) * 7000000);
       }
       if(matches == 5){
           return(uint(_player.value) * 20000);
       }
       if(matches == 4){
           return(uint(_player.value) * 500);
       }
       if(matches == 3){
           return(uint(_player.value) * 25);
       }
       if(matches == 2){
           return(uint(_player.value) * 3);
       }
       return(0);
   }

   /**
    * @dev Check if won in lottery
    */
   function betOf(address _who) constant external returns (uint)  {
       Bet memory player = bets[_who];
       if( (player.value==0) ||
           (player.blockNum<=1) ||
           (block.number<player.blockNum) ||
           (block.number>=player.blockNum + (10 * hashesSize))){
           return(0);
       }
       if(block.number<player.blockNum+256){
           return(betPrize(player,uint24(block.blockhash(player.blockNum))));
       }
       if(hashFirst>0){
           uint32 hash = getHash(player.blockNum);
           if(hash == 0x1000000) { // load hash failed :-(, return funds
               return(uint(player.value));
           }
           else{
               return(betPrize(player,uint24(hash)));
           }
   }
       return(0);
   }

   /**
    * @dev Check if won in lottery
    */
   function won() public {
       Bet memory player = bets[msg.sender];
       if(player.blockNum==0){ // create a new player
           bets[msg.sender] = Bet({value: 0, betHash: 0, blockNum: 1});
           return;
       }
       if((player.value==0) || (player.blockNum==1)){
           payWallet();
           return;
       }
       require(block.number>player.blockNum); // if there is an active bet, throw()
       if(player.blockNum + (10 * hashesSize) <= block.number){ // last bet too long ago, lost !
           LogLate(msg.sender,player.blockNum,block.number);
           bets[msg.sender] = Bet({value: 0, betHash: 0, blockNum: 1});
           return;
       }
       uint prize = 0;
       uint32 hash = 0;
       if(block.number<player.blockNum+256){
           hash = uint24(block.blockhash(player.blockNum));
           prize = betPrize(player,uint24(hash));
       }
       else {
           if(hashFirst>0){ // lottery is open even before swap space (hashes) is ready, but player must collect results within 256 blocks after run
               hash = getHash(player.blockNum);
               if(hash == 0x1000000) { // load hash failed :-(
                   //prize = uint(player.value); no refunds anymore
                   LogLate(msg.sender,player.blockNum,block.number);
                   bets[msg.sender] = Bet({value: 0, betHash: 0, blockNum: 1});
                   return();
               }
               else{
                   prize = betPrize(player,uint24(hash));
               }
       }
           else{
               LogLate(msg.sender,player.blockNum,block.number);
               bets[msg.sender] = Bet({value: 0, betHash: 0, blockNum: 1});
               return();
           }
       }
       bets[msg.sender] = Bet({value: 0, betHash: 0, blockNum: 1});
       if(prize>0) {
           LogWin(msg.sender,uint(player.betHash),uint(hash),prize);
           if(prize > maxWin){
               maxWin = prize;
               LogRecordWin(msg.sender,prize);
           }
           pay(prize);
       }
       else{
           LogLoss(msg.sender,uint(player.betHash),uint(hash));
       }
   }


   function () payable external {
       if(msg.value > 0){
           if(investStart>1){ // during ICO payment to the contract is treated as investment
               invest(owner);
           }
           else{ // if not ICO running payment to contract is treated as play
               play();
           }
           return;
       }
       //check for dividends and other assets
       if(investStart == 0 && balances[msg.sender]>0){
           commitDividend(msg.sender);}
       won(); // will run payWallet() if nothing else available
   }

   /**
    * @dev Play in lottery
    */
   function play() payable public returns (uint) {
       return playSystem(uint(keccak256(msg.sender,block.number)), address(0));
   }

   /**
    * @dev Play in lottery with random numbers
    * @param _partner Affiliate partner
    */
   function playRandom(address _partner) payable public returns (uint) {
       return playSystem(uint(keccak256(msg.sender,block.number)), _partner);
   }

   /**
    * @dev Play in lottery with own numbers
    * @param _partner Affiliate partner
    */
   function playSystem(uint _hash, address _partner) payable public returns (uint) {
       won(); // check if player did not win 
       uint24 bethash = uint24(_hash);
       require(msg.value <= 1 ether && msg.value < hashBetMax);
       if(msg.value > 0){
           if(investStart==0) { // dividends only after investment finished
               dividends[dividendPeriod] += msg.value / 20; // 5% dividend
           }
           if(_partner != address(0)) {
               uint fee = msg.value / 100;
               walletBalance += fee;
               wallets[_partner].balance += uint208(fee); // 1% for affiliates
           }
           if(hashNext < block.number + 3) {
               hashNext = block.number + 3;
               hashBetSum = msg.value;
           }
           else{
               if(hashBetSum > hashBetMax) {
                   hashNext++;
                   hashBetSum = msg.value;
               }
               else{
                   hashBetSum += msg.value;
               }
           }
           bets[msg.sender] = Bet({value: uint192(msg.value), betHash: uint32(bethash), blockNum: uint32(hashNext)});
           LogBet(msg.sender,uint(bethash),hashNext,msg.value);
       }
       putHashes(25); // players help collecing data, now much more than in last contract
       return(hashNext);
   }

/* database functions */

   /**
    * @dev Create hash data swap space
    * @param _sadd Number of hashes to add (<=256)
    */
   function addHashes(uint _sadd) public returns (uint) {
       require(hashFirst == 0 && _sadd > 0 && _sadd <= hashesSize);
       uint n = hashes.length;
       if(n + _sadd > hashesSize){
           hashes.length = hashesSize;
       }
       else{
           hashes.length += _sadd;
       }
       for(;n<hashes.length;n++){ // make sure to burn gas
           hashes[n] = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
       }
       if(hashes.length>=hashesSize) { // assume block.number > 10
           hashFirst = block.number - ( block.number % 10);
           hashLast = hashFirst;
       }
       return(hashes.length);
   }

   /**
    * @dev Create hash data swap space, add 128 hashes
    */
   function addHashes128() external returns (uint) {
       return(addHashes(128));
   }

   function calcHashes(uint32 _lastb, uint32 _delta) constant private returns (uint) {
       // assert(!(_lastb % 10)); this is required
       return( ( uint(block.blockhash(_lastb  )) & 0xFFFFFF )
           | ( ( uint(block.blockhash(_lastb+1)) & 0xFFFFFF ) << 24 )
           | ( ( uint(block.blockhash(_lastb+2)) & 0xFFFFFF ) << 48 )
           | ( ( uint(block.blockhash(_lastb+3)) & 0xFFFFFF ) << 72 )
           | ( ( uint(block.blockhash(_lastb+4)) & 0xFFFFFF ) << 96 )
           | ( ( uint(block.blockhash(_lastb+5)) & 0xFFFFFF ) << 120 )
           | ( ( uint(block.blockhash(_lastb+6)) & 0xFFFFFF ) << 144 )
           | ( ( uint(block.blockhash(_lastb+7)) & 0xFFFFFF ) << 168 )
           | ( ( uint(block.blockhash(_lastb+8)) & 0xFFFFFF ) << 192 )
           | ( ( uint(block.blockhash(_lastb+9)) & 0xFFFFFF ) << 216 )
           | ( ( uint(_delta) / hashesSize) << 240)); 
   }

   function getHash(uint _block) constant private returns (uint32) {
       uint delta = (_block - hashFirst) / 10;
       uint hash = hashes[delta % hashesSize];
       if(delta / hashesSize != hash >> 240) {
           return(0x1000000); // load failed, incorrect data in hashes
       }
       uint slotp = (_block - hashFirst) % 10; 
       return(uint32((hash >> (24 * slotp)) & 0xFFFFFF));
   }

   /**
    * @dev Fill hash data
    */
   function putHash() public returns (bool) {
       uint lastb = hashLast;
       if(lastb == 0 || block.number <= lastb + 10) {
           return(false);
       }
       if(lastb < block.number - 245) {
           uint num = block.number - 245;
           lastb = num - (num % 10);
       }
       uint delta = (lastb - hashFirst) / 10;
       hashes[delta % hashesSize] = calcHashes(uint32(lastb),uint32(delta));
       hashLast = lastb + 10;
       return(true);
   }

   /**
    * @dev Fill hash data many times
    * @param _num Number of iterations
    */
   function putHashes(uint _num) public {
       uint n=0;
       for(;n<_num;n++){
           if(!putHash()){
               return;
           }
       }
   }

}

你的問題是它需要比 Remix 預設使用的 3,000,000 更多的氣體。

如果您在“執行”選項卡中將“氣體限制”設置為 6,000,000,它應該可以毫無問題地部署。

引用自:https://ethereum.stackexchange.com/questions/69106