Contract-Development

即使 require() 為假,事務也會成功

  • December 13, 2018

我寫了一份契約,我的回調函式中有一個 require() 說:

require(msg.value >= 1 / buyPrice);

其中購買價格 = 100;

當我在 Remix 中測試它並發送 0.0001 ETH 時,它可以工作,交易失敗。但是當我在 Ropsten 網路上進行測試並發送相同數量的乙太幣時,怎麼會?

pragma solidity >=0.4.0 <0.6.0;

contract owned {
   address public owner;
   address constant public ledger = 0xd7b4754A023B92F811EF98b2bd1cD8d531905E5a;

   constructor() public {
       owner = msg.sender;
   }

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

   function transferOwnership(address newOwner) onlyOwner public {
       owner = newOwner;
   }
}

interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }

contract MyCoin {
   // Public variables of the token
   string public name = "MyCoin";
   string public symbol = "XYZ";
   uint8 public decimals = 18;
   // 18 decimals is the strongly suggested default, avoid changing it
   uint256 public totalSupply;
   uint256 initialSupply = 50000000;

   // This creates an array with all balances
   mapping (address => uint256) public balanceOf;
   mapping (address => mapping (address => uint256)) public allowance;

   // This generates a public event on the blockchain that will notify clients
   event Transfer(address indexed from, address indexed to, uint256 value);

   // This notifies clients about the amount burnt
   event Burn(address indexed from, uint256 value);

   /**
    * Constrctor function
    *
    * Initializes contract with initial supply tokens to the creator of the contract
    */
   constructor() public {
       totalSupply = initialSupply * 10 ** uint256(decimals);  // Update total supply with the decimal amount
       balanceOf[this] = totalSupply;                // Give the creator all initial tokens
       //name = tokenName;                                   // Set the name for display purposes
       //symbol = tokenSymbol;                               // Set the symbol for display purposes
   }

   /**
    * Internal transfer, only can be called by this contract
    */
   function _transfer(address _from, address _to, uint _value) internal {
       // Prevent transfer to 0x0 address. Use burn() instead
       require(_to != 0x0);
       // Check if the sender has enough
       require(balanceOf[_from] >= _value);
       // Check for overflows
       require(balanceOf[_to] + _value > balanceOf[_to]);
       // Save this for an assertion in the future
       uint previousBalances = balanceOf[_from] + balanceOf[_to];
       // Subtract from the sender
       balanceOf[_from] -= _value;
       // Add the same to the recipient
       balanceOf[_to] += _value;
       Transfer(_from, _to, _value);
       // Asserts are used to use static analysis to find bugs in your code. They should never fail
       assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
   }


   /**
    * Transfer tokens from other address
    *
    * Send `_value` tokens to `_to` in behalf of `_from`
    *
    * @param _from The address of the sender
    * @param _to The address of the recipient
    * @param _value the amount to send
    */
   function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
       require(_value <= allowance[_from][msg.sender]);     // Check allowance
       allowance[_from][msg.sender] -= _value;
       _transfer(_from, _to, _value);
       return true;
   }

   /**
    * Set allowance for other address
    *
    * Allows `_spender` to spend no more than `_value` tokens in your behalf
    *
    * @param _spender The address authorized to spend
    * @param _value the max amount they can spend
    */
   function approve(address _spender, uint256 _value) public
       returns (bool success) {
       allowance[msg.sender][_spender] = _value;
       return true;
   }

   /**
    * Set allowance for other address and notify
    *
    * Allows `_spender` to spend no more than `_value` tokens in your behalf, and then ping the contract about it
    *
    * @param _spender The address authorized to spend
    * @param _value the max amount they can spend
    * @param _extraData some extra information to send to the approved contract
    */
   function approveAndCall(address _spender, uint256 _value, bytes _extraData)
       public
       returns (bool success) {
       tokenRecipient spender = tokenRecipient(_spender);
       if (approve(_spender, _value)) {
           spender.receiveApproval(msg.sender, _value, this, _extraData);
           return true;
       }
   }

   /**
    * Destroy tokens from other account
    *
    * Remove `_value` tokens from the system irreversibly on behalf of `_from`.
    *
    * @param _from the address of the sender
    * @param _value the amount of money to burn
    */
   function burnFrom(address _from, uint256 _value) public returns (bool success) {
       require(balanceOf[_from] >= _value);                // Check if the targeted balance is enough
       require(_value <= allowance[_from][msg.sender]);    // Check allowance
       balanceOf[_from] -= _value;                         // Subtract from the targeted balance
       allowance[_from][msg.sender] -= _value;             // Subtract from the sender's allowance
       totalSupply -= _value;                              // Update totalSupply
       Burn(_from, _value);
       return true;
   }
}

/******************************************/
/*       ADVANCED TOKEN STARTS HERE       */
/******************************************/

