Think Twice Before Using Enums in Solidity Smart Contracts

Oleg Kondrakhanov
Aug 18, 2019 · 4 min read

Imagine you are writing a Solidity smart contract and one of its properties can be characterized like type or state. In other words, something from a limited set of options. You immediately say to yourself: ”Great, I’ll just use enum type for this state variable.” On the one hand, this approach has some benefits like increased readability. On the other hand, it can easily take you down a tricky road potentially leading to problems.

Image for post
Image for post
Photo by Dewa Prabawa from Pexels

What are you talking about?

Well, everything is OK if enum members are encapsulated within only one contract and are never ever mentioned in other files. However, DApps usually consist of several contracts connected with each other. The problem I am talking about appears when the same enum:

For example, you have 2 contracts. The first one is a kind of storage for very important information. You also declare an interface with the enum definition to refer to it.

contract IStorage {    
enum RecordState {StateA, StateB}
function setState(address user, RecordState newState) public;
}

contract Storage is IStorage {
mapping(address=>RecordState) public states;

constructor() public {}

function setState(address user, RecordState newState) public {
states[user] = newState;
}
}

Each user’s record is represented with an enum of 2 possible options: StateA and StateB. setState function is able to change the user’s state. There is also another contract that end users are supposed to interact with (for simplicity I am omitting access control modifiers in Storage contract).

contract StorageUser {
IStorage public recordStorage;

constructor(IStorage _recordStorage) public {
recordStorage = _recordStorage;
}

function changeStateA() public {
recordStorage.setState(msg.sender, IStorage.RecordState.StateA);
}

function changeStateB() public {
recordStorage.setState(msg.sender, IStorage.RecordState.StateB);
}
}

Then you deploy those contracts to the blockchain.

Image for post
Image for post

Everything is OK: you call either changeStateA or changeStateB and Storage contract’s data gets modified accordingly via its own setState function. But one day you realize that you need a completely new state option for some brand new feature. You call it StateC (wow! what a naming!). First, you modify the source code by adding new enum member in IStorage

enum RecordState {StateA, StateB, StateC}

…and a new method for StorageUser.

function changeStateC() public { 
recordStorage.setState(msg.sender, IStorage.RecordState.StateC);
}

Moreover, as a responsible developer, you write tests that call the new method and they report success. Your plan is to redeploy only StorageUser contract and you don’t want to redeploy Storage, there is a lot of important data in the form of mapping that is quite hard to migrate. So StorageUser is redeployed with the current Storage as its constructor parameter. You call new changeStateC function ... and it fails.

Image for post
Image for post
Photo by Vadim B from Pexels

Wait, what?! Why? Tests were OK.

Source of the failure

You see, updated StorageUser knows about 3 members of the RecordState enum, but old Storage doesn’t have a clue about new StateC option. It can’t convert setState function argument StateC to its version of the enum and therefore fails.

Image for post
Image for post

What’s more, your tests might have fooled you because they used the updated version of both contracts.
Actually, you could even have read the warning regarding this issue in the official docs. The explicit conversion from integer checks at runtime that the value lies inside the range of the enum and causes a failing assert otherwise. The sad truth is sometimes you just overlook such things and bump right into those issues yourself.

Lessons to learn

First, in the case like described above it’s way better to replace enums with plain integers. Yes, they don’t look so good but the resulted structure is more reliable and expandable.

Second, don’t throw the whole idea of using enum fields away. If such field lies within one and only one contract, it is absolutely safe. It is also safe if you can ensure that all contracts using the enum are redeployed altogether in case of modification. Remember, the problems appeared when enum first was imported from IStorage to StorageUser contract and only the latter was redeployed after the modification of initial members.

Just don’t forget, that if you really want to use enum in your contracts, it’s better to think twice.

Try yourself

Source code of this example is uploaded to GitHub. I’ve reproduced the problematic behaviour in the test file. Feel free to inspect it and experiment yourself.

Get Best Software Deals Directly In Your Inbox

Image for post
Image for post

Coinmonks

Coinmonks is a non-profit Crypto educational publication.

Sign up for Coinmonks

By Coinmonks

A newsletter that brings you week's best crypto and blockchain stories and trending news directly in your inbox, by CoinCodeCap.com Take a look

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Oleg Kondrakhanov

Written by

Software developer. Ethereum and beyond

Coinmonks

Coinmonks

Coinmonks is a non-profit Crypto educational publication. Follow us on Twitter @coinmonks Our other project — https://coincodecap.com

Oleg Kondrakhanov

Written by

Software developer. Ethereum and beyond

Coinmonks

Coinmonks

Coinmonks is a non-profit Crypto educational publication. Follow us on Twitter @coinmonks Our other project — https://coincodecap.com

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store