Building Cryptocurrency Agnostic Components in My-Wallet-V3 and My-Wallet-V3-Frontend
At Blockchain, we’re striving to write reusable, extendable, and flexible code. This practice has become even more important with the recent addition of Bitcoin Cash and ether to our wallet application. For our users, adhering to writing agnostic components makes project delivery speeds faster and more consistent. For our developers, it makes the code fun to work with and less buggy.
A few months ago, we added ether to the Blockchain wallet along with a crypto-to-crypto exchange integration. This feature was the first we’ve built that supports multiple cryptocurrencies (instead of just bitcoin to fiat or vice versa). In our efforts to build reusable, extendable, and flexible code we built it to support any digital currency our wallet currently offers or will offer in the future.
The components that make up our exchange integration are completely agnostic to the currencies the user wishes to shift. Any digital currency can be plugged into the integration as long as it’s “API” in the My-Wallet-V3 core supports a few basic functions. Let’s walk through how this works a little bit.
When the wallet initializes the exchange checkout process it passes the destination and origin wallets to the shift-create component from the shapeShiftCheckout controller:
My-Wallet-V3-Frontend/assets/js/controllers/shapeshift/shapeShiftCheckout.controller.js
...
this.wallets = Wallet.accounts().concat(Ethereum.defaultAccount);
...
And in My-Wallet-V3-Frontend/app/partials/shapeshift/checkout.pug
...
shift-create(wallets="vm.wallets")
...
Note: There are more attributes passed to the shift-create component, but for simplicity I’ve removed them.
The line in shapeShiftCheckout.controller.js (`this.wallets = Wallet.accounts().concat(Ethereum.defaultAccount);`) was for shifting between bitcoin and ether when we initially launched the ether integration. What it does is concatenate all the bitcoin wallets in your Blockchain wallet with the ether default wallet. In checkout.pug, we tell shift-create to load these wallets as possible origins and destinations in an exchange.
A few months later, we added full support of Bitcoin Cash to our wallet. We wanted to give users the ability to exchange their Bitcoin Cash to and from bitcoin and ether. Here is the commit that added Bitcoin Cash to our exchange integration: https://github.com/blockchain/My-Wallet-V3-Frontend/commit/0c44b5bca7951a9a298d5599d5fd872b0eff5de5.
-this.wallets = Wallet.accounts().concat(Ethereum.defaultAccount);
+this.wallets = Wallet.accounts().concat(Ethereum.defaultAccount).concat(MyWallet.wallet.bch.accounts);
Adding Bitcoin Cash was as easy as changing one line of code because we built cryptocurrency agnostic components for exchanging bitcoin <-> ether from the beginning.
If you check the shift-create component where the wallets are passed, you won’t find any reference towards a specific cryptocurrency itself: (https://github.com/blockchain/My-Wallet-V3-Frontend/blob/master/assets/js/components/shapeshift/shift-create.component.js). You’ll only see functions called on the wallets that the API in My-Wallet-V3 core requires them to have.
- `getAvailableBalance` -> How the component calculates the total amount the outgoing wallet has available to send/exchange. Below are the functions for each cryptocurrency:
- bitcoin: https://github.com/blockchain/My-Wallet-V3/blob/master/src/hd-account.js#L311
- ether: https://github.com/blockchain/My-Wallet-V3/blob/master/src/eth/eth-account.js#L138
- Bitcoin Cash: https://github.com/blockchain/My-Wallet-V3/blob/master/src/bch/bch-account.js#L68
- coinCode -> The wallet’s coinCode to be used later in the cryptoCurrencyMap.
- bitcoin: ‘btc’
- ether: ‘eth’
- Bitcoin Cash: ‘bch’
- Balance -> Used to check for balance updates in the wallet. If a user’s balance changes, the component calls getAvailableBalance.
- cryptoCurrencyMap -> Used to calculate conversions between each digital currency and their fiat values.
When everything comes together, the final result looks like this:
Despite the ease of adding new currencies, we wanted to continue improving the exchange experience for our users.
In version 1 of our exchange integration, there was no loading state to tell users that the component was working on getting a quote. Under good network conditions requests like these can seem instantaneous and a loading state might even seem a little jarring. However, because of the high traffic on digital currency networks, requests can take up to 5 seconds to resolve. Bitcoin, ether, Bitcoin Cash, and other digital currencies are handling more transactions than they ever have before. Record high transaction volumes are leading to network congestion, in turn leading to slow loading times from services like ShapeShift.
Depending on how long a request took to make, it could look like the component was frozen or that something broke and no error was returned. In reality, a request was being made and we weren’t correctly indicating that to the user. We decided to add a loading state to the component as you can see in version 1 vs version 2.
Users were relieved to see an indication of the app working to get a quote after the launch of version 2. Our transaction volume increased by 10% the week after we rolled out these changes, ~30% over the 2 weeks after, and 75% over the following month. Seemingly small improvements like this make a big difference to users.
Building components that are resilient to cryptocurrency’s rapid growth and constant change is just one of the many ways we ensure our users are getting a best-in-class experience on our platform. We hope you’ve enjoyed learning a little more about our process and look forward to continuing to deliver new and innovative solutions in our effort to create an open, fair, and accessible financial future.
Sound exciting? Check out our open positions available to join our incredible UX development team.