Investigation into Failed Transactions in YOP Vaults
A non-significant number of transactions failed while the users were trying to deposit into YOP Vaults due out-of-gas error. In this post, we are going to dive deep into our understanding of why this is happening and what we are doing to mitigate the issue. Along the way we also explain what out of gas error means, how wallets estimate gas cost for smart contract function calls and how we are planning to fix this issue.
What happened and What did we observe?
At the time of writing, YOP has launched 4 Vaults where the community can deposit their crypto to earn profit. The current live vaults accept USDC, DAI and WETH as input tokens respectively. Although these vaults reached their cap of allowable TVL (total value locked) in a short time, some of the community members and the YOP team started noticing a non-trivial number of failed transactions due to out-of-gas error. The WETH and DAI Vaults saw 9 deposit transactions fail and USDC Vault saw 1 deposit transaction fail. Etherscan links to verify failed transactions for USDC, WETH and DAI vaults can be found here, here, and here respectively.
Understanding Gas, Out-of-gas error and Gas estimations done by wallets
To understand what and why out of gas error occurs, we first need to understand what gas is. So, let us start with what gas is and why do we even have transaction fees in Ethereum at all?
EVM computation (logic run by miners to execute & verify transactions) is a scarce resource. Demand for executing transactions is much higher than the supply of computational power that the miners have (demand >> supply).
On the supply side, with current block sizes, current computational for one block is approximately 12.5 million gas (one every ~ 13secs). Where “gas” = measure of computational costs of a transaction. Demand size is much more than this. Ethereum protocol and miners want to maximise economic efficiency of each block, i.e., miners differentiate high vs low value transactions so that the block is packed with highest-value transactions. Because of the huge imbalance between demand and supply of EVM computation a mechanism is required to decide which transactions should be processed first. Miners use “gas” as a metric to decide which transaction should be processed first from the Ethereum mempool. Gas is denominated in GWEI, below figure lays out conversion into commonly used units of ETH token. This is the whole point of having transaction fees in Ethereum so that high value transactions can differentiate themselves by offering to pay higher gas fees and get included earlier in the block on Ethereum blockchain.
Now that we understand Gas better, let us see how estimate Gas Fees for transactions such as depositing into YOP Vaults.
Gas Limits and Estimating Gas
Smart contracts can have complex logic which will require a significant amount of computation power to run, and transactions involving smart contracts normally cost more gas than a simple ETH transaction. In theory, a function in a smart contract can just run an infinite loop and if a user interacted with it, it could consume all the gas of the whole block. Therefore, a gas limit is needed: it allows users to specify the maximum amount of gas they are willing to pay for a transaction, so that they know it will never cost more than the limit.
However, it then presents the problem of how users set a relatively accurate gas limit, so that the limit is not so low that the transaction will never be processed successfully. To solve this problem, Ethereum provides an “eth_estimateGas” RPC method that can be used to return an estimated gas cost of a transaction. When it is called, the node that received the request will try to execute the transaction locally and return an estimation, but the data storage changes made by the transaction are discarded straight away. This is how wallets like MetaMask set the gas limit for transactions.
However, because the way that “eth_estimateGas” is executed, it can only be used as a reference and the actual cost of gas can be either lower or higher. This could be due to many different reasons, e.g., the performance of the node that executes the transaction, different code path being taken when the transaction is executed. If the actual cost is lower than the estimation, the gas left will be returned to the user. However, if the actual cost is higher than the estimation, all the gas will be consumed (as the miner has done the work), but the transaction will fail with an “out-of-gas” error.
This is what happened with these failed transactions. Our dApp did not set a recommended gas limit and let the wallets to estimate the gas and set it as the limit. It turns out sometimes these limits are too low.
It is worth noting that this has nothing to do with the gas price — the gas price will only determine if the transaction will be included in the next block. A transaction can still fail even if it has extremely high gas price but low gas limit.
What are we doing about it?
We have made changes to the dApp to not relying on the wallets to set the gas limits anymore. Instead, our dApp will run the estimation and then increase it by another 20% and then set it as the new gas limit.
This change should reduce the chance of the “out-of-gas” errors significantly. The downside is that as users, you will see that the transactions will be a bit more expensive to run than before in wallets like MetaMask, however, the actual cost will remain the same, and the gas that is not used will be returned.
We will continue to monitor this situation for our upcoming WBTC and USDT vaults. Hopefully, our fix can mitigate this issue in future. We are also working very hard to optimise our smart contracts to reduce the gas cost in general.
We are also deliberating within the team about how to reimburse community users who have faced this issue. We will ask the community for feedback on potential approaches in coming weeks. Stay tuned for more on this.