One of the neat things about running a service based on open source software is that sometimes your customers will write your features for you, as is the case with Rivet’s latest addition.
MyEtherWallet had a problem. Sometimes doing something on Ethereum requires multiple consecutive transactions that rely on in-order execution. For example, if you want to make a trade on 0x, you must first set an allowance for the 0x Exchange contract to take your tokens, then you can execute the trade itself. From a user experience perspective, the best way to do this is to present the user with two transactions to sign, send them off to the blockchain and wait for them to be mined. The problem with this approach is estimating how much gas the second transaction will use.
When you submit a transaction to the blockchain, you have to specify a gas limit
for the transaction. Applications normally get the amount of gas a transaction
will require by running eth_estimateGas
, which runs the transaction against
the current block and tells you how much gas that transaction would use right
now.
But using our example from earlier, if your allowance transaction hasn’t
confirmed on the blockchain, your trade transaction will fail — using
drastically less gas than it would if it succeeded. If you run eth_estimateGas
twice before submitting either transaction, the second transaction’s gas will be
too low, and the transaction will run out of gas.
Until now, Ethereum nodes didn’t offer a good way to estimate gas for that second transaction.The workaround to date has been to use excessively high gas limits on those transactions — the extra gas gets refunded to the user, but it looks scary up front when they see how much the transaction might cost.
Introducing ethercattle_estimateGasList
Wanting to give their users the best possible experience, MyEtherWallet made a
pull request to the Geth
team to add an eth_estimateGasList
RPC method, which allows applications to
specify a list of transactions that will be executed consecutively, and the gas
for each will be returned. Using our earlier example of a 0x trade, an
application could submit an allowance setting transaction and a trade execution
transaction together. eth_estimateGasList
will execute them in order, so the
gas returned for the trade execution transaction reflects the fact that the
allowance was already set.
Now, the Geth team hasn’t merged this change into core Geth yet, but it has been merged into the EtherCattle fork of Geth (with a slight modification), and as of this week is available on Rivet.cloud.
When we pulled the change into EtherCattle, we renamed the RPC method
ethercattle_estimateGasList
. Since this is an outstanding pull-request to
Geth, it’s possible it might be merged with a modified interface at some point
in the future, and we don’t want to be in a situation where our version of
eth_estimateGasList
is different from the Core Geth version. If and when the
feature gets merged into core Geth we will deprecate our version, but we want to
allow for a grace period where customers who rely on this feature now can
transition to a standard version.
Example
In this example, we run an ethercattle_estimateGasList
query with 3
transactions, and it responds with 3 gas estimations.
curl https://ethercattlereplica/ --data '{"jsonrpc":"2.0","id":1,"method":"ethercattle_estimateGasList","params":[[{"from":"0x17b5065693f44e999d8eb692e41385e67859f838","data":"0x552410770000000000000000000000000000000000000000000000000000000000000000","to":"0x99d0e6b5bfec73c8fd96b1c067617e1287a593cc"},{"from":"0x17b5065693f44e999d8eb692e41385e67859f838","data":"0x552410770000000000000000000000000000000000000000000000000000000000000001","to":"0x99d0e6b5bfec73c8fd96b1c067617e1287a593cc"},{"from":"0x17b5065693f44e999d8eb692e41385e67859f838","data":"0x552410770000000000000000000000000000000000000000000000000000000000000002","to":"0x99d0e6b5bfec73c8fd96b1c067617e1287a593cc"}]]}'
{"jsonrpc":"2.0","id":1,"result":["0x52c8","0x52d4","0x52d4"]}
Caveats
As is always the case with gas estimation, some transactions may depend on
changes to the blockchain that are beyond your control. For example, executing a
trade with 0x depends not only on allowances being set, but the order having not
been filled. If someone partially fills the order while your transaction sits in
the mempool, the actual gas requirements could change from the estimation. For
transactions that depend on state that other people can change, it’s still
generally a good idea to leave some buffer on top of the estimated gas, but
estimateGasList
gets you a much better starting point (and any extra gas will
still be refunded at the end of the transaction).