Getting "out of gas" when sending whole amount of eth - ethereum

Here is an example of such transaction:
etherscan
It was 569730199030000 Wei on a balance,
I used current gas price(by web3.eth.getGasPrice()) 19888345864 Wei.
So my estimated fee was (19888345864 * 21000) 417655263144000 Wei.
I was trying to send (balance - estimated fee) = 152074935886000 Wei.
The result as you can see "out of gas".
Why it is possible that regular send was required more than 21000 gas? If I understand properly in case if gas price is not enough then miners simply must ignore this tx, but in case if they are agree with the gas price they have to run this transaction normally, and normally sending eth requires gas limit 21000. Where is mistake in my calculations and how to execute such tx properly?

Generally, this often happens when you're trying to send ETH to a contract that either doesn't implement a special function to accept the ETH - or the contract does implement the function but it explicitly rejects the incoming transfer.
Specifically, the first-hand recipient of your transaction is a proxy contract deployed at address 0xB233903ACec807C61eeeCc4F69dd795A617a1732, that redirects the request to a target contract deployed at address 0xd332254f274cc65aa11178b74734e2992b8f349e.
Author of the target contract has not shared its source code, but from the decompiled bytecode it seems to accept incoming ETH transfer only if series of conditions (depending on the payload of the incoming transaction and the state of another contract) is met. Otherwise it rejects the transaction with the revert statement.
def _fallback() payable: # default function
require ext_code.size(0xa24787320ede4cc19d800bf87b41ab9539c4da9d)
static call 0xa24787320ede4cc19d800bf87b41ab9539c4da9d.0x4fef8ec4 with:
gas gas_remaining wei
if not ext_call.success:
revert with ext_call.return_data[0 len return_data.size]
require return_data.size >= 32
delegate ext_call.return_data[0] with:
funct call.data[0 len 4]
gas gas_remaining wei
args call.data[4 len calldata.size - 4]
if not delegate.return_code:
revert with ext_call.return_data[0 len return_data.size]
return ext_call.return_data[0 len return_data.size]

Related

Why does the USDC transferWithAuthorisation method work inconsistently?

The USDC smart contract on Ethereum has a transferWithAuthorisation that any address can call if they have a signature.
However, the method seems to be inconsistent and often fails.
What is the reason for this?
https://etherscan.io/address/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48?method=0xe3ee160e
Most of the failures are due to: "Warning! Error encountered during contract execution [execution reverted] "
I debugged some of the failing transactions with Tenderly and learned that they all (or at least those that I debugged) fail on insufficient gas limit.
Each EVM operation costs some predefined amount of gas units. The transaction sender specifies gas limit, and then buys this amount (times gas price) for ETH. If the transaction consumes less gas units than the limit, the difference is refunded.
The transferWithAuthorisation() function seems to cost between 72k and 82k gas units depending on internal code logic (successful transactions: 0x7e..., 0x32..., 0xec...).
However the failing transactions supplied lower gas limit between 59k and 62k (0xd3..., 0xf1..., 0x54...).
So the sender possibly used an incorrect estimation of gas usage, and specified an insufficient limit.

Setting gas for transaction on Ethereum when interacting with smart contract

I am using web3-eth-contract package to connect to contract on Ethereum. I am executing it's methods by:
contract.methods
.methodName(ids)
.send({
to: address,
from: address
})
The problem is that I get:
After that I tried to add gasLimit there:
contract.methods
.methodName(ids)
.send({
to: address,
from: address,
gasLimit: 300000,
})
and it worked fine when I use methods that require only simple arguments. When I use methods where I pass array of arguments and there are more arguments than 2 transactions are being cancelled. What should I pass to gasLimit or how can I estimate it so it will work every time?
The gas used to execute the transaction may depend on your parameters, so what you experience is totally reasonable.
Now, to know what gasLimit to set you need to know what the transaction is doing and how much gas it is expected to burn. You can take one of the following approaches:
set the gasLimit very high, so that your transaction is always executed
check the older transactions to the same contract and see how much gas they burnt
use the transaction format after the London Uprade and specify max ETH you are willing to pay for the transaction (maxFeePerGas) instead of setting gasLimit

how to transfer fund from msg.sender(amount) to recipient address without using setting msg.value

