1

When I use the variable minimum in the require statement

require(msg.value >= minimum, "Below Minimum");

I get the below error in Remix. However, if I hardcode the require statement with 0 instead of minimum everything works. Why?

transact to SendContractEther.sendEtherToContract errored: VM error: revert. revert The transaction has been reverted to the initial state. Note: The called function should be payable if you send value and the value you send should be less than your current balance. Debug the transaction to get more information.

I am sending Ether to contract King from contract SendContractEther.

Thank you for an explanation for this behavior.

// SPDX-License-Identifier: MIT

pragma solidity 0.7.4;

contract King {

  uint public minimum;
  address payable public owner;

  constructor() payable {
    owner = msg.sender;  
    minimum = 0;
  }

  receive () external payable {
    
    require(msg.value >= minimum, "Below Minimum");
    owner.transfer(msg.value);
  
  }

}

// SPDX-License-Identifier: MIT

pragma solidity 0.7.4;

contract SendContractEther {
    
    address payable public contractAddress ;
    
    // Assign the address of the Contract
    constructor(address payable _contractAddress) payable {
        contractAddress = _contractAddress;
    }
    
    function sendEtherToContract() external payable {
        
        contractAddress.transfer(address(this).balance);
        
    }
}

1 Answer 1

2

This is a very good question!

The gas-stipend for functions receive and fallback when executed as a result of transferring ETH to the contract from another contract (i.e., not from an externally-owned account) is 2300.

In your case:

require(msg.value >= minimum, "Below Minimum");
owner.transfer(msg.value);
  • The cost of reading the storage variable minimum is 800 gas
  • The cost of reading the storage variable owner is 800 gas
  • The cost of executing an external function-call via transfer is at least 800 gas (depending on the amount being transferred, as well as the balance of each side)

The gas required for evaluating the expression msg.value >= minimum (as well as for reading the local variable msg.value itself) is negligible, but as you can see, you are already above the gas-stipend even without it, using at least 2400 gas.

Thus, replacing the storage variable minimum with a hard-coded 0 may get you well below the gas-stipend (using around 1600 gas) and resolve the problem.

You can verify this by transferring ETH to that contract from an externally-owned account, where no gas-stipend applies, and see that it completes successfully despite the storage variable minimum being used (provided that you transfer an amount equal to or larger than minimum).

Alternatively, you can rename function receive and then call it directly from the other contract:

contract King {
    ...

    function test() external payable {
        require(msg.value >= minimum, "Below Minimum");
        owner.transfer(msg.value);
    }
}

contract SendContractEther {
    ...
    
    function sendEtherToContract() external payable {
        King(contractAddress).test{value: address(this).balance}();
    }
}

Since no gas-stipend applies here., executing sendEtherToContract should complete successfully.

2
  • Thank you goodvibration for that detailed explanation. I appreciate it.
    – Axium7
    Commented Dec 1, 2020 at 8:15
  • Another solution that I just tried is to replace contractAddress.transfer(address(this).balance); with contractAddress.call{value: address(this).balance, gas: 3000000}(""); and ensure that there is enough GAS allocated in MetaMask.
    – Axium7
    Commented Dec 9, 2020 at 8:28

Your Answer

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

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