Seed - Development - DApp Launcher, Module Loading, High Level API & Seed DApp
Repository
https://github.com/Cajarty/Seed
Overview
The following development changes were based upon the design article DApp Launcher. This pull request includes functional, unstyled UI's for the launcher, the Seed module and the Relay module. It also includes dynamically loading modules, a High Level API (HLAPI) implementation for Seed DApps, and functional implementations of the Seed & Relay modules with their UI's.
To reiterate, this pull request was not focused around creating styled UI's, but instead about creating simple UI's which are functional and genuinely communicate with the Seed ecosystem.
Terminology Note: A "Module" is similar to a "Smart Contract" in Ethereum, while a "DApp" is an application which uses Modules. "Module" refers strictly to the raw code interpreted by a virtual machine, while a DApp includes an application and interface users interact with.
DApp Launcher
The DApp launcher is created using NodeJS and Electron. It dynamically loads modules, pairs them with the virtual machine, can launch the dynamically loaded UI's for their modules, and offers a high level API for these DApps.
Due to the nature of Electron, all communication between the Launcher window, DApps and Seed (through the API) must be done though interprocess communication (IPC) between the individual pages and the Main process.
Dynamic Module Loading
Modules & their accompanying DApp are loaded dynamically by the Main process. The Launcher UI is then informed and updated to respect these loaded DApps. The Main process does not know anything about the underlying modules, only where to find the downloaded modules and how to load them.
DApp Requirements
There are four primary requirements a DApp must describe to the launcher in order for the launcher to effectively load the DApp.
DApps must:
Be able to describe to the launcher how they wish to be displayed in the launcher
Describe where to find their Modules source code
Describe where to find their UI's source code
State their Modules expected checksum for validation purposes
When being loaded dynamically, the Module Loader will read a module.json file which will explain these for the launcher.
For the purpose of example, the current module.json file for the Seed DApp contains the following JSON object.
{
"name" : "Seed",
"dappSource" : "index.html",
"moduleSource" : "seed.js",
"moduleChecksum" : "fd37"
}
Implementation
All modules are stored in the /modules folder in the base directory of the client. Each sub folder of the /modules folder represents a separate DApp that the launcher can launch. Inside each sub folder is a module.json file, which contains a JSON object that describes the above DApp requirements to the launcher.
When the launcher opens, it will read each subfolder's module.json file and dynamically create each tile & associated module. In order to add modules to the launcher, one must simply be downloaded and places in the /modules folder.
The above logic is express in code in the moduleLoader.js file, under the exported function "loadModules".
High Level API (HLAPI)
DApps which choose to be hosted inside the launcher will have the luxury of accessing a High Level API for Seed. This High Level API is referred to as the SeedHLAPI. This API wraps all logic needed regarding how Seed works under the hood, and lets users create transactions, read from modules or subscribe for updates in a couple simple function calls.
This approach differs from the current Seed API, which is now being referred to as the Seed Low level API (SeedLLAPI). The Low Level API (LLAPI) exposes the underlying exports needed to communicate directly with Seed's subsystems, while the High Level API (HLAPI) wraps the logic, removing a developers need from understanding how to appropriately communicate with the Seed subsystems.
Electron Constraints
Electron brings its own set of constraints we must adhere to when building the HLAPI. The primary constraints we must respect is how Electron and the underlying Chromium system its built on communicate across processes.
Each process has its own set of memory, with each window being a separate process. The base process is known as "Main", which creates each individual window. Each window has its own process, which is known as a Renderer. Renderer's and Main communicate with one-other through Electron's IPC channels. These IPC channels cannot have JavaScript references sent through it, so all data or objects that are sent over IPC must be serializable.
Since we cannot send references, the processes cannot communicate with the same Seed API instances directly. Instead, Main must be the process that holds the Seed LLAPI instance used by the Seed HLAPI, and the Renderers must request Main do the processing. These requests must be made through IPC.
DApp Requirements
DApps must be able to make HLAPI requests, wait for a response from a separate process, and continue their work once the execution of the HLAPI has completed. Therefore, the DApps require that the HLAPI is asynchronous, despite the LLAPI being synchronous.
With regards to what DApps must be able to do, DApps must be able to:
API | Description |
---|---|
Switch Accounts | DApps may request an accounts witch, as users may have more than one account they wish to use. |
Get Account | DApps may want to know which account is currently logged in. |
Create Transaction | DApps must be able to create transactions when a user chooses to execute a function in their module. |
Get Transactions | DApps may want to fetch a transaction from storage by the transactions hash in order to read it. |
Call "Getters" | DApps may want to call any modules getters, even other modules. |
Read | DApps may want to read raw data from the ledger regarding a module. |
Subscribe | DApps must be able to subscribe to function callbacks and data changes in order to live update appropriately. |
Unsubscribe | DApps must be able to unsubscribe to avoid memory leaks and unwanted callback invocations. |
Create Modules | DApps must be able to create modules, as well as add them to the Seed system. |
Get Modules | DApps may want to fetch a module to read their data directly. |
Implementation
The implementation of the HLAPI is split into two fronts. The first is how the Main Process implements the API in order to communicate with the Seed LLAPI. The second portion is how the DApp's Renderers make the asynchronous API requests to the Main Process.
This implementation added the requirement of asynchronous IPC communication. In order to meet this requirement, the npm module "PromiseIPC" was added to the Electron clients dependencies.
Main Process
For the implementation of the Main process portion, the code can be found at the bottom of main.js.
The Main process instantiates a PromiseIPC instance to listen for requests by the Renderers. Each API call a Renderer can make has its own listener inside main.js. These calls arrive asynchronously, where the file then works on the request, wrapping any logic needed to perform these actions. These listeners communicate with the Seed LLAPI directly through the index.js file in the seedSrc external folder.
Most API implementations are simple, simply accessing the LLAPI's appropriate export and speaking directly with whichever subsystem is needed. Some calls require a little more work, such as switching accounts.
Renderer Process
For the implementation of the Renderer processes portion, the code can be found in the seedHLAPI.js file, which DApps can require.
In seedHLAPI.js exists a class known as SeedHLAPI. This class takes a instantiated Renderer's PromiseIPC object upon creation to communicate with Main on the Renderer's behalf. This class is very simple, as the logic heavy side is done by the Main process. The SeedHLAPI class simply wraps the communication logic, making it easier for DApps to communicate without needing to know what channels the Main process is listening for.
Seed DApp
The Seed DApp is the first module & DApp available in the launcher. This DApp hosts the Seed cryptocurrency and acts as a wallet for users.
Wireframe Design
The theme will be a simple white background with a light green tint. Upon opening, a users public address will be visible in plain text, as well as a QR code display of that address. The users' balance will also be available, live updating while sending or receiving funds.
The various actions a user can do, such as transfer currency, are listed as buttons below the information heavy header.
At the bottom of the DApp will be the recent transaction history.
Current State
This pull request did not focus on styling or design yet, instead it focused on making every Seed module function implemented in a functional manner.
In the Seed DApp, the users public key and account balance are displayed on the screen. These values update dynamically, so if a user is given Seed or if accounts change, these values will automatically update to match.
There is a drop-down menu which correlates to the different functions in the Seed module. Seed was designed after the Ethereum ERC20 standard for tokens, and therefore there are four primary functions that users can invoke. "Transfer", "TransferFrom", "Approve" and "Burn". On top of that, there is also a "Constructor" option, which can be invoked upon the creation of the module to give it the initial starting values.
When a user changes between these five options in the drop-down menu, the input form live updates to match. These inputs are the same inputs that the functions expect as parameters into Seed. Users can invoke the function after filling in inputs, which creates a transaction and adds it to the entanglement.
Video of forms changing based on selected function
Relay Module
The Relay module is created for development only. This module and DApp allows the simply creation of empty transactions which simply validates some transactions in the entanglement. The Relay DApp will randomize one of one hundred possible users, simulating multiple users pushing the system forward.
The UI is simply a single button which says "Relay".
Honourable Mention
Unit Test Migration
Previously, the unit tests setup the entanglement and constructed the launch of Seed. Now, that must be done through the UI.
In order to run the unit tests, you must:
Switch to using "Account #1" through the menu system
Open the Seed DApp in the launcher
"Construct" the Seed module, keeping the initial amount in circulation at "1000" SEED
Click the "Unit Tests" button on the main UI.
In the terminal, the unit tests will be run in the main process, showing up in your NodeJS terminal (NOT in the Launcher or Seed windows debug logs)
Video of Seed module unit test being run
GitHub
https://github.com/CarsonRoscoe
Thanks for the contribution, @carsonroscoe! Once again an amazing piece of work, I really don't have any feedback to give other than that you should 100% continue with what you are doing! From what I can tell you took @gregory.latinier's advice into account as well, which is always great to see. Definitely picking this as a staff pick and I look forward to seeing more of your contributions in the future!
Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.
To view those questions and the relevant answers related to your post, click here.
Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]
Thank you for your review, @amosbastian!
So far this week you've reviewed 2 contributions. Keep up the good work!
Thank you very much for the kind words! I really appreciate it :)
Hey @carsonroscoe
Thanks for contributing on Utopian.
Congratulations! Your contribution was Staff Picked to receive a maximum vote for the development category on Utopian for being of significant value to the project and the open source community.
We’re already looking forward to your next contribution!
Want to chat? Join us on Discord https://discord.gg/h52nFrV.
Vote for Utopian Witness!