This article clarifies common misunderstandings among Ethereum developers: Gas, transactions and smart contracts, etc.

This article clarifies common misunderstandings among Ethereum developers: Gas, transactions and smart contracts, etc.

Loading

Sort out 7 common misunderstandings of Ethereum programmers.

Original title: “Introduction | Common Misunderstandings of Ethereum Programmers”
Written by: spalladino
Translation & Proofreading: Min Min & A Jian

Recently, I happened to read an article entitled “Programmers’ Misunderstandings about Time Zones”, which made me burst into laughter. This article reminded me of programmers’ misunderstandings in other areas, such as names and time, so I started looking for anything about Ethereum. No matter how hard I can find, I have to do my best.

Misunderstandings about Gas

Calling estimateGas will return the amount of gas consumed by the transaction

Calling estimateGas will indeed return a gas consumption amount, but this is the amount of gas that the transaction will spend when it is packaged in the current state. The current state of the blockchain may be very different from the state when you need the transaction to be put on the chain. Therefore, when your transactions are effectively packaged into blocks, different code paths may be used, and the amount of gas that needs to be consumed may also be completely different.

If the executed code is the same, the amount of gas consumed by my transaction is also the same.

wrong. Even if you use the same parameters to execute the same instructions, the gas cost may be different. For example, compared to a storage location that already has a non-zero value, if you want to write to a new storage location, the cost of SSTORE (write storage operation) will be much higher (see EIP2200). This means that if you send two ERC20 token transfers to a new address, the cost of the first transaction will be much higher than the second, even if the code executed by the two is exactly the same.

If the status is exactly the same, the amount of gas consumed by my transaction is also the same

Normally yes, unless you are unlucky enough to run into a hard fork, causing some operations to be repriced. Although this sounds complicated, to put it bluntly, you cannot safely hardcode the gas limit of transactions in dApps unless you are determined to release dApp updates after each hard fork.

If the code is the same, the state is the same, and there is no hard fork, can I trust the return value of estimateGas ?

Now you can believe that the return value of estimateGas is the amount of gas that your transaction needs to consume, but you don’t know whether the transaction will proceed as you wish. The so-called gas estimation is that the node will use different gas values ​​to try your transaction and return the lowest gas value that ensures that your transaction will not fail. However, the node will only look at your transaction, not the internal calls of the transaction. This means that if the contract code you call has a try/catch block, which causes the internal call to be irrevocable after it occurs, the estimated gas value you get is sufficient for the calling contract, but for the called contract Is not enough.

In multi-signature wallets, this situation often occurs: even in the case of a transaction failure, most multi-signature wallets will mark the operation as executed, which means that they cannot undo the outermost transaction (the resulting influences). Therefore, the value returned by a native gas estimate may be sufficient for multi-signature code, but not necessarily enough for the operation you actually want to run. This is why Gnosis Safe has a dedicated gas estimation method.

Please note that this is why the failure of the operation due to insufficient gas is difficult to detect. Internal calls may run out of gas because the allocated gas is too small, and the transaction itself may still have a lot of gas available. This means that checking the gas usage and gas limit of a transaction is not a reliable way to detect gas errors.

No matter what, I just send more gas every time

In most cases, this method works. But remember, the contract can view the gas it receives in a transaction. Therefore, the contract can be easily written, and once too much gas is received, the transaction will fail. But what I suspect is that there is no point in doing so except to prove it.

Misunderstandings about trading

As long as the node accepts the transaction, the transaction will be mined

Want to be beautiful. Ethereum’s network congestion will cause the gas price to fluctuate greatly, so your transaction may be evicted from the mempool (a collection of transactions waiting to be mined). If the gas price spikes, you need to resend the transaction.

I can slightly increase the gas price and resubmit the transaction

As long as you increase the gas price to the minimum amount required by the node you interact with (see txpool.pricebump ), then there is no problem, otherwise you will still be rejected.

Miners always choose the transaction with the highest gas price

Not necessarily. Miners can choose whatever they want. They may squeeze into their own transactions for their own benefit, or even open an out-of-agreement channel to package transactions for users who meet their requirements.

However, even if they decide the packing priority based on the revenue, how to fill the block in the best way is a knapsack problem. Since the transaction cannot be divided into several parts, it may be more profitable to pack two 5M gas transactions in a block with a gas cap of 10M instead of one 6M gas transaction, even if the gas price of the 5M gas transaction is low Transaction at 6M gas.

If I send the same transaction with a higher gas price, will the miner choose the next transaction to replace the previous one?