contract MyAdvancedToken is owned, MyCoin {

   uint256 public sellPrice = 500;
   uint256 public buyPrice = 500;
   uint256 private endTime = 1517788799; //4 February 2018 23:59:59

   uint256 private phaseOneStart = 1513814400; //21.12.2017 00:00:00
   uint256 private phaseOneEnd = 1514764799; //31.12.2017 23:59:59

   uint256 private phaseTwoStart = 1514764800; //1.1.2018 00:00:00
   uint256 private phaseTwoEnd = 1516147199; //16.1.2018 23:59:59

   uint256 private xmasStart = 1514160000; //25.12.2017 00:00:00
   uint256 private xmasEnd = 1514246399;   //25.12.2017 23:59:59
   uint256 private newyearStart = 1514764800;  //1.1.2018 00:00:00
   uint256 private newyearEnd = 1514851199;    //1.1.2018 23:59:59

   mapping (address => bool) public frozenAccount;

   /* This generates a public event on the blockchain that will notify clients */
   event FrozenFunds(address target, bool frozen);

   /* Initializes contract with initial supply tokens to the creator of the contract */
   constructor() MyCoin() public {}

   /* Internal transfer, only can be called by this contract */
   function _transfer(address _from, address _to, uint _value) internal {
       require (_to != 0x0);                               // Prevent transfer to 0x0 address. Use burn() instead
       require (balanceOf[_from] > _value);                // Check if the sender has enough
       require (balanceOf[_to] + _value > balanceOf[_to]); // Check for overflows
       require(!frozenAccount[_from]);                     // Check if sender is frozen
       require(!frozenAccount[_to]);                       // Check if recipient is frozen
       balanceOf[_from] -= _value;                         // Subtract from the sender
       balanceOf[_to] += _value;                           // Add the same to the recipient
       Transfer(_from, _to, _value);
       ledger.transfer(this.balance);                       //send balance to the owner
   }

   /// @notice Create `mintedAmount` tokens and send it to `target`
   /// @param target Address to receive the tokens
   /// @param mintedAmount the amount of tokens it will receive
   function mintToken(address target, uint256 mintedAmount) onlyOwner public {
       balanceOf[target] += mintedAmount;
       totalSupply += mintedAmount;
       Transfer(0, this, mintedAmount);
       Transfer(this, target, mintedAmount);
   }

   /// @notice `freeze? Prevent | Allow` `target` from sending & receiving tokens
   /// @param target Address to be frozen
   /// @param freeze either to freeze it or not
   function freezeAccount(address target, bool freeze) onlyOwner public {
       frozenAccount[target] = freeze;
       FrozenFunds(target, freeze);
   }

   /// @notice Allow users to buy tokens for `newBuyPrice` eth and sell tokens for `newSellPrice` eth
   /// @param newSellPrice Price the users can sell to the contract
   /// @param newBuyPrice Price users can buy from the contract
   function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner public {
       sellPrice = newSellPrice;
       buyPrice = newBuyPrice;
   }

   /**
    * Destroy tokens
    *
    * Remove `_value` tokens from the system irreversibly
    *
    * @param _value the amount of money to burn
    */
   function burn(uint256 _value) onlyOwner public returns (bool success) {
       require(balanceOf[this] >= _value);   // Check if the sender has enough
       balanceOf[this] -= _value;            // Subtract from the sender
       //totalSupply -= _value;                      // Updates totalSupply
       Burn(this, _value);
       return true;
   }

   /**
    * Transfer tokens
    *
    * Send `_value` tokens to `_to` from your account
    *
    * @param _to The address of the recipient
    * @param _value the amount to send
    */
   function transfer(address _to, uint256 _value) onlyOwner public {
       _transfer(this, _to, _value);
   }

   /// @notice Buy tokens from contract by sending ether
   function () payable public {

       //this is minimum what you can buy
       require(msg.value >= 1 / buyPrice);

       //ICO has to be active
       require(now < endTime);

       //get amount of tokens to send to the seller
       uint amount = calculateAmount(msg.value);
       uint bonus = getBonus(amount);

       _transfer(this, msg.sender,  amount + bonus);
   }

   /**
    * Get bonus for Xmas and New Year
    */
   function getBonus(uint _amount) constant private returns (uint256) {
       if((now >= xmasStart && now <= xmasEnd) || (now >= newyearStart && now <= newyearEnd)) {
           return _amount * 50 / 100;
       }  else {
           return 0;
       }


   }

   /**
    * Calculate how mush XYZ you need to send
    */
   function calculateAmount(uint _amount) constant private returns (uint256) {
       //phase 1 (10 days)
       if(now >= phaseOneStart && now <= phaseOneEnd) {
           // calculates the amount
           return _amount * buyPrice + (_amount * buyPrice * 50 / 100);
       } 
       //phase 2 (15 days)
       else if (now >= phaseTwoStart && now <= phaseTwoEnd) {
           return _amount * buyPrice;
       } 
       //phase 3 (20 days)
       else {
           return _amount * buyPrice - (_amount * buyPrice * 50 / 100);
       }
   }

   /// @notice Sell `amount` tokens to contract
   /// @param amount amount of tokens to be sold
   function sell(uint256 amount) public {
       require(this.balance >= amount * sellPrice);      // checks if the contract has enough ether to buy
       _transfer(msg.sender, this, amount);              // makes the transfers
       msg.sender.transfer(amount * sellPrice);          // sends ether to the seller. It's important to do this last to avoid recursion attacks
   }
}

msg.value包含金額 in ,而wei不是金額 in Ether

1 Ether等於1000000000000000000 wei,所以如果你發送1 ether給你的函式, 的值msg.value將是1000000000000000000

你可能想要這樣的東西:

require(msg.value >= 1 ether / buyPrice);

etherSolidity 中的關鍵字表示“乘以 1000000000000000000 ”。

我不知道你為什麼在 Ropsten 和 Mainnet 上得到不同的結果。它們的功能應該完全相同;如果合約處於相同狀態並且函式呼叫相同,您應該得到完全相同的結果。

請分享您的 Ropsten 和 Mainnet 交易的交易 ID,然後我可以比較它們,看看究竟發生了什麼不同。

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