Imagine you are writing a Solidity smart contract and one of its properties can be characterized as a or . In other words, something from a limited set of options. You immediately say to yourself: 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. type state ”Great, I’ll just use enum type for this state variable.” What are you talking about? Well, everything is OK if 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 enum Appears in more than one contract and Is modified during the DApp lifecycle. For example, you have 2 contracts. The first one is a kind of for very important information. You also declare an interface with the enum definition to refer to it. storage contract IStorage { enum RecordState {StateA, StateB} { mapping( RecordState) public states; () public {} { states[user] = newState; } } ( ) ; } function setState address user, RecordState newState public contract Storage is IStorage => address constructor ( ) function setState address user, RecordState newState public Each user’s is represented with an enum of 2 possible options: StateA and StateB. 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 contract). record setState Storage contract StorageUser { IStorage public recordStorage; (IStorage _recordStorage) public { recordStorage = _recordStorage; } { recordStorage.setState(msg.sender, IStorage.RecordState.StateA); } { recordStorage.setState(msg.sender, IStorage.RecordState.StateB); } } constructor ( ) function changeStateA public ( ) function changeStateB public Then you deploy those contracts to the blockchain. Everything is OK: You call either or and contract’s data gets modified accordingly via its own 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 … changeStateA changeStateB Storage setState IStorage enum RecordState {StateA, StateB, StateC} …and a new method for . StorageUser { recordStorage.setState(msg.sender, IStorage.RecordState.StateC); } ( ) function changeStateC public Moreover, as a responsible developer, you write tests that call the new method and they report success. Your plan is to redeploy only contract and you don’t want to redeploy , there is a lot of important data in the form of mapping that is quite hard to migrate. So is redeployed with the current as its constructor parameter. You call new function ... and it fails. StorageUser Storage StorageUser Storage changeStateC Wait, what?! Why? Tests were OK. Source of the failure You see, updated knows about 3 members of the enum, but old doesn’t have a clue about new StateC option. It can’t convert function argument StateC to its version of the enum and therefore fails. StorageUser RecordState Storage setState 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 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 are redeployed altogether in case of modification. Remember, the problems appeared when first was imported from to contract and only the latter was redeployed after the modification of initial members. enum enum enum IStorage StorageUser Just don’t forget, that if you really want to use in your contracts, it’s better to think twice. enum Try yourself Source code of this example is uploaded to . I’ve reproduced the problematic behaviour in the test file. Feel free to inspect it and experiment yourself. GitHub Credits Image of people in the forest. Photo by from Dewa Prabawa Pexels Image of a kitten. Photo by from Vadim B Pexels