When deploying new code to production contracts, you obviously can't destroy old account state, as you do during rapid prototyping. So how to you prevent the dreaded error?
You can use a couple different approaches, depending on the complexity of your contract.
As a reminder, the goal was to change this:
The NEAR Runtime looks at your current code as well as your contract's data, which is serialized and saved on-disk. When it executes the code, it tries to match these up. If you change the code but the data stays the same, it can't figure out how to do this. Previously we "solved" this by removing old serialized data. Now let's see how to update the data instead.
First, keep the old
struct around for at least one deploy:
And add a
migrate method to the main struct:
Need a refresher?
Click here to see the full diff between the starting contract and the update + migration.
When you deploy your change, call the
Finally, you can view old statuses with your new
At this point, all contract state has been migrated, and you don't need to keep the
OldStatusMessage struct or the
migrate method. Feel free to remove them and deploy again with no
initFunction call. Your contract will be all tidy and ready for the next migration!
In the example above, all contract state is stored in one simple struct. Many real-world contracts are more complex, often having one struct referenced by another. For example, a DAO contract might look something like this:
Say you want to update the structure of
Proposal but keep
The first thing to note is that the contract could be storing a huge number of proposals, which makes it impossible to migrate all of them in one transaction due to the gas limit. In an off-chain script, you could query the full state of the contract and update every single one of them via multiple transactions. But that may be prohibitively expensive, so you might opt to upgrade proposals to the new structure during the next interaction with them, rather than all at once (this disperses the upgrade cost to users of the contract).
In either case, your contract can end up with proposals using the original structure and the new structure at the same time, and the
DAO struct needs to know how to load both of them. How do you do that?
The example above is not tested and may contain bugs or be incomplete.
Someone (us? you??) needs to create a full example repository that clearly demonstrates this upgrade path, and link to it in the snippets above.
In the meantime, you can see working examples and learn more about this pattern at the following links:
If you plan to upgrade your contracts throughout their lifetime, start with enums. Adding them only after you decide to upgrade is (usually) possible, but will result in harder-to-follow (and thus more error-prone) code.