Estimate Gas when using Oraclize

September 12, 2017

Ethereum transactions require you to pay a fee that is measured with gas. Every instruction executed by the EVM costs a certain amount of gas, so before queuing your transaction, you have to specify a gas limit and a gas price. That gas is paid in ether, so for example, if your transaction has a gas limit of 82470 and your gas price is, say, 1 Gwei (0.000000001 ΞTH), then the cost of the transaction would be:

82470 x 0.000000001 = 0.00008247 ΞTH

If your transaction spent more gas than your gas limit, it gets reverted (i.e., changes to the contract state are not applied, although the transaction is kept in the blockchain), and you lose the ether spent on it. This is by design, and it is meant to avoid spam in the chain: spamming it will cost you real money.

Good news is that if the transaction is successful, you will get refunded with the amount of gas that was not spent. In the example above, let’s say the transaction used 54980 units of gas, then you would get refunded:

0.00008247 - (54980 x 0.000000001) = 0.00002749 ΞTH

In other words, the actual transaction cost was 0.00005498 ΞTH. The lesson here is: it’s OK to overestimate the gas limit because you will be refunded any extra gas not consumed.

Oraclize

The EVM is deterministic by design. This means, for example, that there is no way to generate a random number because to reach consensus each miner has to get the same result after executing the contract’s code. That’s also why there is no way to do HTTP calls from within a smart contract.

So we need “oracles”, which are 3rd party services that allow you to introduce randomness or external sources data into the blockchain.

Oraclize is one of the most popular oracle services. The idea is simple:

You make your contract extend a super contract provided by them

You make queries to data sources (e.g. “URL”, “Random”, “WolframAlpha”, and more), which are transactions made against Oraclize’s contract

They detect queries, perform them, and call your contract back

The callback in your contract receives the requested data, and then you do something with it

Which brings us to their pricing model: before you query their contract, you need to make sure you have enough ether in your contract to cover the fee. Prices depend on the type of query (e.g. 0.01 USD for URL). But there is more to it: because Oraclize will call your contract back, that transaction will also consume gas, so you must pay for that upfront when you call oraclize_query.

There is big caveat here, from their docs:

If no settings are specified, Oraclize will use the default values of 200,000 gas and 20 GWei.
…
Smart contract developers should estimate correctly and minimize the cost of their __callback method, as any unspent gas will be returned to Oraclize and no refund is available.

In other words, overestimating the gas limit means you will lose money. And that is neither good nor fair, but it seems like there’s no way around it, except of course to estimate correctly.

Estimating gas limit

web3.js comes with a handy function called estimateGas that executes a message call or transaction, which is directly executed in the VM of the node, but never mined into the blockchain and returns the amount of the gas used.

Now, that seems like something very useful. But be aware that the estimated gas could change depending on many factors, like the current state of the contract or the input values of the function.

Let’s take a look at an example. Imagine you have an array in a smart contract that you want to shuffle. So we could use the KFY algorithm to do this, except that we do not have a way to generate random data from within the smart contract. We could, however, use Oraclize to provide us with a random string that we can then use to effectively introduce entropy in the contract. Let’s see the code:

The latter requires a structural change to the contract. oraclize_query receives as a third parameter a custom gas limit. Note, however, that in this particular case we can’t just hardcode the gas limit because depending on the size of the array we want to shuffle, we can consume more or less gas.

We abstracted the code of the callback into myFunction and added estimateCostOfMyFunction which should consume almost the same gas as the oracle’s __callback. Then, from our dApp we could have something like this: