Requiring expression in Solidity - ethereum

So I have tried both of these in remix and one errors and the other does not and I'm trying to figure out why.
This is my first case -
mapping(address => uint) public balanceReceived;
function withdrawMoney(address _to, uint _amount) payable public {
require(balanceReceived[msg.sender] - _amount > 0, "You do not have the funds");
}
This produces the error -
transact to ExceptionExample.withdrawMoney 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.
However, the below works -
function withdrawMoney(address _to, uint _amount) payable public {
require(balanceReceived[msg.sender] > _amount, "You do not have the funds");
}
This produces the error -
transact to ExceptionExample.withdrawMoney errored: VM error: revert.
revert
The transaction has been reverted to the initial state.
Reason provided by the contract: "You do not have sufficient funds".
Debug the transaction to get more information.
In this case the require actually triggered.
I would expect both of these expressions to be identical, but the behaviour is different - why?

balanceReceived[msg.sender] - _amount
This subtraction in the first snippet causes an integer underflow if _amount is larger than balanceReceived[msg.sender].
Since Solidity 0.8.0, an exception is thrown if integer underflow or overflow would happen. And the uncaught exception results in the revert without any custom error message.
Solution: Use the second snippet, which can't cause integer underflow.
require(balanceReceived[msg.sender] > _amount, "You do not have the funds");
I'm not sure if this is on purpose, but it's unusual that your condition only allows the withdraw amount to be larger than the balance - not "larger or equal". So if you had balance of 1000000000000000000 (1 token with 18 decimals), you would only be able to withdraw 999999999999999999 (little less than the 1 full token) because of the current condition balanceReceived[msg.sender] > _amount.
If you changed the condition to "larger or equal" (balanceReceived[msg.sender] >= _amount), you would be able to withdraw the full balance.

Related

Gas Estimation Error when Transfer ERC721 token in Remix

I am currently implementing an ERC721 token staking function contract, but when I add the transfer code, a Gas Estimation Error occurs.
MarineBluesContract(_nftContract).transferFrom(msg.sender, address(this), _tokenId);
function stake(uint256 _tokenId, address _nftContract)
external
nonReentrant
{
require(ntfContractList[_nftContract], "Not allowed NFT contract");
require(msg.sender != address(0), "Invalid staker address");
require(_tokenId != 0, "Invalid token id");
require(MarineBluesContract(_nftContract).ownerOf(_tokenId) == msg.sender, "Not token owner");
// Staking start time
uint48 timestamp = uint48(block.timestamp);
// Staking to contract
MarineBluesContract(_nftContract).transferFrom(msg.sender, address(this), _tokenId);
// Save staking information
stakedTokens.push(
StakedToken(msg.sender, _tokenId, _nftContract, timestamp, false)
);
// Increase in staking count
totalStaked++;
emit Stake(msg.sender, _tokenId, _nftContract, timestamp);
}
enter image description here
Make sure I have enough Ethereum
Make sure staking contract has enough Ethereum
Authorize the staking contract using setApprovalForAll so that it can transfer my NFTs
Gas Estimation error came out even after taking the above steps. I'm not sure why, but if you can guess or if I'm doing something wrong, please tell me. thanks in advance!
Based on the fact that the require() condition validating _nftContract.ownerOf() doesn't fail, I'm assuming that your contract is correctly deployed on the same network as _nftContract.
transferFrom() can fail for several reasons. Most common causes might be:
The token sender (specified in the first argument) didn't approve the transaction sender (in this case your contract) to operate this specific _tokenId
The _tokenId does not belong to the token sender
The _tokenId does not exist
Mind that you're executing the transferFrom() function from your contract - so the msg.sender (user executing stake() function) needs to give approval directly to yourContract.

Does ERC20's transferFrom function fail if it doesn't meet the require statement?

I'm learning about the ERC20 contract by OpenZeppelin and curious about the approve function:
function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
uint256 currentAllowance = _allowances[sender][_msgSender()];
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
_approve(sender, _msgSender(), currentAllowance - amount);
return true;
}
Logically, it would make sense that you're only able to transfer amount from sender to recipient only if currentAllowance >= amount.
However, as you can see in this function body, the require(currentAllowance >= amount) occurs after the call to _transfer(sender, recipient, amount).
Now I remember reading that ethereum transactions are atomic. In this context, does it mean that if the require condition fails, then the _transfer is also not executed?
does it mean that if the require condition fails, then the _transfer is also not executed
Technically, it gets executed, but then it reverts because of the invalid opcode that failed require condition produces.
So there is no state change (this is what you probably mean by the "not executed"), but the gas has been used (for the execution).
Edit to clarify: No state change in this case means that no tokens are transfered.

Unable to match calculated "gas used" value using Solidity IDE to the etherscan explorer "Gas Used By Transaction"