The replacement transaction must be sent to the miner before the old transaction is on the chain. In other words, if you send a replacement transaction, you still need to monitor the hash values ​​of all transactions under the same nonce you sent before.

Misunderstandings about Nonce

I can get the nonce of my next transaction through getTransactionCount

It depends on the block parameters you are using. If you query your transaction count based on the latest block, your unpackaged transactions will be ignored, which will further cause you to accidentally overwrite one of your unpackaged transactions.

I can get the nonce of my next transaction through getTransactionCount('pending')

Although this is feasible in most cases, you cannot guarantee that all your unpackaged transactions are in the mempool of the node you are querying. If you have a lot of unpackaged transactions, the node you are communicating with may have discarded some of them, but these transactions may still exist elsewhere!

Misunderstandings about Log

I can effectively monitor events by continuously calling getLogs

Although this is a very useful method (yes, it’s polling!), it will cause problems with chain reorganization. If you want to poll for the new log on the latest block, you will not be notified about the block reorganization, and you do not know whether the events you see need to be readjusted.

I can effectively monitor events by installing filtering programs

Until two weeks ago, this was not a common choice, because Infura does not support http-based filtering programs, MetaMask uses http-based filtering programs by default, which means that 99% of your dApp users use this filtering program ( Note: I may be a little exaggerated). In addition to new events, the filter will also notify you of events deleted due to block reorganization. However, this requires that the infrastructure and nodes you are interacting with remain online. If they happen to lose the state of the filter, you may miss the reorganization event.

I can effectively monitor events through websocket subscription

Great! In this way, in addition to believing that your node will stay online, you must also believe that you will stay online and that the connection between you and the node is reliable. I want to know how many times did you get disconnected while attending the Zoom meeting this week?

Now, I must admit that I have become so fascinated by this topic that I gave a lightning talk on this topic on Devcon 5. If you want to learn more, EIP234 explains the basic principles of these challenges well, and ethereumjs-blockstream solves this problem.

Misunderstandings about contracts

Smart contracts are immutable

Brother, if you still have this idea, you are really out. I explained this in a 30-page article, which is really long.

Smart contracts that do not contain any DELEGATECALL are unchangeable

In fact, the contract can be called periodically ( CALL ) to a variable address, and the result is used as part of the calculation, or as an instruction to change the state, thereby changing the running code.

That does not contain any DELEGATECALL or CALL smart contracts, they are always unchangeable, right?

There is also STATICCALL . Don’t forget STATICCALL !

Smart contracts that do not contain any CALL are unchangeable

You have to rule out a situation: this smart contract is deployed through CREATE2, will dynamically load the runtime in its initial code (initcode), and can self-destruct. In this case, the “owner” can destroy the contract and recreate the contract on the same address with a different code.

Smart contracts that do not contain any CALL and are not deployed through CREATE2 are unchangeable

There is one case to be ruled out: this contract is deployed through a contract deployed by CREATE2. Therefore, you need to trace the entire deployment chain to find the Ethereum external account that originally created the contract to ensure that there is no trickiness and no self-destruct operation. This article delves into this issue in depth.

Misunderstandings about ERC20 tokens

I won’t expand on it. This topic is more suitable for writing a complete article. When interacting with tokens, use OpenZeppelin’s SafeERC20 (you can read more about it in this article). Please keep in mind that when transferring money, the tokens received by the receiver are not necessarily equal to the tokens deducted by the sender. Let’s look at the next part.

Misunderstandings about Ethereum

The total supply of Ether will only increase

We all know that many ethers are unusable. Some are due to the loss of the private key of an external account, some are accidentally sent to an all-zero address, and some are stuck in the contract and cannot be processed (sorry, I can’t help it). All in all, this part of Ether still exists, but it is not accessible.

However, there is a way to destroy ether. If you instruct a contract to selfdestruct and designate itself as the recipient of funds, all Ether in this contract will be destroyed. This means that as long as you are willing to destroy more ether than the block reward, you can deflate the ether.

I can write a contract that rejects any transfer of Ether

As you may know, if you do not declare any payable methods, Solidity will reject all Ether transfers sent to your contract to prevent funds from being stuck in the contract. However, we can also send funds to the contract without triggering any code: either the contract is designated as the recipient of the self-destruct operation reward, or it is designated as the recipient of the block reward. As @gorgos pointed out in the comments, the contract deployment address can be calculated in advance, and Ether can be sent to that address before the contract is deployed.

That is, if you track all Ether transfers sent to your contract, your total balance may be greater than the sum of all transfers you process.

Source link: gist.github.com