TLDR: I was having a bad time with Blockchain smartcontract’s development, and fortunately I had found solutions to save my ass. I’d like to share it with you, so maybe it could be useful to you also.
Blockchain is fun!
It’s a huge public database (chain of data blocks), which is being shared across many computers. Everyone can query data inside it, or add a new record by sending transactions. Everything is stored historically, so you can trace the changes easily.
With ethereum chains, it’s even funnier with SmartContract – a program that runs on chains. Developers can write and compile a program, and upload it into the chain. Then everyone can run it to read its data, update its data with transactions, or even use it to run another program.
Tips: You can use this tool Smartcontract UI to interact with smartcontracts easily.
I was working on many projects based on blockchains, mainly in decentralized finance. Many of them are programs (written with Solidity) that involve managing users’ account balance, allowing users to trade their assets, or to stake their assets to the liquidity pools and gain interests.
Problems with my development/deployment
However, I was usually facing a big problem: Smartcontracts in ethereum chains are immutable. It means once you deployed it on the chain, there is no way to change it. It works just like a contract we have in real life: you surely won’t change the contract’s contents once you signed it, if you want to, you’d need to have another contract. Same in ethereum chains, if we make changes in the contract code, we’d need to deploy it again to a new smartcontract.
And since a smartcontract has its data, when I deployed to a new smartcontract, I’d need to migrate all data from the old contract to the new one, while keeping support on both contracts until all users moved to the new contract.
Another problem I was having is the contract’s deployment. After finishing the code and passing all tests on the local environment, I have to deploy it to the test chains so I can test its communication with other contracts. And since the contract’s address will be changed each deployment, it’s a really big pain for me to update all the addresses on each deployment.
After the contract’s deployment, I have another problem with the interaction with my contracts. I provide ABIs (Application Binary Interface) to other people, so they can use them to interact with our contracts. It’s also needed for me to write applications that interact with contracts, so I’ll need to keep them always updated with my deployed contracts.
SmartContract’s development is fun, but its deployment was a pain for me for a long time.
Writing Upgradable smartcontracts with OpenZeppelin
I’ve decided to improve my deployment process. Thanks to OpenZeppelin, I’m now able to upgrade my contracts smoothly.
When I deploy the contract on the first time, OpenZeppelin will create a Proxy Contract, points it to my actual contract, and finally, it deploys all contracts. Later, every time I make changes to the contract’s code, OpenZeppelin will deploy it, and points the Proxy Contract to the newly deployed contract, keeping the old contract’s state. Now I don’t have to worry about the migration between contract’s deployments. Our users and devs always connect with the Proxy Contract address, which points to the latest deployed contract.
It was a good change, that helped me during the release process. However, I was still having other problems with the deployment: I’d still need to build the contracts and deploy them to the chains. It requires access to my deployer’s wallet.
Automatic deployment with Github Actions and Workflows
After many tries, I finally integrate my build & deployment process with Github workflows. I also added some tweaks (like caching dependencies for faster build in future, configuring Truffle environment correctly, having wallet’s private key in Github’s secret,..)
My deployment is automatic now! Every time I merge features into a development branch, a workflow will be triggered to build the contracts and upload them into the test blockchain. And when they are ready for production, I just need to tag the version. Github will deploy the contracts into the production blockchain.
I also release the contract’s built artifact to Github release page every time I release the contracts to production, along with the prerelease of the contract’s ABI on each push to the development branches. In this way, users and devs can always update the contract’s ABI quickly and easily.
Memorize Truffle’s migration process
Everything seems OK, but actually, I’m still missing a piece: I use actions/cache@v2 to cache the Truffle’s build. So in the next deployment, I can continue the migration without doing it from the beginning.
However, the cache will be removed after some inactivity time, and when it’s removed, or if there is some problem in the cache (cause of a wrongly configured deployment), our migration process will be restarted from the beginning. Therefore, all the proxy contracts will be changed. I will need to notice our users and devs, and migrate all contract’s state.
Again, I had to try other approaches, and finally, I found a solution: Saving the Truffle’s build on a deployed branch. So for each deployment, instead of taking the previous build from the cache, I will pull it from the deployed branch, and continue with it. It has some more advantages:
- The Truffle’s build will be there always
- I can inspect the Truffle’s build if there was any problem with the deployment
- I can also customize the deployed branch to give more informations
Here is an example repository with this workflow: https://github.com/ActionsHackathon21/deploy-upgradable-smartcontract-to-blockchain