Ethereum Gas Exactimation

Cruz Molina
Truffle Suite
Published in
4 min readJun 21, 2019

Co-authored by Nicholas Paterno & Cruz Molina

In Ethereum, estimating gas for a given transaction is a tricky problem to solve (especially when attempting to maintain EIP-114 compliance). Most of the well-known Ethereum implementations like Geth¹ and Parity² use interval halving (binary search) to estimate gas by running transactions through the EVM until an approximate estimation converges.

At Truffle, we decided such an approach was an unnecessarily CPU-intensive solution to the problem and set out to find a (theoretically) more performant and perfectly accurate way of estimating gas. The result? Gas exactimation. In this tutorial, we’ll go over gas exactimation at a high level in order to demonstrate its precision.

Complete example available at the following repo.

Prerequisites: You should be familiar with Truffle, Ganache, and Solidity. If you need an introduction please consult the following resources:

EIP-114, or the “1/64ths rule”

EIP-114 mandates that certain stackdepth-creating opcodes withhold 1/64th of remaining gas from the stack they create. In practice this means:

  1. The gas required for a successful transaction can be greater than the actual gas spent (similar to how gas refunds behave).
  2. The extra gas required for a successful transaction varies depending on the transaction’s initial gasamount.

A long-standing issue with Ganache has been the fact that we haven’t returned EIP-114 compliant gas estimations. This has caused our gas estimates to be too low in cases where a transaction executed certain opcodes. Gas exactimation addresses this by considering how the gas withheld at any nested stack depth/frame affects the gas needed outside of its execution context.

Let’s see it in action.

Create a New Truffle Project

We will use truffle init to create a new Truffle project and then wire up an example Solidity smart contract and test script.

// In a new project directory...
$ truffle init

Setup an Example Smart Contract

With our new project initialized, we will create an example file: ContractFactory.sol.

$ touch ./contracts/ContractFactory.sol

Our file will have two contracts, Contract and ContractFactory. ContractFactory will have the method createInstance that we will use to create a new emptyContract.

// ./contracts/ContractFactory.sol
pragma solidity ^0.5.0;
contract ContractFactory {
function createInstance() public {
new Contract();
}
}
contract Contract {
constructor() public {}
}

Note the new keyword being used to create a new Contract. A valid statement containing the new keyword gets compiled to bytecode containing the CREATE opcode which is subject to the EIP-114 1/64th gas withholding.

Write a Test Case

Next, we will write a ContractFactory test case.

$ touch ./test/ContractFactory.js

This test case will deploy ContractFactory to a Ganache test network and use a gas estimate provided by Ganache to create a new Contract.

// ./test/ContractFactory.js
const ContractFactory = artifacts.require("ContractFactory");
contract("ContractFactory", () => {
it("...should deploy and successfully call createInstance using the method's provided gas estimate", async () => {
const contractFactoryInstance = await ContractFactory.new();
const gasEstimate = await contractFactoryInstance.createInstance.estimateGas(); const tx = await contractFactoryInstance.createInstance({
gas: gasEstimate
});
assert(tx);
});
});

A Quick Check

Before we run our test, we’ll download the most recent version of Truffle that uses Ganache before gas exactimation.

// In the project directory...
$ npm i truffle@5.0.13

And we’ll make sure we have the latest version of Truffle installed globally that uses Ganache with gas exactimation.

$ npm i -g truffle

Testing Before and After

When we run our test using Ganache before exactimation, notice our createInstance transaction reverts!

$ npx truffle test......Error: Returned error: VM Exception while processing transaction: revert

And when we use gas exactimation…

$ truffle test......Contract: ContractFactory✓ ...should deploy and successfully call createInstance using the method's provided gas estimate (130ms)1 passing (143ms)

!!!!

Testing Exactimation

But is gas exactimation actually exact? 🤔

We’ll open our test file and subtract exactly a single unit of gas from the gasEstimate before sending our test transaction.

// ./test/ContractFactory.js
const ContractFactory = artifacts.require("ContractFactory");
contract("ContractFactory", () => {
it("...should deploy and successfully call createInstance using the method's provided gas estimate", async () => {
const contractFactoryInstance = await ContractFactory.new();
const gasEstimate = await contractFactoryInstance.createInstance.estimateGas();const tx = await contractFactoryInstance.createInstance({
gas: gasEstimate - 1
});
assert(tx);
});
});

Running our test again against Ganache with gas exactimation…

$ truffle test......Error: Returned error: VM Exception while processing transaction: revert

Exactimation confirmed 🚀.

For a deeper dive into gas exactimation, join us for TruffleCon 2019 August 2–4 at Microsoft’s campus in Redmond, WA! Get Tickets

Note: Since the initial release of gas exactimation, an even more performant iteration of the algorithm is currently in review here.

[1]: https://github.com/ethereum/go-ethereum/blob/master/accounts/abi/bind/backends/simulated.go#L267

[2]: https://github.com/paritytech/parity-ethereum/blob/master/ethcore/src/client/client.rs#L1658

--

--