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:

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

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 empty Contract .

// ./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 🚀.