I am trying find the gas used by the transaction when a method is clicked in the remix solidity IDE. my code is as below. Value I am getting in the gasUsed variable is different to the value that is being shown on the etherscan explorer for this transaction. It would be helpful if someone helps me in correcting my code.
pragma solidity ^0.4.22;
contract id{
uint public id;
uint public senderValue;
uint256 public gasUsed;
constructor() public {
senderValue= msg.sender;
}
function setId(uint _id) public {
uint256 gasInitial = gasleft();
id= _id;
setGasUsed(gasInitial - gasleft());
}
function setGasUsed(uint256 _gasUsed) private {
gasUsed = _gasUsed;
}
}
The value of "gas used" in remix IDE is Execution Cost and the value of "Gas Used By Transaction" in etherscan is "Transaction cost".
Execution Costs are based on the cost of computational operations which are executed as a result of the transaction.
Transaction Costs are always based on the cost of which type of data you will send to blockchain. This depends on,
base cost of transaction (21000 gas)
the cost of a contract deployment (32000 gas)
the cost for every zero byte of data or code for a transaction.
the cost of every non-zero byte of data or code for a transaction.
You can understand easily by this image
Hope this answer clear your doubt.

Ethereum Transaction Error while calling a contract function from another contract

Following smart contract works fine in Remix and Ganache. However doesn't work on private ethereum blockchains like Kaleido or Azure. What am I missing. When I call setA it consumes all gas and then fails.
pragma solidity ^0.4.24;
contract TestA {
uint public someValue;
function setValue(uint a) public returns (bool){
someValue = a;
return true;
}
}
contract TestB {
address public recentA;
function createA() public returns (address) {
recentA = new TestA();
return recentA;
}
function setA() public returns (bool) {
TestA(recentA).setValue(6);
return true;
}
}
I tried your contract in Kaleido, and found even calling eth_estimateGas with very large numbers was resulting in "out of gas".
I changed the setValue cross-contract call to set a gas value, and I was then able to call setA, and estimating the gas for setA showed just 31663.
recentA.setValue.gas(10000)(6);
I suspect this EVM behavior is related to permissioned chains with a gasprice of zero. However, that is speculation as I haven't investigated the internals.
I've also added eth_estimateGas, and support for multiple contracts in a Solidity file, to kaleido-go here in case it's helpful:
https://github.com/kaleido-io/kaleido-go
Another possibility for others encountering "out of gas" calling across contracts - In Geth if a require call fails in a called contract, the error is reported as "out of gas" (rather than "execution reverted", or a detailed reason for the require failing).
You are hitting the limit of gas allowed to be spent per block. Information about gas limit is included into every block, so you can check what's this value is right now in your blockchain. Currently on Ethereum MainNet, GasLimit (per block) is about 8 millions (see here https://etherscan.io/blocks)
To fix this, you can start your blockchain with modified genesis file. Try to increase value of gasLimit parameter in your genesis file, which specifies the maximum amount of gas processed per block. Try "gasLimit": "8000000".
Try to discard the return statement of setValue method in contract TestA.
pragma solidity ^0.4.24;
contract TestA {
uint public someValue;
function setValue(uint a) public {
someValue = a;
}
}

Creating instance of contract inside another contract and calling it's methods results in thrown exception

I have 2 basic contracts: one is for token and the second is for sale.
Token сontract:
contract MyToken is StandardToken, Ownable {
string public constant name = "My Sample Token";
string public constant symbol = "MST";
uint32 public constant decimals = 18;
function MyToken(uint _totalSupply) {
require (_totalSupply > 0);
totalSupply = _totalSupply;
balances[msg.sender] = totalSupply;
}
}
Sale Contract
contract Sale {
address owner;
address public founderAddress;
uint256 public constant foundersAmount = 50;
MyToken public token = new MyToken(1000);
uint256 public issuedTokensAmount = 0;
function Sale() {
owner = msg.sender;
founderAddress = 0x14723a09acff6d2a60dcdf7aa4aff308fddc160c;
token.transfer(founderAddress, foundersAmount);
}
function() external payable {
token.transfer(msg.sender, 1);
owner.transfer(msg.value);
}
}
StandardToken and Ownable are all standard implementations from OpenZeppelin repository. Full contract source is available here.
So basically in my Sale Contract I create an instance of my token contract with fixed supply and assign all of the tokens to the caller. Then I transfer some amount of tokens to founder address. When I try to send some ethereum to Sale contract I'm attempting to transfer some of my tokens to the sender (Running all code in Remix browser, I create an instance of Sale contract and call "fallback" method specifying some ether amount). However, this fails with "Exception during execution. (invalid opcode). Please debug the transaction for more information." message. All that I can see when debugging is that code fails in payable method at line:
token.transfer(msg.sender, 1);
I can't see the exact reason for this as I'm not able to step into this method and see whats going on inside.
Interesting thing is that when I remove a call to transfer method on token instance in Sale Contract constructor - code seems to run fine without any exceptions.
What am I missing?
I debugged into the the contract using remix, and the invalid opcode is thrown by:
290 DUP8
291 DUP1
292 EXTCODESIZE
293 ISZERO
294 ISZERO
295 PUSH2 012f
298 JUMPI
299 PUSH1 00
301 DUP1
302 INVALID
I left out the rest, but essentially it loads the address of the token contract and calls EXTCODESIZE which retrieves the contracts code size, and checks that it's not equal to 0 (the token contract exists), unfortunately, it did equate to 0. At this point, I'm unsure whether this is a limitation in remix or I've misunderstood the setup.
I tried the identical contract setup on truffle + testrpc and it deployed, accepted the currency successfully. Do note however that testrpc indicated:
Gas usage: 59137
Meaning that this is above the default sendTransaction w/ no data default (21,000 gas). This means that in a live environment, ensure that you inform users to include extra gas, otherwise the fallback function would probably fail due to OOG errors.
The reason behind this is that you're using a fallback function. Try using a normal function and it should happen.