How the standardized usage of Open Zeppelin smart contracts cannibalized NFTs with insane gas prices.

Chance
Coinmonks
Published in
7 min readNov 7, 2021

--

OpenZeppelin, whether intentionally or not, directly contributes to the bear market and low-quality NFT projects.

For weeks the NFT industry has been screaming “GAS PRICES ARE SO HIGH ETHEREUM IS BROKEN.”

Let’s slam the brakes and look into that.

You’ll find, things are not what you’d expect. Instead of having an efficient method of management for your favorite NFTs, developers are assembling pieces often without much consideration of the consequences that come by doing so. With random Open Zeppelin and copy-pasted pieces slammed together many of the most popular collections are the most poorly written contracts within the industry.

Today we are going to dive into that and realize that giving inexperienced developers millions of dollars is not a preferable reality. Credit for bringing this to my attention goes to squeebo_nft and the GoldenXnft team.

For the uninitiated, Open Zeppelin is an open-source library that has many popular smart contracts that are used throughout the NFT industry.

It’s a great entry into Solidity and takes a ton of the guesswork and initial struggle out of your first days...

… but as a developer one will often hear not to use libraries not fully understood...

Why?

Because more often than not there is a pile of things you don’t need or care about for your specific use. That’s what happened to us in this cycle.

For the duration of this article we are using:

OpenZeppelin version 4.3.2 and Solidity 0.8.4.

Let’s get right to it and hit the ground running with a 🧪 test mint of:
A 1 token transaction and then a 5 token transaction.

Gas usage report from hardhat in USD.

The results immediately illustrate the issue…

Minting w/ Open Zeppelin: $204.83 ($34.13/mint)
Minting w/ Optimized Contract: $80.24 ($13.37/mint)

That is a massive difference.

Now, it’s important to keep in mind that within this test we have a mint of 1 and a mint of 5. Although we are minting different amounts, the gas used by these mint transactions does not scale linearly.

Each time we run the transaction we check that the requirements are met regardless of the count, but if we have a count that is higher than one we don’t need to check the requirements each time and thus the gas used per mint on higher quantity mints is lower. Super simple, right?

So, what’s the issue here?

Out of the box, Open Zeppelin ERC721Enumerable comes with an inordinate amount of transfer processing that simply is not needed for the majority of projects. What’s this processing?

To break this down as simply as possible: Not only is gas wasted on minting but also every single time the token is transferred… The owner of the token is paying a fee that’s magnitudes higher than it should be. Why? Inexperienced developers. So, the next time you wonder: “Why does it cost $90 to transfer this token?” you almost assuredly have your answer.

Instead of attempting to manage the cost within the transfers, the method of tracking can be moved into the view functions. Generally, a project contract will need a few modifications to fit this method but it’s quite easy to get rolling.

There are three primary functions developers use ERC721Enumerable. Let’s make sure we handle those now and you can decide how in-depth you want to get with this. So, instead of using the version supplied by Open Zeppelin let’s create our own and make sure we have these basic functions:

Notes: What you will notice immediately is that tokenOfOwnerByIndex() is extremely inefficient. That is a sacrifice we are making to save our buyer's small fortunes.

We aren’t just removing a few lines, we have to rework things to quite a significant degree so you will want to make sure and test every square inch before moving anything into production. For reference, this was the starting point of tokenOfOwnerByIndex()

Incredibly, this is not where cost optimizations stop when using Open Zeppelin. We can take this entire analysis one step further and look at the core ERC721 contract.

Immediately we can see that we have quite a few redundancies that we can optimize out. Let’s focus on one key one though:

If you’re a developer you know the idea of storing the same piece of data twice is quite a bad practice immediately. But, even if you’re not let’s explain why.

In this contract, we also have the variable:

And while we have the owners we also know the totalSupply() of the collection at any given time. This means, we already know the balances inherently. We just have to properly manage the data so that we don’t need to store and use redundant information, especially when it costs more!

So, if we want to iterate through our owners we instead manage our owners in array format. Immediately, we’ve removed one mapping that we have to manage and we’ve replaced one mapping with an array of addresses. This is not a massive change, but it does have massive impacts.

With a final structure determined it is time to go through and update everything like:

And with this we will go ahead and update the management of these owners like:

Even for the general creator, this is not a moot effort. There are two more major things we need to take a look at first before we can say that:

👉 The amount of gas you are actually saving the ecosystem.
👉 The money that can be saved during deployment.

As creators, we have to see the value in saving our community multiple hundreds of thousands.

In a project that sells 10,000 tokens with absolute zero gas war, several hundred thousand dollars stay 🏝 in-ecosystem. If you have a gas war, that can become millions.

Every single penny counts.

With one round of optimization, we can lower the cost of deploying to the Ethereum mainnet from $2,004 to $1,760 with a few minutes of focus…

The final cost of deploying an on-chain generative project using byte hashes.

Goal Achieved: 💪 Lowering creator risk one step further and decreasing the cost of minting for every single buyer by at least 50% even during a gas war.

Was relying on Open Zeppelin to pick up the slack worth it? Was the time saved not learning what was in the smart contract worth the millions lost in gas and stagnated projects? Do you know a fix to this? Is it worth it to make a wrapper for projects on a bad contract?

Can we go back and undo time?

This is not Open Zeppelin's fault though. So many times they stress to understand what is going into your contract, especially before moving to production…

Here is a basic checklist to prevent this issue in your project:
✅ Check everything that happens during the time of transfer.
✅ Limit the amount of data storage you force a minter to pay for.
✅ Move expensive processing to view functions when possible.

The industry is ripe and that allows crazy mistakes like this one to tarnish an entire industry for an entire 🔁 cycle.

Join Coinmonks Telegram Channel and Youtube Channel learn about crypto trading and investing

Also Read

--

--

Chance
Coinmonks

PLEASE STOP TRYING TO HIRE ME. IF I WANT TO WORK WITH YOU, YOU WILL KNOW.