A Smart-er Contract: How DeFiner is Reducing Gas Costs by 85%

Mark Mackey
DeFiner Blog

--

It’s well known that most DApps on Ethereum have failed to attract large numbers of users, with many struggling to tackle scaling issues and uncontrollable gas costs. For decentralized apps to gain meaningful adoption, these challenges (among others) must be solved to provide a seamless user experience. Rather than waiting for infrastructure improvements to public chains like Ethereum, DeFiner is built to scale now due to an innovative approach of structuring smart contracts using factories & proxies. This saves gas costs on our platform by more than 85%!

The focus of this article is to explain the mechanics behind gas fees incurred from launching new contracts and to share a secure method we’re using to minimize these fees for DeFiner users.

If you’re not familiar with gas fees, a simple way to think about them is the cost that’s paid to miners (computers securing the Ethereum nework) for verifying transactions and storing the entire blockchain. The more storage a transaction requires, the larger the gas fee will be. This is crucial, as one of the biggest scaling challenges cryptocurrencies are dealing with is keeping the size of the blockchain under control. At the time of this writing, it costs roughly $1500 to store 1MB on the Ethereum blockchain. For DApps to be cost effective, they must minimize their storage footprint.

Many DApps require multiple instances of the same smart contract to be launched. These can easily be the most expensive transactions required by the DApp, especially if the duplicated contracts have lots of code. There are a few different ways developers might arrange for users to create new contracts:

  1. Rely on the user’s web3 provider to launch the contract on their own.
  2. The user sends a transaction to a contract implementing a factory pattern.

I include the first option only to stress that it should never be used. Having the user send their own contract creation transaction via their web3 provider potentially allows the user to replace the contract logic with new logic (of their choosing) before deploying the contract. If the newly created contract is supposed to implement any security logic at all, the user can completely subvert it.

So we’re left with the second option. What does it mean to implement a factory pattern? Factories are contracts that create new instances of other contracts. These new instances are often referred to as child contracts. There are two ways factories can create new child contracts:

  1. Calling new() from within solidity.
  2. Creating clones (also called proxies) that point to a “master” child contract.

Option 1 is easy enough to implement:

contract Child {
bytes32 public name;
constructor (bytes32 name_) public {
name = name_;
}
}
contract Factory {
address[] children;
function createChildContract(bytes32 name) public {
address newContract = new Child(name);
children.push(newContract);
}
}

Calls to new() allocate everything required to create a complete copy of the child contract. This includes allocating space for duplicating the instructions and additional space to store the state.

Option 2 exploits the DELEGATECALL opcode to create new child contract clones. The clones will behave exactly the same as the master contracts that they point to, except for the extra gas required for DELEGATECALL. This extra cost is non-trivial to calculate (see ethereum yellow paper appendix H). In our tests, this typically works out to be an additional ~760 gas each time a function was called on the clone contract. Here’s where the massive savings come in: unlike the first option, cloning does not require duplicating the child contract’s code for each new instance. This can save a fortune in gas costs depending on the size of the code and the number of times the clone will be called.

To save the most gas at creation time, the clone factory’s code should be as small as possible. EIP-1167 was finalized last month, and specifies a minimal bytecode implementation of a clone contract. Using this standard will save you the most gas and allow third party tools to recognize your clones as clones (for more information, refer to Optionality’s clone factory).

Following the above analysis, it was clear that using factories and proxies for DeFiner’s loan contracts would serve the best interests of users and save the most gas. In fact, our method ended up reducing gas fees by over 3 million Wei, a reduction of over 85%! Since loan contracts on DeFiner are only intended to be called by our users ~20 times, the extra gas incurred by DELEGATECALL was more than compensated by the savings at creation time. It’s important to note that clones aren’t always the preferred method, particularily for DApps that require hundreds of calls to the child contract from multiple users (like Bloom’s poll contracts). Always do the math beforehand to determine which method uses less gas for your use case.

Stay tuned for my next article on how you can save even more gas with clones using deterministic addresses and vanity address generators!

For more information about DeFiner:
- Join our Telegram
- Follow us on Twitter
- Follow us on LinkedIn
- Visit our Website

--

--