Or, more accurately, how to build a Blockchain Graphical User Interface (GUI) in Electron. This is the story of how Nelumbo came to life, why I wrote the app in JavaScript, and what I learned along the way.

First, what is Nelumbo? Nelumbo is the first desktop interface for the Filecoin local devnet version of their Lotus blockchain. That was a lot of words to say it makes developing Filecoin applications easier. Nelumbo has a one-click install and start process for the local developer version of the Filecoin blockchain. It includes the ability to start an IPFS node alongside it and developers can configure the Filecoin Lotus configuration right from the app, without touching the command line.

Why did I build this?

There’s an interesting thing that happens with new technology, especially in blockchain. It goes through some pretty standard phases:

  • Phase One: Theoretical research
  • Phase Two: Rough implementation with very difficult interfaces
  • Phase Three: Full command line user interface

  • Phase Thirty: A simple, easy-to-use interface that appeals to all developers and makes their lives easier.

I kid, of course. But the general flow of development of new technologies is a lot like what I’ve outlined. Fortunately, Filecoin, even with just launching their mainnet in October, was ready for the final phase. I know they were ready because I was building products and features that leveraged Filecoin’s blockchain and I knew I was ready for easier tools.

Simply put, unless you are a code engineer on the underlying blockchain, you probably don’t want to go through sixty different command-line only steps just to get a development environment up. Sure, you’ll deal with that to launch your product Filecoin node. That part isn’t too different from traditional DevOps. However, early in building an app, speed is crucial. The iterative process of early-stage development doesn’t jive with the complexity that often comes with launching blockchain test environments.

So, I got to work fixing this. I was inspired by the Truffle team, who built the Ganache desktop client for Ethereum and are working on their own Filecoin desktop client now. Having their fantastic work (which I used frequently when building Ethereum-based products) as a template helped me a ton. I was able to scope Nelumbo properly because I’d seen what they did with Ganache.

I probably was a little stupid in the fact that I forgot Ganache’s code base is open-source and as I ran into problems building Nelumbo, I could have used their code to help me find the solution, but by fighting through the problems myself, I learned a lot about the limitations and workarounds necessary to build a GUI on top of a product designed to run from the command line.

So, with that in mind, let’s dive into why I used Electron to build this app and what I learned along the way.

Why Electron?

At the time of this writing, Filecoin’s Lotus blockchain does not support Windows. So, the local devnet could be run on Linux or macOS. Since I have a MacBook, the decision to build a standalone Mac app made total sense. But that means I could have used Swift and built it natively with Xcode and Apple’s available developer tools. Only, that’s not entirely true. It’s true in theory, but in practice, I had 0 experience building with Swift. That’s changed recently, and I enjoy building apps with SwiftUI very much, but when I started this project, my options were limited to JavaScript-based frameworks that could be built as native apps. There are a few options out there, but since I had experience building an app called Write/Sprint in Electron, I knew that’s what I’d use for this app.

The big question was: How would I execute all of the necessary command line arguments, installations, and configurations…from NodeJS? Enter child_process. NodeJS gives us access to the system-level command line through child_process. This process exposes a few methods, including execSync. As I built, I made heavy use of execSync.

The interface came along easily. It’s a React app that connects to Electron. But as I started building out the connection to the command line, I realized I was writing a lot of functions that called execSync. I could simply write a couple shell scripts and boom, I’d be all set.

Eh, not quite. The shell script idea was a good one, and it’s what I ultimately used. But the first problem was understanding what dependencies a user needed to have installed to run the local devnet. I needed these dependencies communicated back to the front-end so I could alert the user that dependencies were being installed. And since these dependencies were dynamic, I had to write individual functions for each dependency installation.

Someone much smarter than me with shell scripting probably has a better solution, and quite honestly, I hope to see some pull requests on the Nelumbo repository that make it better.

Once I sorted the dependency installation out, I ran into another problem. On Mac, some users might be using bash and some might be using zsh. To execute my shell scripts, I would need to make sure I executed my scripts with the proper prefix. That meant another check on the user’s machine to determine if they were using bash or zsh.

Ok, now that I got that out of the way, I could finally run my scripts! Sort of. I mentioned earlier that one of the key features of Nelumbo was the ability to visually edit the configuration of the local Filecoin Lotus blockchain. Those configuration settings are hosted in a config.toml file. That file is completely erased and re-written when executing the start script for the Filecoin devnet. Ugh.

After some experimentation, I realized, I could grab the configuration provided by the frontend react app, store it in a temporary file, wait for the start script to kill the existing config.toml file, then move the temp file back into the proper directory before the chain started up. Again, I’m sure someone much better at shell scripting has a better solution, but this works and I’m happy with it at the moment:mkdir ~/.tempLotus && mv ~/.lotus/config.toml ~/.tempLotus/config.toml && rm -rf ~/.lotus && mkdir ~/.lotus && mv ~/.tempLotus/config.toml ~/.lotus/config.toml && rm -rf ~/.tempLotus &&rm -rf ~/.lotusminer &&cd ~/lotus &&
...

Ok finally, the app could start, and with one-click, a user could start their local Filecoin devnet. It was time to ship this bad boy.

Whoops. Turns out, when packaged, there are a lot of things that don’t work that did work locally. I always tell myself when I start a new project that I’m going to build for production and test a production build early and often. And invariably, I don’t do that. One of those things bit me pretty hard and took a long time to solve.

When an Electron app is built and packaged, the accessible $PATH is different from the user’s normal machine $PATH. This is a problem when you are installing dependencies based on a user’s $PATH. Go, for example, needs this to be exactly right or the whole world blows up.

Fortunately, after some digging, I discovered a library that would fix the $PATH and allow you to use the user’s $PATH as expected. Naturally, this library is called fix-path. This solved my problem and we were off to the races.

Electron, I think, was the right tool for this job. I would be interested in what this looks like in Swift, but my strengths are still very much in JavaScript-land. Electron is flexible, easy to understand, and powerful. It comes with its downsides, for sure, but those downsides, in my opinion, do not outweigh the benefits.

I learned after the fact that the Truffle team built Ganache in Electron and that made me feel a lot better about my decision. All in all, building a GUI on top of a piece of software designed to run on the command line was a great experience. It was challenging and ultimately solved the problems I faced while building storage client apps for Filecoin. Hopefully, Nelumbo can help solve some of your problems too.