Production App Basics
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.
#
Migration methodFor cases like the change to the rust-status-message
contract that we looked at previously, a simple migration method is all you need.
As a reminder, the goal was to change this:
into 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 migrate
method:
Finally, you can view old statuses with your new get_tagline
method:
Hooray!
Tidying Up
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!
#
Using EnumsIn 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:
note
For a more complete DAO example, check out SputnikDAO, Flux, and others.
Say you want to update the structure of Proposal
but keep DAO
unchanged.
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?
Use enums:
Untested Example
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:
#
Writing Upgradable ContractsIf 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.