Introduction

This guide describes the steps needed to implement a DAPP (decentralized application) front-end that works without the need to know which blockchain it communicates to by making these methods abstract. In this way multiple implementations of these abstract methods can be made, which means you will be able to support multiple blockchain platforms in a modular way.

Reasoning

During the development of a proof-of-concept that uses multiple smart contract platforms, we realised that the code for interacting with different blockchains was not separated to make it modular. We then created a modular approach that also makes it very easy to add more platforms.

In this guide, we show code examples of the usage in our project, to make it easier to understand.

Components

The modular approach consists of the following three components:

Abstract class

InjectionToken

BlockchainResolver

The abstract class defines the methods and variables each blockchain platform needs to implement.

The InjectionToken is used by Angular. It makes it possible to just add this token to a module as a provider. This way we are able to get an array of all implementations with this token.

In the BlockchainResolver we parse all modules with the injection token and implement the methods that are used for getting the desired implementation of the abstract service.

Implementation

First we need an abstract class. This is the class where all methods and variables that are needed will be defined. Lets call this the BlockchainService

export abstract class BlockchainService { constructor() {} wallet;

abstract login(); //YOUR METHODS AND VARIABLES }

Then we create the InjectionToken in a separate file. The injection token uses the abstract class as its type.

import { InjectionToken } from '@angular/core';

import { BlockchainService } from './services/blockchain.service'; export const BLOCKCHAIN_TOKEN: InjectionToken<BlockchainService[]> = new InjectionToken<BlockchainService[]>('BlockchainToken');

After that, we create a module which provides this injection token and uses a specific class that implements the type of the injection token, in this case the EthereumBlockchainService.

import { NgModule, NgZone } from '@angular/core';

import { CommonModule } from '@angular/common';

import { EthereumBlockchainService } from './eth-blockchain-service';

import { BLOCKCHAIN_TOKEN } from '../app/blockchain-token';

import { BrowserModule } from '@angular/platform-browser'; @NgModule({

imports: [

CommonModule,

BrowserModule

],

declarations: [],

providers: [

{

provide: BLOCKCHAIN_TOKEN,

useClass: EthereumBlockchainService,

multi: true,

deps: [NgZone]

}

],

}) export class EthereumModule { }

In this module the BLOCKCHAIN_TOKEN uses the EthereumBlockchainService. This is the class that implements the abstract BlockchainService class. We are also setting multi to true so angular knows that there are multiple modules that provide the BLOCKCHAIN_TOKEN, and finally we are adding a dependency that is used in the EthereumBlockchainService.

Now, we implement a factory. This is a default method that chooses which BlockchainService to implement. We do this in the app.module.ts file.

// YOUR IMPORTS // The factory method

export function blockchainServiceFactory(

blockchainResolver: BlockchainResolver) {

return blockchainResolver.getCurrentBlockchain();

} @NgModule({

declarations: [ // YOUR DECLARATIONS ],

imports: [ // YOUR IMPORTS ],

providers: [

{

provide: BlockchainService,

useFactory: blockchainServiceFactory, // the factory method

deps: [BlockchainResolver]

}

],

bootstrap: [AppComponent],

entryComponents: [ // YOUR ENTRYCOMPONENTS(not required)]

}) export class AppModule { }

Every time the BlockchainService gets used in a component, the usefactory method will provide the right implementation.

As you can see we call the getCurrentBlockchain method of the BlockchainResolver class in the factory method. Let’s see our implementation of this class.

The above class is just an example of a possible implementation. In our case we wanted a solution where we could also set the BlockchainService within a component, which is why we implemented the getBlockchains method for example. We also won’t return a BlockchainService if the ticker isn’t set. We want the user to click on the service they want to use, which will then set the ticker.

So let’s implement the most bare-bones BlockchainResolver

import { BlockchainService } from './blockchain.service';

import { Injectable, Inject } from '@angular/core';

import { BLOCKCHAIN_TOKEN } from '../blockchain-token'; @Injectable({

providedIn: 'root'

}) export class BlockchainResolver { constructor

(

@Inject(BLOCKCHAIN_TOKEN) public blockchains: BlockchainService[]

)

{} getCurrentBlockchain(): BlockchainService {

// YOUR IMPLEMENTATION HERE

return this.blockchains[0];// REMOVE THIS

} }

The most important part is the constructor. This asks angular to store all blockchain services with the BLOCKCHAIN_TOKEN in the ‘blockchains’ array. Now you can access all implemented BlockchainServices from this array.

Conclusion

By implementing this you will be able to support multiple blockchain platforms which are all completely separated into their own modules. Also adding a platform will only involve implementing the abstract methods and adding the InjectionToken in the module.

Background

This post was part of an internship at Topicus. My assignment was to review the state of other smart contract platforms besides Ethereum.