We can transfer funds from address(this) to recipient. But is there any way to transfer funds directly msg.sender wallet to recipient? I can not set msg.value at the time of invoking payoutBonus call. Because I can get the amount only inside payoutBonus method.
function payoutBonus(address recipient) public payable returns (bool) {
// bonus = calculateBonus();
//transfer this bonus to recipient from msg.sender;
return true;
}
msg.value is a read-only property reflecting value of the incoming transaction. If you want to send an ETH amount, you can do it in one of two ways.
Notes:
These examples work on Solidity 0.8. Some previous versions also allow the send() method that is now deprecated, don't require the payable type, and have slightly different syntax for the call() method.
bonus is amount of wei (1 ETH is 10^18 wei)
The transfer() method
uint256 bonus = calculateBonus();
payable(msg.sender).transfer(bonus);
The transfer() method only allows consuming 2300 gas, which is enough for non-contract recipients. If the recipient is a contract requiring more than 2300 gas to receive the ETH (for instance it sets some local variable), the transaction reverts.
Low-level call()
uint256 bonus = calculateBonus();
msg.sender.call{value: bonus}("");
With the low-level call() method, you can also specify the gas limit, function to call and it's arguments. Use this method only if you know what you're doing.

Is there any API that I can call inside of the smartcontract that can get a transaction detail by txid?

I'm learning ethereum developing and want to check the transaction detail by txid in the smart contract, but I didn't find any interface that can help me to do that, anyone has any clue?
I think you want to call getTransaction-like RPC call in smart contract. However, that's not possible.
All global variables which can be used in Solidity is following.
Global Variables
block.coinbase (address): current block miner’s address
block.difficulty (uint): current block difficulty
block.gaslimit (uint): current block gaslimit
block.number (uint): current block number
block.blockhash (function(uint) returns (bytes32)): hash of the given block - only works for 256 most recent blocks
block.timestamp (uint): current block timestamp
msg.data (bytes): complete calldata
msg.gas (uint): remaining gas
msg.sender (address): sender of the message (current call)
msg.value (uint): number of wei sent with the message
now (uint): current block timestamp (alias for block.timestamp)
tx.gasprice (uint): gas price of the transaction
tx.origin (address): sender of the transaction (full call chain)
sha3(...) returns (bytes32): compute the Ethereum-SHA3 hash of the (tightly packed) arguments
sha256(...) returns (bytes32): compute the SHA256 hash of the (tightly packed) arguments
ripemd160(...) returns (bytes20): compute RIPEMD of 256 the (tightly packed) arguments
ecrecover(bytes32, uint8, bytes32, bytes32) returns (address): recover public key from elliptic curve signature
addmod(uint x, uint y, uint k) returns (uint): compute (x + y) % k where the addition is performed with arbitrary precision and does not wrap around at 2**256.
mulmod(uint x, uint y, uint k) returns (uint): compute (x * y) % k where the multiplication is performed with arbitrary precision and does not wrap around at 2**256.
this (current contract’s type): the current contract, explicitly convertible to address
super: the contract one level higher in the inheritance hierarchy
selfdestruct(address): destroy the current contract, sending its funds to the given address
.balance: balance of the address in Wei
.send(uint256) returns (bool): send given amount of Wei to address, returns false on failure.
Of course, there is some tricky solution which is using Oraclize.
I recommend to see this website :) https://docs.oraclize.it/
In conclusion, for your question, getting transaction detail from solidity is not possible in native-way, you should use offchain solution like Oraclize. :)
Smart Contract only has the Access to the current state of the blockchain. Solidity is use to only creating the rules for the Transactions and updating the state of the variable. For getting the Transaction you have to use web3 libraray.

Assert vs. require within Solidity?

My understanding is that "require" refunds the remaining gas if it fails whereas "assert" doesn't.
I'm a bit confused though b/c what is meant by remaining gas? I often specify a gas limit of a very large number even when the contract I'm calling requires very little.
If I were to specify 4,700,000 gas limit, and only 50,000 gas was required, but "assert" failed in the called contract, would I lose all 4,700,000 gas?
Yes, you would lose the entire amount (or close to it). assert is effectively the catastrophic bail out of the transaction due to something completely unexpected. It should be used to check for things like making sure your contract hasn't wound up in an invalid state, avoid divide by 0, over/underflow, etc.
require, on the other hand, will only consume the gas used up to the point of failure. The remaining gas will be refunded.
The gas limit you specify shouldn't be arbitrarily high. When you initiate a transaction, the full amount of gas you specify is sent and whatever is left at the end of execution is refunded back to you. Not only are you exposing yourself to asserts in your own contract, but if you call other contracts from your own, you are giving that contract a lot of bandwidth to do whatever it wants with the gas you pass on to it.
Example showing gas consumption:
pragma solidity ^0.4.16;
contract Test {
function run(uint8 i) public pure {
uint8 total = 0;
for (uint8 j = 0; j < 10; j++)
total += j;
assert (i < 20);
require (i < 10);
for (j = 0; j < 10; j++)
total += j;
}
}
All tests run with gas limit = 5000000
Test #1 - run(8): Function runs successfully - 1860 gas consumed.
Test #2 - run(15): Function passes assert, fails at require. Only first loop is run - 1049 gas consumed.
Test #3 - run(25): Function fails at assert. Again, only first loop is run - 4978536 gas consumed (ouch).