Solidity gas estimation - always out of gas when setting to 0 - ethereum

I'm trying to figure out a solution to this problem, to do with transaction confirmation order and setting values to 0
pragma solidity ^0.5.17;
contract Test {
uint256 amount;
constructor() public {}
function join() public {
amount += 100;
}
function leave() public {
amount -= 100;
}
}
Given these transactions (tested on ropsten):
tx 1) Call Join Confirmed
amount == 100
tx 2) Call Join (gas price 1) Pending
amount == 100 should tx3 get mined first
tx 3) Call Leave (gas price 100) Pending
amount == 0
However tx 2 will always fail with an out of gas error for as long as the amount is set back to 0. This doesn't happen if the value is any higher than 0. My understanding is that it costs more gas to set a value to its 0 state instead of a positive integer, and the gas estimation isn't taking this into account. I've tried delete hoping this would give a gas refund to compensate for the too-low gas limit, but it still failed.
Is there an elegant way to handle this scenario? The only ways I can think of are over-estimating the gas for all join transactions, which has its obvious drawbacks, or never setting amount back to 0.

You are making correct observations.
by setting it to 0 you delete storage, and get gas refund. This is why it takes less amount of gas. But gas refunds have top limit, so you can't use it to store ETH.
It costs 20,000 gas to store one value in a slot (source:https://github.com/ethereum/go-ethereum/blob/d13c59fef0926af0ef0cff0a2e793f95d46442f0/params/protocol_params.go#L41 )
it costs 2,200 gas to load one value from a storage slot (source: https://github.com/ethereum/go-ethereum/blob/d13c59fef0926af0ef0cff0a2e793f95d46442f0/params/protocol_params.go#L89)
That's why you are seeing different gas consumption values.
Just set your gasLimit to some empirically found roughly-estimated value.
Do a trace of your transaction and you will see all gas consumption values.
Now, the way gas refunds work is that during the state transition function, you're asked for the full gas for the run of your contract. During this run, all gas refunds are accumulated in StateDB (temporary state object). At the end of the state transition function, you will get refunds for all storage releases your contract is going to make. This is why you have to set higher gas limit that Etherscan shows, because lets say your contract needs 15,000 gas to run, after storage is released (say for 5,000 gas) , Etherscan will show like the transaction needed 10,000 gas. This is not true because you have got gas refunds at the end, but at the beginning you needed the whole amount of gas (15,000). Gas refunds are sponsored by the miner, his account is going to get less ETH because he is paying you these refunds.

Related

Is there a way to get the transaction fees on uniswap and sushiswap_

I'm building a trading bot and I need to get the cost of the swap transaction on uniswap and sushiswap. But I cant find anything that can help me get it from an API or something similar.
I tried looking at the docs of uniswap and sushiswap but they only talked about how they calculate the fees but not about if there was any way to get them using the SDK or something similar.
Using web3.js, you can estimate the cost in gas units, and then multiply by the gas price, to get the transaction cost in ETH.
Simplified example estimating execution cost of function foo():
// how many gas units is the transaction going to cost
const gasUnits = await myContract.methods.foo().estimateGas();
// price (recommended by your node) of each gas unit in wei
const gasPrice = await web3.eth.getGasPrice();
Docs:
https://web3js.readthedocs.io/en/v1.8.2/web3-eth-contract.html#methods-mymethod-estimategas
https://web3js.readthedocs.io/en/v1.8.2/web3-eth.html#getgasprice

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.

web3js Uniswap handle event log data

I am building a scraper to get all Swap data from Uniswap using web3js.
So far I subscribe to the log and filter topics by Swap() event.
then I decode the data and get amount0In, amount1In, amount0Out, amount1Out.
My problem is in the swapExactETHForTokensSupportingFeeOnTransferTokens() function.
Normally a swap has token0 in and token1 out, but this function gives me values for 3 of the 4 and I can not seem to understand how to handle that. eventually what I want is to know what they spend, what they got and what I need to update the new reserves of that pair.
If someone has the understanding of the Uniswap RouterV2 contract swap functions, I would like to get some pointers on how to handle the data to get my calculations right.
I found the solution here ethereum.stackexchange Anyone with the same question gets a very detailed answer there.
some contracts actually transfers to recipient specified amount of tokens minus 10% (5% tax fee and 5% liquidity fee) 208940457743532637 - 10% = 188046411969179375 and emits Transfer event. Then, PancakePair _swap function emits Swap event with base value of Amount0Out 208940457743532637
Amount0In is greater than zero because the token contract returns part of tokens as a liquidity pair on Pancake Swap.

Is this gas fee calculation correct?

An address consumes 20,000 gas via SSTORE.
Given is a gas price of 35 Gwei.
If I store 10,000 addresses in a map, it will cost me:
20,000 gas * 10,000 = 200,000,000 gas
200,000,000 Gas * 35 Gwei = 7 Ether.
Is the calculation correct?
If I do the same on a layer2 chain, does the whole thing cost me 7 matic for example, or is there something else I need to consider?
Your calculation is correct.
I'm assuming you want to store the values in an array instead of 10k separate storage variables. If it's a dynamic-length array, you should also consider the cost of sstore while updating a (non-zero to non-zero) value of the slot holding the array length (currently 2900 gas for each .push() function resizing the array).
You should also consider the block gas limit - a transaction costing 200M gas is not going to fit into a block on probably any network, so any miner won't mine it.
So based on your use case, you might want to change the approach. For example, if the addresses are used for validation, you might be able to store just the merkle tree root (1 value instead of the 10k) and then validate against it using the address and its merkle proof.

What gasPrice to use to deploy contract on congested ethereum mainnet

I am attempting to deploy a contract onto the Ethereum mainnet, I have specified:
network_id: 1,
provider: infura,
gas: 5000000,
gasPrice: 140
And I continuously get the errors:
Error: Contract transaction couldn't be found after 50 blocks
or
transaction underpriced
Is this because the mainnet is so congested, miners are not picking up my transaction within the 50 block period?
What gas/gasPrices have other people successfully used on the mainnet?
You can check current gas price at https://ethgasstation.info/ for example. Note that gas price is usually shown in Gwei. So 21 Gwei is 21000000000 wei or 0.000021 eth.
To expand on Andrey's answer, your gas is WAY too low. Historically, you will typically need to pay around 10-20 Gwei to have your transaction mined in an average amount of time. If you can wait 10s of minutes or even hours, you can get away with ~5Gwei. If you need the transaction to execute fast, or if you just want an average transaction time when the network is really congested, you're probably looking at ~40Gwei.
You can programmatically set your gas price based on the median gas price of the most recent mined blocks using web3.eth.getGasPrice(callback). Source.