The latest version of Solidity kickstarted a season of upgrades in the BUIDL space. The latest version of solidity essentially meant that all your smart contracts are now broken. It all seemed ok though; just a couple day’s work to get caught up. Then, just before Christmas came Truffle 5 with support for solidity 0.5 and web3 beta, and guess what? Now all your tests and migrations are broken as well. Happy holidays!
I’ll try to list the challenges we came across while upgrading and how we solved them. I hope that this will help other devs save some time while upgrading their Solidity/Truffle projects.
The whole ethereum ecosystem and developer tools are in a nascent stage, but at least they were somewhat explored and tested. They were like known unknowns. The latest updates, on the other hand, felt like unknown unknowns.
We started fixing all the syntax errors, incompatibilities and whatnots one by one. Just like how little drops of water make the mighty ocean, our small-small fixes made the refactored code a reality. We have done all these changes in the
dev-3.0.0 branch of Polymath-core.
The first task was to set up our work environments to work with both older tools and newer tools. We did this by installing Truffle 4 globally while installing Truffle 5 locally. We also had to update our truffle-config.js accordingly. You may read more about this in my previous blog post about compiling smart contracts faster using native solc.
The next challenge was to refactor our Solidity 0.4.24 code to work with solidity 0.5.X. There are quite a few breaking changes in Solidity 0.5 that are mentioned in the official docs. As you may have noticed, most of the required modifications are due to the new explicitness requirements. Most of these changes can be done automatically, so we decided to create an AI (a few if-else statements) that does most of the work for us.
The refactoring tool was mentioned in a week in ethereum edition, and you should try it on your projects. More details about its features and usage instructions are available on GitHub. It is essentially a plugin for the famous code formatter, prettier, that refactors Solidity 0.4.X syntax to Solidity 0.5.X syntax while beautifying your code.
There are limitations to what a tool can do (mentioned on GitHub), and we had to make a few changes manually as well. For example, we had to write a new function to extract the function signature from bytes data as the earlier version used a casting that is no longer allowed.
Now that we had our contracts compiling with Solidity 0.5, we had to make them work on our CI as well. To simplify this, we created a docker imagewhich had everything we need pre-installed. You can grab it from docker huband use it in your projects. The Dockerfile is available on GitHub if you want to customize the image. Feel free to pick a thing or two from our CircleCI config on GitHub.
So we now have our CI running to remind us that all our migrations and tests are broken. Truffle 5 uses web3 1.0 beta rather than web3 0.20 that Truffle 4 uses. The main changes in web3 1.0 are that it uses BN.js instead of BigNumber.js and it has a lot more going on with promises. We started refactoring our migrations for the latest version of web3 but soon came across a bug that breaks web3 when using huge numbers. A workaround is to use an older version of web3 like 1.0.0-beta.35.
Refactoring our test cases and migration scripts was a long and tedious task that took us days to complete. We have some tips for you that may help speed up the process:
- Use regex. You can search and replace a lot of old syntax to new syntax by using patterns detected by regex.
- BN can be converted to a string using
toString(). This will help you assert conditions on BNs. You may also use bn-chai.
- You can not directly add numbers to BN. You’ll need to convert the number to BN and then use
addmethod of BN. Same applies for other mathematical operations.
- You need to pass full addresses of ethereum accounts now rather than just partial like
- You need to convert ASCII to Hex before passing it in bytes datatype. web3 does not automatically convert it anymore.
- Loading contract instance in truffle using
.atnow returns a promise so you need to use
awaitor handle the promise.
After days, we finally had all our migrations and tests working as expected. You’d think that everything is working now, but no… there’s more stuff to do. solidity-coverage does not support Solidity 0.5. The main problem was that the parser wasn’t updated to support the latest Solidity version. The community came through on this one, and with a combined effort, we were able to fix solidity-coverage unofficially. More info on this GitHub Issue.
You can mostly fix it by using the updated parser. To fetch the updated parser, use:
curl -o node_modules/solidity-parser-sc/build/parser.js https://raw.githubusercontent.com/maxsam4/solidity-parser/solidity-0.5/build/parser.js
Feel free to fork my repo so that you can download it from your repo directly. You’ll need to replace this file every time you add an npm module. You’ll also need to make it a part of your CI if you run coverage on your CI.
We finally had everything working! Or did we?
During the days we were refactoring the code, we were also working on the older code to fix bugs/add features. Now, we’re in the process of solving the gazillion merge conflicts it created.
As we were one of the first teams trying out the tools against Solidity 0.5, we came across numerous bugs among various tools. We created pull requests to fix a few of them in tools like solidity-coverage, solidity-parser, solidity-parser-antlr, prettier-plugin-solidity, and solidity-docgen. A lot of other bugs in other tools were fixed by their respective maintainers. Most of the developer tools are now considerably more polished than when we started refactoring our code.
We hope that it will be a bit easier for you to update your smart contract code, tests, and deployment scripts now. Happy refactoring!