From df008fcace5bbf09eb3322ee2c3dc22da80c5160 Mon Sep 17 00:00:00 2001 From: Spencer Ofwiti Date: Fri, 11 Jun 2021 16:57:16 +0300 Subject: [PATCH 1/3] Refactor keystore into singleton service. --- src/app/_services/auth.service.ts | 131 ++++++++++----------- src/app/_services/index.ts | 2 + src/app/_services/keystore.service.spec.ts | 16 +++ src/app/_services/keystore.service.ts | 20 ++++ src/app/_services/token.service.ts | 6 +- src/app/_services/transaction.service.ts | 4 +- src/app/_services/user.service.ts | 3 +- src/app/auth/auth.component.ts | 8 +- 8 files changed, 114 insertions(+), 76 deletions(-) create mode 100644 src/app/_services/keystore.service.spec.ts create mode 100644 src/app/_services/keystore.service.ts diff --git a/src/app/_services/auth.service.ts b/src/app/_services/auth.service.ts index 937f2ce..9947631 100644 --- a/src/app/_services/auth.service.ts +++ b/src/app/_services/auth.service.ts @@ -3,12 +3,13 @@ import { hobaParseChallengeHeader } from '@src/assets/js/hoba.js'; import { signChallenge } from '@src/assets/js/hoba-pgp.js'; import { environment } from '@src/environments/environment'; import { LoggingService } from '@app/_services/logging.service'; -import { MutableKeyStore, MutablePgpKeyStore } from '@app/_pgp'; +import { MutableKeyStore } from '@app/_pgp'; import { ErrorDialogService } from '@app/_services/error-dialog.service'; import { HttpClient } from '@angular/common/http'; import { HttpError, rejectBody } from '@app/_helpers/global-error-handler'; import { Staff } from '@app/_models'; import { BehaviorSubject, Observable } from 'rxjs'; +import { KeystoreService } from '@app/_services/keystore.service'; @Injectable({ providedIn: 'root', @@ -25,17 +26,15 @@ export class AuthService { private httpClient: HttpClient, private loggingService: LoggingService, private errorDialogService: ErrorDialogService - ) { - this.mutableKeyStore = new MutablePgpKeyStore(); - } + ) {} async init(): Promise { - await this.mutableKeyStore.loadKeyring(); + this.mutableKeyStore = await KeystoreService.getKeystore(); if (localStorage.getItem(btoa('CICADA_PRIVATE_KEY'))) { await this.mutableKeyStore.importPrivateKey(localStorage.getItem(btoa('CICADA_PRIVATE_KEY'))); } } - + getSessionToken(): string { return sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN')); } @@ -49,84 +48,80 @@ export class AuthService { } getWithToken(): Promise { - const headers = { - Authorization: 'Bearer ' + this.getSessionToken, - 'Content-Type': 'application/json;charset=utf-8', - 'x-cic-automerge': 'none', - }; - const options = { - headers, - }; - return fetch(environment.cicMetaUrl, options).then((response) => { - if (!response.ok) { - this.loggingService.sendErrorLevelMessage('failed to get with auth token.', - this, - { error: "" }); + const headers = { + Authorization: 'Bearer ' + this.getSessionToken, + 'Content-Type': 'application/json;charset=utf-8', + 'x-cic-automerge': 'none', + }; + const options = { + headers, + }; + return fetch(environment.cicMetaUrl, options).then((response) => { + if (!response.ok) { + this.loggingService.sendErrorLevelMessage('failed to get with auth token.', this, { + error: '', + }); - return false; - } - return true; - }); + return false; + } + return true; + }); } // TODO rename to send signed challenge and set session. Also separate these responsibilities sendSignedChallenge(hobaResponseEncoded: any): Promise { - const headers = { - Authorization: 'HOBA ' + hobaResponseEncoded, - 'Content-Type': 'application/json;charset=utf-8', - 'x-cic-automerge': 'none', - }; - const options = { - headers, - }; - return fetch(environment.cicMetaUrl, options) + const headers = { + Authorization: 'HOBA ' + hobaResponseEncoded, + 'Content-Type': 'application/json;charset=utf-8', + 'x-cic-automerge': 'none', + }; + const options = { + headers, + }; + return fetch(environment.cicMetaUrl, options); } getChallenge(): Promise { - return fetch(environment.cicMetaUrl) - .then(response => { - if (response.status === 401) { - const authHeader: string = response.headers.get('WWW-Authenticate'); - return hobaParseChallengeHeader(authHeader); - } - }); + return fetch(environment.cicMetaUrl).then((response) => { + if (response.status === 401) { + const authHeader: string = response.headers.get('WWW-Authenticate'); + return hobaParseChallengeHeader(authHeader); + } + }); } async login(): Promise { if (this.getSessionToken()) { - sessionStorage.removeItem(btoa('CICADA_SESSION_TOKEN')); + sessionStorage.removeItem(btoa('CICADA_SESSION_TOKEN')); } else { - const o = await this.getChallenge(); + const o = await this.getChallenge(); - const r = await signChallenge( - o.challenge, - o.realm, - environment.cicMetaUrl, - this.mutableKeyStore - ); + const r = await signChallenge( + o.challenge, + o.realm, + environment.cicMetaUrl, + this.mutableKeyStore + ); - const tokenResponse = await this.sendSignedChallenge(r) - .then(response => { - const token = response.headers.get('Token') - if (token) { - return token - } - if (response.status === 401) { - let e = new HttpError("You are not authorized to use this system", response.status) - throw e - } - if (!response.ok) { - let e = new HttpError("Unknown error from authentication server", response.status) - throw e - } - }) - - if (tokenResponse) { - this.setSessionToken(tokenResponse); - this.setState('Click button to log in'); - return true + const tokenResponse = await this.sendSignedChallenge(r).then((response) => { + const token = response.headers.get('Token'); + if (token) { + return token; } - return false + if (response.status === 401) { + throw new HttpError('You are not authorized to use this system', response.status); + } + if (!response.ok) { + throw new HttpError('Unknown error from authentication server', response.status); + } + }); + + if (tokenResponse) { + this.setSessionToken(tokenResponse); + this.setState('Click button to log in'); + return true; + } + return false; } } diff --git a/src/app/_services/index.ts b/src/app/_services/index.ts index 88db87d..13aefd9 100644 --- a/src/app/_services/index.ts +++ b/src/app/_services/index.ts @@ -7,3 +7,5 @@ export * from '@app/_services/location.service'; export * from '@app/_services/logging.service'; export * from '@app/_services/error-dialog.service'; export * from '@app/_services/web3.service'; +export * from '@app/_services/keystore.service'; +export * from '@app/_services/registry.service'; diff --git a/src/app/_services/keystore.service.spec.ts b/src/app/_services/keystore.service.spec.ts new file mode 100644 index 0000000..b0a249b --- /dev/null +++ b/src/app/_services/keystore.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { KeystoreService } from './keystore.service'; + +describe('KeystoreService', () => { + let service: KeystoreService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(KeystoreService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/_services/keystore.service.ts b/src/app/_services/keystore.service.ts new file mode 100644 index 0000000..6fcaa37 --- /dev/null +++ b/src/app/_services/keystore.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@angular/core'; +import { MutableKeyStore, MutablePgpKeyStore } from '@app/_pgp'; + +@Injectable({ + providedIn: 'root', +}) +export class KeystoreService { + private static mutableKeyStore: MutableKeyStore; + + constructor() {} + + public static async getKeystore(): Promise { + if (!KeystoreService.mutableKeyStore) { + this.mutableKeyStore = new MutablePgpKeyStore(); + await this.mutableKeyStore.loadKeyring(); + return KeystoreService.mutableKeyStore; + } + return KeystoreService.mutableKeyStore; + } +} diff --git a/src/app/_services/token.service.ts b/src/app/_services/token.service.ts index d9ffed8..292b417 100644 --- a/src/app/_services/token.service.ts +++ b/src/app/_services/token.service.ts @@ -4,7 +4,7 @@ import { TokenRegistry } from '@app/_eth'; import { HttpClient } from '@angular/common/http'; import { RegistryService } from '@app/_services/registry.service'; import { Token } from '@app/_models'; -import {BehaviorSubject, Observable, Subject} from 'rxjs'; +import { BehaviorSubject, Observable, Subject } from 'rxjs'; @Injectable({ providedIn: 'root', @@ -14,7 +14,9 @@ export class TokenService { tokenRegistry: TokenRegistry; onload: (status: boolean) => void; tokens: Array = []; - private tokensList: BehaviorSubject> = new BehaviorSubject>(this.tokens); + private tokensList: BehaviorSubject> = new BehaviorSubject>( + this.tokens + ); tokensSubject: Observable> = this.tokensList.asObservable(); constructor(private httpClient: HttpClient) {} diff --git a/src/app/_services/transaction.service.ts b/src/app/_services/transaction.service.ts index 5a6c7d8..6c09320 100644 --- a/src/app/_services/transaction.service.ts +++ b/src/app/_services/transaction.service.ts @@ -18,6 +18,7 @@ import { CICRegistry } from 'cic-client'; import { RegistryService } from '@app/_services/registry.service'; import Web3 from 'web3'; import { Web3Service } from '@app/_services/web3.service'; +import { KeystoreService } from '@app/_services/keystore.service'; const vCard = require('vcard-parser'); @Injectable({ @@ -170,7 +171,8 @@ export class TransactionService { tx.value = toValue(value); tx.data = data; const txMsg = tx.message(); - const privateKey = this.authService.mutableKeyStore.getPrivateKey(); + const keystore = await KeystoreService.getKeystore(); + const privateKey = keystore.getPrivateKey(); if (!privateKey.isDecrypted()) { const password = window.prompt('password'); await privateKey.decrypt(password); diff --git a/src/app/_services/user.service.ts b/src/app/_services/user.service.ts index e1db2d5..9b83503 100644 --- a/src/app/_services/user.service.ts +++ b/src/app/_services/user.service.ts @@ -14,6 +14,7 @@ import { CICRegistry } from 'cic-client'; import { AuthService } from '@app/_services/auth.service'; import { personValidation, vcardValidation } from '@app/_helpers'; import { add0x } from '@src/assets/js/ethtx/dist/hex'; +import { KeystoreService } from '@app/_services/keystore.service'; const vCard = require('vcard-parser'); @Injectable({ @@ -45,7 +46,7 @@ export class UserService { async init(): Promise { await this.authService.init(); await this.tokenService.init(); - this.keystore = this.authService.mutableKeyStore; + this.keystore = await KeystoreService.getKeystore(); this.signer = new PGPSigner(this.keystore); this.registry = await RegistryService.getRegistry(); } diff --git a/src/app/auth/auth.component.ts b/src/app/auth/auth.component.ts index 328997f..4272cec 100644 --- a/src/app/auth/auth.component.ts +++ b/src/app/auth/auth.component.ts @@ -22,7 +22,7 @@ export class AuthComponent implements OnInit { private authService: AuthService, private formBuilder: FormBuilder, private router: Router, - private errorDialogService: ErrorDialogService, + private errorDialogService: ErrorDialogService ) {} async ngOnInit(): Promise { @@ -49,10 +49,10 @@ export class AuthComponent implements OnInit { async login(): Promise { try { - const loginResult = await this.authService.login() - if (loginResult) { + const loginResult = await this.authService.login(); + if (loginResult) { this.router.navigate(['/home']); - } + } } catch (HttpError) { this.errorDialogService.openDialog({ message: HttpError.message, From 85e41e99b04f1c6b9b30108bb7d656cb9791fdb3 Mon Sep 17 00:00:00 2001 From: Spencer Ofwiti Date: Mon, 14 Jun 2021 17:50:39 +0300 Subject: [PATCH 2/3] Add promises to keystore singleton service. --- src/app/_services/keystore.service.ts | 14 ++++++++------ src/environments/environment.dev.ts | 2 +- src/environments/environment.prod.ts | 2 +- src/environments/environment.ts | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/app/_services/keystore.service.ts b/src/app/_services/keystore.service.ts index 6fcaa37..7d8b92e 100644 --- a/src/app/_services/keystore.service.ts +++ b/src/app/_services/keystore.service.ts @@ -10,11 +10,13 @@ export class KeystoreService { constructor() {} public static async getKeystore(): Promise { - if (!KeystoreService.mutableKeyStore) { - this.mutableKeyStore = new MutablePgpKeyStore(); - await this.mutableKeyStore.loadKeyring(); - return KeystoreService.mutableKeyStore; - } - return KeystoreService.mutableKeyStore; + return new Promise(async (resolve, reject) => { + if (!KeystoreService.mutableKeyStore) { + this.mutableKeyStore = new MutablePgpKeyStore(); + await this.mutableKeyStore.loadKeyring(); + return resolve(KeystoreService.mutableKeyStore); + } + return resolve(KeystoreService.mutableKeyStore); + }); } } diff --git a/src/environments/environment.dev.ts b/src/environments/environment.dev.ts index 978f71f..cde24f3 100644 --- a/src/environments/environment.dev.ts +++ b/src/environments/environment.dev.ts @@ -6,7 +6,7 @@ export const environment = { logLevel: NgxLoggerLevel.ERROR, serverLogLevel: NgxLoggerLevel.OFF, loggingUrl: '', - cicMetaUrl: 'https://meta.dev.grassrootseconomics.net', + cicMetaUrl: 'https://meta-auth.dev.grassrootseconomics.net', publicKeysUrl: 'https://dev.grassrootseconomics.net/.well-known/publickeys/', cicCacheUrl: 'https://cache.dev.grassrootseconomics.net', web3Provider: 'wss://bloxberg-ws.dev.grassrootseconomics.net', diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index f4786c1..32cc50b 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -6,7 +6,7 @@ export const environment = { logLevel: NgxLoggerLevel.ERROR, serverLogLevel: NgxLoggerLevel.OFF, loggingUrl: '', - cicMetaUrl: 'https://meta.dev.grassrootseconomics.net', + cicMetaUrl: 'https://meta-auth.dev.grassrootseconomics.net', publicKeysUrl: 'https://dev.grassrootseconomics.net/.well-known/publickeys/', cicCacheUrl: 'https://cache.dev.grassrootseconomics.net', web3Provider: 'wss://bloxberg-ws.dev.grassrootseconomics.net', diff --git a/src/environments/environment.ts b/src/environments/environment.ts index df02af7..f2d7c4f 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -6,7 +6,7 @@ export const environment = { logLevel: NgxLoggerLevel.ERROR, serverLogLevel: NgxLoggerLevel.OFF, loggingUrl: 'http://localhost:8000', - cicMetaUrl: 'https://meta.dev.grassrootseconomics.net', + cicMetaUrl: 'https://meta-auth.dev.grassrootseconomics.net', publicKeysUrl: 'https://dev.grassrootseconomics.net/.well-known/publickeys/', cicCacheUrl: 'https://cache.dev.grassrootseconomics.net', web3Provider: 'wss://bloxberg-ws.dev.grassrootseconomics.net', From 6a1ca3d4a0f45278cd9811469fd6f54c9152f76f Mon Sep 17 00:00:00 2001 From: Spencer Ofwiti Date: Tue, 15 Jun 2021 16:37:33 +0000 Subject: [PATCH 3/3] Add precommit hooks --- .gitignore | 1 + .husky/.gitignore | 1 - .husky/_/husky.sh | 30 - docs/compodoc/classes/TokenServiceStub.html | 2 +- .../classes/TransactionServiceStub.html | 2 +- docs/compodoc/classes/UserServiceStub.html | 197 +- .../components/AccountDetailsComponent.html | 433 +- .../components/AccountSearchComponent.html | 48 +- .../components/AccountsComponent.html | 74 +- docs/compodoc/components/AdminComponent.html | 89 +- docs/compodoc/components/AuthComponent.html | 35 +- .../components/CreateAccountComponent.html | 146 +- .../components/ErrorDialogComponent.html | 10 +- docs/compodoc/components/FooterComponent.html | 10 +- .../components/FooterStubComponent.html | 8 +- .../components/NetworkStatusComponent.html | 12 +- .../components/OrganizationComponent.html | 30 +- docs/compodoc/components/PagesComponent.html | 14 +- .../components/SettingsComponent.html | 71 +- .../compodoc/components/SidebarComponent.html | 5 +- .../components/SidebarStubComponent.html | 8 +- .../components/TokenDetailsComponent.html | 31 +- docs/compodoc/components/TokensComponent.html | 49 +- docs/compodoc/components/TopbarComponent.html | 10 +- .../components/TopbarStubComponent.html | 8 +- .../TransactionDetailsComponent.html | 178 +- .../components/TransactionsComponent.html | 101 +- .../directives/RouterLinkDirectiveStub.html | 4 +- docs/compodoc/js/search/search_index.js | 4 +- docs/compodoc/miscellaneous/variables.html | 8 +- docs/compodoc/modules/AppModule.html | 8 +- .../modules/AppModule/dependencies.svg | 8 +- docs/compodoc/modules/SettingsModule.html | 46 +- .../modules/SettingsModule/dependencies.svg | 46 +- docs/compodoc/modules/SharedModule.html | 8 +- .../modules/SharedModule/dependencies.svg | 8 +- docs/compodoc/modules/TransactionsModule.html | 56 +- .../TransactionsModule/dependencies.svg | 56 +- docs/typedoc/assets/js/search.js | 2 +- ...ing_user_service_stub.userservicestub.html | 10 +- .../modules/environments_environment_dev.html | 5 +- .../environments_environment_prod.html | 5 +- package-lock.json | 23332 +--------------- package.json | 18 +- src/app/auth/auth.component.html | 33 +- .../account-details.component.html | 431 +- .../account-search.component.html | 46 +- .../pages/accounts/accounts.component.html | 72 +- .../create-account.component.html | 144 +- src/app/pages/admin/admin.component.html | 87 +- src/app/pages/pages.component.html | 12 +- .../organization/organization.component.html | 28 +- .../pages/settings/settings.component.html | 69 +- .../token-details.component.html | 29 +- src/app/pages/tokens/tokens.component.html | 47 +- .../transaction-details.component.html | 176 +- .../transactions/transactions.component.html | 99 +- .../error-dialog/error-dialog.component.html | 8 +- src/app/shared/footer/footer.component.html | 8 +- .../network-status.component.html | 10 +- src/app/shared/sidebar/sidebar.component.html | 3 +- src/app/shared/topbar/topbar.component.html | 8 +- src/index.html | 235 +- src/main.ts | 5 +- src/polyfills.ts | 3 +- src/styles.scss | 116 +- src/test.ts | 13 +- src/testing/router-link-directive-stub.ts | 4 +- src/testing/shared-module-stub.ts | 8 +- src/testing/token-service-stub.ts | 2 +- src/testing/transaction-service-stub.ts | 2 +- src/testing/user-service-stub.ts | 101 +- 72 files changed, 3365 insertions(+), 23661 deletions(-) delete mode 100644 .husky/.gitignore delete mode 100644 .husky/_/husky.sh diff --git a/.gitignore b/.gitignore index 5a63fb1..14fe5b7 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ npm-debug.log yarn-error.log testem.log /typings +/.husky # System Files .DS_Store diff --git a/.husky/.gitignore b/.husky/.gitignore deleted file mode 100644 index 31354ec..0000000 --- a/.husky/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_ diff --git a/.husky/_/husky.sh b/.husky/_/husky.sh deleted file mode 100644 index ca2720e..0000000 --- a/.husky/_/husky.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh -if [ -z "$husky_skip_init" ]; then - debug () { - [ "$HUSKY_DEBUG" = "1" ] && echo "husky (debug) - $1" - } - - readonly hook_name="$(basename "$0")" - debug "starting $hook_name..." - - if [ "$HUSKY" = "0" ]; then - debug "HUSKY env variable is set to 0, skipping hook" - exit 0 - fi - - if [ -f ~/.huskyrc ]; then - debug "sourcing ~/.huskyrc" - . ~/.huskyrc - fi - - export readonly husky_skip_init=1 - sh -e "$0" "$@" - exitCode="$?" - - if [ $exitCode != 0 ]; then - echo "husky - $hook_name hook exited with code $exitCode (error)" - exit $exitCode - fi - - exit 0 -fi diff --git a/docs/compodoc/classes/TokenServiceStub.html b/docs/compodoc/classes/TokenServiceStub.html index cf7c7b2..44ec4f6 100644 --- a/docs/compodoc/classes/TokenServiceStub.html +++ b/docs/compodoc/classes/TokenServiceStub.html @@ -186,7 +186,7 @@ getBySymbol(symbol: string): any { return { name: 'Reserve', - symbol: 'RSV' + symbol: 'RSV', }; } } diff --git a/docs/compodoc/classes/TransactionServiceStub.html b/docs/compodoc/classes/TransactionServiceStub.html index d0bbe0b..e3f4748 100644 --- a/docs/compodoc/classes/TransactionServiceStub.html +++ b/docs/compodoc/classes/TransactionServiceStub.html @@ -352,7 +352,7 @@
-
import {Observable, of} from 'rxjs';
+        
import { Observable, of } from 'rxjs';
 
 export class TransactionServiceStub {
   setTransaction(transaction: any, cacheSize: number): void {}
diff --git a/docs/compodoc/classes/UserServiceStub.html b/docs/compodoc/classes/UserServiceStub.html
index 047ffe3..3dd2cbe 100644
--- a/docs/compodoc/classes/UserServiceStub.html
+++ b/docs/compodoc/classes/UserServiceStub.html
@@ -153,13 +153,19 @@
     { id: 3, user: 'Will', role: 'superadmin', action: 'Reclaim RSV 1000', approval: true },
     { id: 4, user: 'Vivian', role: 'enroller', action: 'Complete user profile', approval: true },
     { id: 5, user: 'Jack', role: 'enroller', action: 'Reclaim RSV 200', approval: false },
-    { id: 6, user: 'Patience', role: 'enroller', action: 'Change user information', approval: false }
+    {
+      id: 6,
+      user: 'Patience',
+      role: 'enroller',
+      action: 'Change user information',
+      approval: false,
+    },
   ]
                         
                     
                         
                             
-                                    
+                                    
                             
                         
 
@@ -187,11 +193,71 @@
                     
                         
                             Default value : [
-    {id: 1, name: 'John Doe', phone: '+25412345678', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'user', created: '08/16/2020', balance: '12987', failedPinAttempts: 1, status: 'approved', bio: 'Bodaboda', gender: 'male'},
-    {id: 2, name: 'Jane Buck', phone: '+25412341234', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'vendor', created: '04/02/2020', balance: '56281', failedPinAttempts: 0, status: 'approved', bio: 'Groceries', gender: 'female'},
-    {id: 3, name: 'Mc Donald', phone: '+25498765432', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'group', created: '11/16/2020', balance: '450', failedPinAttempts: 2, status: 'unapproved', bio: 'Food', gender: 'male'},
-    {id: 4, name: 'Hera Cles', phone: '+25498769876', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'user', created: '05/28/2020', balance: '5621', failedPinAttempts: 3, status: 'approved', bio: 'Shop', gender: 'female'},
-    {id: 5, name: 'Silver Fia', phone: '+25462518374', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'token agent', created: '10/10/2020', balance: '817', failedPinAttempts: 0, status: 'unapproved', bio: 'Electronics', gender: 'male'},
+    {
+      id: 1,
+      name: 'John Doe',
+      phone: '+25412345678',
+      address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865',
+      type: 'user',
+      created: '08/16/2020',
+      balance: '12987',
+      failedPinAttempts: 1,
+      status: 'approved',
+      bio: 'Bodaboda',
+      gender: 'male',
+    },
+    {
+      id: 2,
+      name: 'Jane Buck',
+      phone: '+25412341234',
+      address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865',
+      type: 'vendor',
+      created: '04/02/2020',
+      balance: '56281',
+      failedPinAttempts: 0,
+      status: 'approved',
+      bio: 'Groceries',
+      gender: 'female',
+    },
+    {
+      id: 3,
+      name: 'Mc Donald',
+      phone: '+25498765432',
+      address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865',
+      type: 'group',
+      created: '11/16/2020',
+      balance: '450',
+      failedPinAttempts: 2,
+      status: 'unapproved',
+      bio: 'Food',
+      gender: 'male',
+    },
+    {
+      id: 4,
+      name: 'Hera Cles',
+      phone: '+25498769876',
+      address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865',
+      type: 'user',
+      created: '05/28/2020',
+      balance: '5621',
+      failedPinAttempts: 3,
+      status: 'approved',
+      bio: 'Shop',
+      gender: 'female',
+    },
+    {
+      id: 5,
+      name: 'Silver Fia',
+      phone: '+25462518374',
+      address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865',
+      type: 'token agent',
+      created: '10/10/2020',
+      balance: '817',
+      failedPinAttempts: 0,
+      status: 'unapproved',
+      bio: 'Electronics',
+      gender: 'male',
+    },
   ]
                         
                     
@@ -233,8 +299,8 @@
 
             
                 
-                    
+                    
                 
             
 
@@ -303,8 +369,8 @@
 
             
                 
-                    
+                    
                 
             
 
@@ -373,8 +439,8 @@
 
             
                 
-                    
+                    
                 
             
 
@@ -443,8 +509,8 @@
 
             
                 
-                    
+                    
                 
             
 
@@ -501,15 +567,75 @@
 
 
     
-
import {Observable, of} from 'rxjs';
+        
import { Observable, of } from 'rxjs';
 
 export class UserServiceStub {
   users = [
-    {id: 1, name: 'John Doe', phone: '+25412345678', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'user', created: '08/16/2020', balance: '12987', failedPinAttempts: 1, status: 'approved', bio: 'Bodaboda', gender: 'male'},
-    {id: 2, name: 'Jane Buck', phone: '+25412341234', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'vendor', created: '04/02/2020', balance: '56281', failedPinAttempts: 0, status: 'approved', bio: 'Groceries', gender: 'female'},
-    {id: 3, name: 'Mc Donald', phone: '+25498765432', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'group', created: '11/16/2020', balance: '450', failedPinAttempts: 2, status: 'unapproved', bio: 'Food', gender: 'male'},
-    {id: 4, name: 'Hera Cles', phone: '+25498769876', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'user', created: '05/28/2020', balance: '5621', failedPinAttempts: 3, status: 'approved', bio: 'Shop', gender: 'female'},
-    {id: 5, name: 'Silver Fia', phone: '+25462518374', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'token agent', created: '10/10/2020', balance: '817', failedPinAttempts: 0, status: 'unapproved', bio: 'Electronics', gender: 'male'},
+    {
+      id: 1,
+      name: 'John Doe',
+      phone: '+25412345678',
+      address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865',
+      type: 'user',
+      created: '08/16/2020',
+      balance: '12987',
+      failedPinAttempts: 1,
+      status: 'approved',
+      bio: 'Bodaboda',
+      gender: 'male',
+    },
+    {
+      id: 2,
+      name: 'Jane Buck',
+      phone: '+25412341234',
+      address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865',
+      type: 'vendor',
+      created: '04/02/2020',
+      balance: '56281',
+      failedPinAttempts: 0,
+      status: 'approved',
+      bio: 'Groceries',
+      gender: 'female',
+    },
+    {
+      id: 3,
+      name: 'Mc Donald',
+      phone: '+25498765432',
+      address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865',
+      type: 'group',
+      created: '11/16/2020',
+      balance: '450',
+      failedPinAttempts: 2,
+      status: 'unapproved',
+      bio: 'Food',
+      gender: 'male',
+    },
+    {
+      id: 4,
+      name: 'Hera Cles',
+      phone: '+25498769876',
+      address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865',
+      type: 'user',
+      created: '05/28/2020',
+      balance: '5621',
+      failedPinAttempts: 3,
+      status: 'approved',
+      bio: 'Shop',
+      gender: 'female',
+    },
+    {
+      id: 5,
+      name: 'Silver Fia',
+      phone: '+25462518374',
+      address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865',
+      type: 'token agent',
+      created: '10/10/2020',
+      balance: '817',
+      failedPinAttempts: 0,
+      status: 'unapproved',
+      bio: 'Electronics',
+      gender: 'male',
+    },
   ];
 
   actions = [
@@ -518,7 +644,13 @@ export class UserServiceStub {
     { id: 3, user: 'Will', role: 'superadmin', action: 'Reclaim RSV 1000', approval: true },
     { id: 4, user: 'Vivian', role: 'enroller', action: 'Complete user profile', approval: true },
     { id: 5, user: 'Jack', role: 'enroller', action: 'Reclaim RSV 200', approval: false },
-    { id: 6, user: 'Patience', role: 'enroller', action: 'Change user information', approval: false }
+    {
+      id: 6,
+      user: 'Patience',
+      role: 'enroller',
+      action: 'Change user information',
+      approval: false,
+    },
   ];
 
   getUserById(id: string): any {
@@ -533,7 +665,7 @@ export class UserServiceStub {
       failedPinAttempts: 1,
       status: 'approved',
       bio: 'Bodaboda',
-      gender: 'male'
+      gender: 'male',
     };
   }
 
@@ -544,20 +676,17 @@ export class UserServiceStub {
       key: {
         ethereum: [
           '0x51d3c8e2e421604e2b644117a362d589c5434739',
-          '0x9D7c284907acbd4a0cE2dDD0AA69147A921a573D'
-        ]
+          '0x9D7c284907acbd4a0cE2dDD0AA69147A921a573D',
+        ],
       },
       location: {
         external: {},
         latitude: '22.430670',
-        longitude: '151.002995'
+        longitude: '151.002995',
       },
-      selling: [
-        'environment',
-        'health',
-        'transport'
-      ],
-      vcard: 'QkVHSU46VkNBUkQNClZFUlNJT046My4wDQpFTUFJTDphYXJuZXNlbkBob3RtYWlsLmNvbQ0KRk46S3VydMKgS3JhbmpjDQpOOktyYW5qYztLdXJ0Ozs7DQpURUw7VFlQPUNFTEw6NjkyNTAzMzQ5ODE5Ng0KRU5EOlZDQVJEDQo='
+      selling: ['environment', 'health', 'transport'],
+      vcard:
+        'QkVHSU46VkNBUkQNClZFUlNJT046My4wDQpFTUFJTDphYXJuZXNlbkBob3RtYWlsLmNvbQ0KRk46S3VydMKgS3JhbmpjDQpOOktyYW5qYztLdXJ0Ozs7DQpURUw7VFlQPUNFTEw6NjkyNTAzMzQ5ODE5Ng0KRU5EOlZDQVJEDQo=',
     });
   }
 
@@ -567,7 +696,7 @@ export class UserServiceStub {
       user: 'Tom',
       role: 'enroller',
       action: 'Disburse RSV 100',
-      approval: false
+      approval: false,
     };
   }
 
@@ -577,7 +706,7 @@ export class UserServiceStub {
       user: 'Tom',
       role: 'enroller',
       action: 'Disburse RSV 100',
-      approval: true
+      approval: true,
     };
   }
 }
diff --git a/docs/compodoc/components/AccountDetailsComponent.html b/docs/compodoc/components/AccountDetailsComponent.html
index 8357abb..265c7d2 100644
--- a/docs/compodoc/components/AccountDetailsComponent.html
+++ b/docs/compodoc/components/AccountDetailsComponent.html
@@ -2348,30 +2348,44 @@ export class AccountDetailsComponent implements OnInit {
         <ol class="breadcrumb">
           <li class="breadcrumb-item"><a routerLink="/home">Home</a></li>
           <li class="breadcrumb-item"><a routerLink="/accounts">Accounts</a></li>
-          <li *ngIf="account" class="breadcrumb-item active" aria-current="page">{{account?.vcard?.fn[0].value}}</li>
+          <li *ngIf="account" class="breadcrumb-item active" aria-current="page">
+            {{ account?.vcard?.fn[0].value }}
+          </li>
         </ol>
       </nav>
       <div *ngIf="!account" class="text-center">
-        <div class="spinner-grow text-primary m-1" role="status" style="width: 3rem; height: 3rem;">
+        <div class="spinner-grow text-primary m-1" role="status" style="width: 3rem; height: 3rem">
           <span class="sr-only">Loading...</span>
         </div>
-        <div class="spinner-grow text-primary m-1" role="status" style="width: 3rem; height: 3rem;">
+        <div class="spinner-grow text-primary m-1" role="status" style="width: 3rem; height: 3rem">
           <span class="sr-only">Loading...</span>
         </div>
-        <div class="spinner-grow text-primary m-1" role="status" style="width: 3rem; height: 3rem;">
+        <div class="spinner-grow text-primary m-1" role="status" style="width: 3rem; height: 3rem">
           <span class="sr-only">Loading...</span>
         </div>
       </div>
       <div *ngIf="account" class="card mb-3">
         <div class="row card-body">
           <h3>
-            <strong> {{account?.vcard?.fn[0].value}} </strong>
+            <strong> {{ account?.vcard?.fn[0].value }} </strong>
           </h3>
-          <span class="ml-auto"><strong>Balance:</strong> {{account?.balance | tokenRatio}} {{ tokenSymbol | uppercase }}</span>
-          <span class="ml-2"><strong>Created:</strong> {{account?.date_registered | unixDate}}</span>
-          <span class="ml-2"><strong>Address:</strong>
-            <a href="{{bloxbergLink}}" target="_blank"> {{accountAddress}} </a>
-            <img src="assets/images/checklist.svg" class="ml-2" height="20rem" (click)="copyAddress()" alt="Copy">
+          <span class="ml-auto"
+            ><strong>Balance:</strong> {{ account?.balance | tokenRatio }}
+            {{ tokenSymbol | uppercase }}</span
+          >
+          <span class="ml-2"
+            ><strong>Created:</strong> {{ account?.date_registered | unixDate }}</span
+          >
+          <span class="ml-2"
+            ><strong>Address:</strong>
+            <a href="{{ bloxbergLink }}" target="_blank"> {{ accountAddress }} </a>
+            <img
+              src="assets/images/checklist.svg"
+              class="ml-2"
+              height="20rem"
+              (click)="copyAddress()"
+              alt="Copy"
+            />
           </span>
         </div>
       </div>
@@ -2379,85 +2393,145 @@ export class AccountDetailsComponent implements OnInit {
         <div class="card-body">
           <form [formGroup]="accountInfoForm" (ngSubmit)="saveInfo()">
             <div class="row form-inline">
-
               <div class="col-md-6 col-lg-4">
                 <mat-form-field appearance="outline">
                   <mat-label>First Name: *</mat-label>
-                  <input matInput type="text" id="firstName" placeholder="{{account?.vcard?.fn[0].value.split(' ')[0]}}"
-                         value="{{account?.vcard?.fn[0].value.split(' ')[0]}}" formControlName="firstName" [errorStateMatcher]="matcher">
-                  <mat-error *ngIf="submitted && accountInfoFormStub.firstName.errors">First Name is required.</mat-error>
+                  <input
+                    matInput
+                    type="text"
+                    id="firstName"
+                    placeholder="{{ account?.vcard?.fn[0].value.split(' ')[0] }}"
+                    value="{{ account?.vcard?.fn[0].value.split(' ')[0] }}"
+                    formControlName="firstName"
+                    [errorStateMatcher]="matcher"
+                  />
+                  <mat-error *ngIf="submitted && accountInfoFormStub.firstName.errors"
+                    >First Name is required.</mat-error
+                  >
                 </mat-form-field>
               </div>
 
               <div class="col-md-6 col-lg-4">
                 <mat-form-field appearance="outline">
                   <mat-label>Last Name(s): *</mat-label>
-                  <input matInput type="text" id="lastName" placeholder="{{account?.vcard?.fn[0].value.split(' ').slice(1).join(' ')}}"
-                         value="{{account?.vcard?.fn[0].value.split(' ').slice(1).join(' ')}}" formControlName="lastName" [errorStateMatcher]="matcher">
-                  <mat-error *ngIf="submitted && accountInfoFormStub.lastName.errors">Last Name is required.</mat-error>
+                  <input
+                    matInput
+                    type="text"
+                    id="lastName"
+                    placeholder="{{ account?.vcard?.fn[0].value.split(' ').slice(1).join(' ') }}"
+                    value="{{ account?.vcard?.fn[0].value.split(' ').slice(1).join(' ') }}"
+                    formControlName="lastName"
+                    [errorStateMatcher]="matcher"
+                  />
+                  <mat-error *ngIf="submitted && accountInfoFormStub.lastName.errors"
+                    >Last Name is required.</mat-error
+                  >
                 </mat-form-field>
               </div>
 
               <div class="col-md-6 col-lg-4">
                 <mat-form-field appearance="outline">
                   <mat-label>Phone Number: </mat-label>
-                  <input matInput type="text" id="phoneNumber" placeholder="{{account?.vcard?.tel[0].value}}"
-                         value="{{account?.vcard?.tel[0].value}}" formControlName="phoneNumber" [errorStateMatcher]="matcher">
-                  <mat-error *ngIf="submitted && accountInfoFormStub.phoneNumber.errors">Phone Number is required.</mat-error>
+                  <input
+                    matInput
+                    type="text"
+                    id="phoneNumber"
+                    placeholder="{{ account?.vcard?.tel[0].value }}"
+                    value="{{ account?.vcard?.tel[0].value }}"
+                    formControlName="phoneNumber"
+                    [errorStateMatcher]="matcher"
+                  />
+                  <mat-error *ngIf="submitted && accountInfoFormStub.phoneNumber.errors"
+                    >Phone Number is required.</mat-error
+                  >
                 </mat-form-field>
               </div>
 
               <div class="col-md-6 col-lg-4">
                 <mat-form-field appearance="outline">
                   <mat-label>Age: </mat-label>
-                  <input matInput type="text" id="age" placeholder="{{account?.age}}"
-                         value="{{account?.age}}" formControlName="age" [errorStateMatcher]="matcher">
-                  <mat-error *ngIf="submitted && accountInfoFormStub.age.errors">Age is required.</mat-error>
+                  <input
+                    matInput
+                    type="text"
+                    id="age"
+                    placeholder="{{ account?.age }}"
+                    value="{{ account?.age }}"
+                    formControlName="age"
+                    [errorStateMatcher]="matcher"
+                  />
+                  <mat-error *ngIf="submitted && accountInfoFormStub.age.errors"
+                    >Age is required.</mat-error
+                  >
                 </mat-form-field>
               </div>
 
               <div class="col-md-6 col-lg-4">
                 <mat-form-field appearance="outline">
                   <mat-label> ACCOUNT TYPE: </mat-label>
-                  <mat-select id="accountType" [(value)]="account.type" formControlName="type"
-                              [errorStateMatcher]="matcher">
+                  <mat-select
+                    id="accountType"
+                    [(value)]="account.type"
+                    formControlName="type"
+                    [errorStateMatcher]="matcher"
+                  >
                     <mat-option *ngFor="let accountType of accountTypes" [value]="accountType">
-                      {{accountType | uppercase}}
+                      {{ accountType | uppercase }}
                     </mat-option>
                   </mat-select>
-                  <mat-error *ngIf="submitted && accountInfoFormStub.type.errors">Type is required.</mat-error>
+                  <mat-error *ngIf="submitted && accountInfoFormStub.type.errors"
+                    >Type is required.</mat-error
+                  >
                 </mat-form-field>
               </div>
 
               <div class="col-md-6 col-lg-4">
                 <mat-form-field appearance="outline">
                   <mat-label>Bio: </mat-label>
-                  <input matInput type="text" id="bio" placeholder="{{account?.products}}" value="{{account?.products}}"
-                         formControlName="bio" [errorStateMatcher]="matcher">
-                  <mat-error *ngIf="submitted && accountInfoFormStub.bio.errors">Bio is required.</mat-error>
+                  <input
+                    matInput
+                    type="text"
+                    id="bio"
+                    placeholder="{{ account?.products }}"
+                    value="{{ account?.products }}"
+                    formControlName="bio"
+                    [errorStateMatcher]="matcher"
+                  />
+                  <mat-error *ngIf="submitted && accountInfoFormStub.bio.errors"
+                    >Bio is required.</mat-error
+                  >
                 </mat-form-field>
               </div>
 
               <div class="col-md-6 col-lg-4">
                 <mat-form-field appearance="outline">
                   <mat-label> GENDER: </mat-label>
-                  <mat-select id="gender" [(value)]="account.gender" formControlName="gender"
-                              [errorStateMatcher]="matcher">
+                  <mat-select
+                    id="gender"
+                    [(value)]="account.gender"
+                    formControlName="gender"
+                    [errorStateMatcher]="matcher"
+                  >
                     <mat-option *ngFor="let gender of genders" [value]="gender">
-                      {{gender | uppercase}}
+                      {{ gender | uppercase }}
                     </mat-option>
                   </mat-select>
-                  <mat-error *ngIf="submitted && accountInfoFormStub.gender.errors">Gender is required.</mat-error>
+                  <mat-error *ngIf="submitted && accountInfoFormStub.gender.errors"
+                    >Gender is required.</mat-error
+                  >
                 </mat-form-field>
               </div>
 
               <div class="col-md-6 col-lg-4">
                 <mat-form-field appearance="outline">
                   <mat-label> BUSINESS CATEGORY: </mat-label>
-                  <mat-select id="businessCategory" [(value)]="account.category" formControlName="businessCategory"
-                              [errorStateMatcher]="matcher">
+                  <mat-select
+                    id="businessCategory"
+                    [(value)]="account.category"
+                    formControlName="businessCategory"
+                    [errorStateMatcher]="matcher"
+                  >
                     <mat-option *ngFor="let category of categories" [value]="category">
-                      {{category | titlecase}}
+                      {{ category | titlecase }}
                     </mat-option>
                   </mat-select>
                   <mat-error *ngIf="submitted && accountInfoFormStub.businessCategory.errors">
@@ -2469,9 +2543,15 @@ export class AccountDetailsComponent implements OnInit {
               <div class="col-md-6 col-lg-4">
                 <mat-form-field appearance="outline">
                   <mat-label>User Location: </mat-label>
-                  <input matInput type="text" id="userLocation" placeholder="{{account?.location.area_name}}"
-                         value="{{account?.location.area_name}}" formControlName="userLocation"
-                         [errorStateMatcher]="matcher">
+                  <input
+                    matInput
+                    type="text"
+                    id="userLocation"
+                    placeholder="{{ account?.location.area_name }}"
+                    value="{{ account?.location.area_name }}"
+                    formControlName="userLocation"
+                    [errorStateMatcher]="matcher"
+                  />
                   <mat-error *ngIf="submitted && accountInfoFormStub.userLocation.errors">
                     User Location is required.
                   </mat-error>
@@ -2481,50 +2561,82 @@ export class AccountDetailsComponent implements OnInit {
               <div class="col-md-6 col-lg-4">
                 <mat-form-field appearance="outline">
                   <mat-label> LOCATION: </mat-label>
-                  <mat-select id="location" [(value)]="account.location.area" formControlName="location"
-                              [errorStateMatcher]="matcher">
+                  <mat-select
+                    id="location"
+                    [(value)]="account.location.area"
+                    formControlName="location"
+                    [errorStateMatcher]="matcher"
+                  >
                     <mat-option *ngFor="let area of areaNames" [value]="area">
-                      {{area | uppercase}}
+                      {{ area | uppercase }}
                     </mat-option>
                   </mat-select>
-                  <mat-error *ngIf="submitted && accountInfoFormStub.location.errors">Location is required.</mat-error>
+                  <mat-error *ngIf="submitted && accountInfoFormStub.location.errors"
+                    >Location is required.</mat-error
+                  >
                 </mat-form-field>
               </div>
 
               <div class="col-md-6 col-lg-4">
                 <mat-form-field appearance="outline">
                   <mat-label> LOCATION TYPE: </mat-label>
-                  <mat-select id="locationType" [(value)]="account.location.area_type" formControlName="locationType"
-                              [errorStateMatcher]="matcher">
+                  <mat-select
+                    id="locationType"
+                    [(value)]="account.location.area_type"
+                    formControlName="locationType"
+                    [errorStateMatcher]="matcher"
+                  >
                     <mat-option *ngFor="let type of areaTypes" [value]="type">
-                      {{type | uppercase}}
+                      {{ type | uppercase }}
                     </mat-option>
                   </mat-select>
-                  <mat-error *ngIf="submitted && accountInfoFormStub.locationType.errors">Location Type is required.</mat-error>
+                  <mat-error *ngIf="submitted && accountInfoFormStub.locationType.errors"
+                    >Location Type is required.</mat-error
+                  >
                 </mat-form-field>
               </div>
 
               <div class="col-md-6 col-lg-4">
-                <button mat-raised-button color="primary" type="button" class="btn btn-outline-primary mb-3">
+                <button
+                  mat-raised-button
+                  color="primary"
+                  type="button"
+                  class="btn btn-outline-primary mb-3"
+                >
                   Add User KYC
                 </button>
               </div>
 
               <div class="col-md-6 col-lg-4">
-                <button mat-raised-button color="primary" type="button" class="btn btn-outline-success mb-3"
-                        (click)="resetPin()">
+                <button
+                  mat-raised-button
+                  color="primary"
+                  type="button"
+                  class="btn btn-outline-success mb-3"
+                  (click)="resetPin()"
+                >
                   Reset Pin
                 </button>
               </div>
 
               <div class="col-md-6 col-lg-4">
-                <button mat-raised-button color="warn" type="button" class="btn btn-outline-danger mb-3">
+                <button
+                  mat-raised-button
+                  color="warn"
+                  type="button"
+                  class="btn btn-outline-danger mb-3"
+                >
                   Delete User
                 </button>
               </div>
 
               <div class="col-md-6 col-lg-4">
-                <button mat-raised-button color="primary" type="submit" class="btn btn-outline-primary">
+                <button
+                  mat-raised-button
+                  color="primary"
+                  type="submit"
+                  class="btn btn-outline-primary"
+                >
                   SAVE DETAILS
                 </button>
               </div>
@@ -2534,32 +2646,32 @@ export class AccountDetailsComponent implements OnInit {
       </div>
 
       <div class="card mb-3">
-        <mat-card-title class="card-header">
-          USER
-        </mat-card-title>
+        <mat-card-title class="card-header"> USER </mat-card-title>
         <div class="card-body">
           <div class="table-responsive">
             <table class="table table-striped table-bordered table-hover">
-              <caption> 1 user </caption>
+              <caption>
+                1 user
+              </caption>
               <thead class="thead-dark">
-              <tr>
-                <th scope="col">NAME</th>
-                <th scope="col">BALANCE</th>
-                <th scope="col">CREATED</th>
-                <th scope="col">STATUS</th>
-              </tr>
+                <tr>
+                  <th scope="col">NAME</th>
+                  <th scope="col">BALANCE</th>
+                  <th scope="col">CREATED</th>
+                  <th scope="col">STATUS</th>
+                </tr>
               </thead>
               <tbody>
-              <tr>
-                <td>{{account?.vcard?.fn[0].value}}</td>
-                <td>{{account?.balance | tokenRatio}} {{ tokenSymbol | uppercase }}</td>
-                <td>{{account?.date_registered | unixDate}}</td>
-                <td>
-                  <span class="badge badge-success badge-pill">
-                    {{accountStatus}}
-                  </span>
-                </td>
-              </tr>
+                <tr>
+                  <td>{{ account?.vcard?.fn[0].value }}</td>
+                  <td>{{ account?.balance | tokenRatio }} {{ tokenSymbol | uppercase }}</td>
+                  <td>{{ account?.date_registered | unixDate }}</td>
+                  <td>
+                    <span class="badge badge-success badge-pill">
+                      {{ accountStatus }}
+                    </span>
+                  </td>
+                </tr>
               </tbody>
             </table>
           </div>
@@ -2568,135 +2680,216 @@ export class AccountDetailsComponent implements OnInit {
 
       <mat-tab-group *ngIf="account" dynamicHeight mat-align-tabs="start">
         <mat-tab label="Transactions">
-          <app-transaction-details [transaction]="transaction" (closeWindow)="transaction = $event"></app-transaction-details>
+          <app-transaction-details
+            [transaction]="transaction"
+            (closeWindow)="transaction = $event"
+          ></app-transaction-details>
           <div class="card mt-1">
             <div class="card-header">
               <div class="row">
                 <mat-form-field appearance="outline">
                   <mat-label> TRANSACTION TYPE </mat-label>
-                  <mat-select id="transferSelect" [(value)]="transactionsType" (selectionChange)="filterTransactions()">
+                  <mat-select
+                    id="transferSelect"
+                    [(value)]="transactionsType"
+                    (selectionChange)="filterTransactions()"
+                  >
                     <mat-option value="all">ALL TRANSFERS</mat-option>
-                    <mat-option *ngFor="let transactionType of transactionsTypes" [value]="transactionType">
-                      {{transactionType | uppercase}}
+                    <mat-option
+                      *ngFor="let transactionType of transactionsTypes"
+                      [value]="transactionType"
+                    >
+                      {{ transactionType | uppercase }}
                     </mat-option>
                   </mat-select>
                 </mat-form-field>
-                <button mat-raised-button color="primary" type="button" class="btn btn-outline-primary ml-auto mr-2" (click)="downloadCsv(transactions, 'transactions')"> EXPORT </button>
+                <button
+                  mat-raised-button
+                  color="primary"
+                  type="button"
+                  class="btn btn-outline-primary ml-auto mr-2"
+                  (click)="downloadCsv(transactions, 'transactions')"
+                >
+                  EXPORT
+                </button>
               </div>
             </div>
 
             <div class="card-body">
               <mat-form-field appearance="outline">
                 <mat-label> Filter </mat-label>
-                <input matInput type="text" (keyup)="doTransactionFilter($event.target.value)" placeholder="Filter">
+                <input
+                  matInput
+                  type="text"
+                  (keyup)="doTransactionFilter($event.target.value)"
+                  placeholder="Filter"
+                />
                 <mat-icon matSuffix>search</mat-icon>
               </mat-form-field>
 
-              <table mat-table class="mat-elevation-z10" [dataSource]="transactionsDataSource" matSort matSortActive="created"
-                         #TransactionTableSort="matSort" matSortDirection="asc" matSortDisableClear>
-
+              <table
+                mat-table
+                class="mat-elevation-z10"
+                [dataSource]="transactionsDataSource"
+                matSort
+                matSortActive="created"
+                #TransactionTableSort="matSort"
+                matSortDirection="asc"
+                matSortDisableClear
+              >
                 <ng-container matColumnDef="sender">
-                  <th mat-header-cell *matHeaderCellDef mat-sort-header> Sender </th>
-                  <td mat-cell *matCellDef="let transaction"> {{transaction?.sender?.vcard.fn[0].value || transaction.from}} </td>
+                  <th mat-header-cell *matHeaderCellDef mat-sort-header>Sender</th>
+                  <td mat-cell *matCellDef="let transaction">
+                    {{ transaction?.sender?.vcard.fn[0].value || transaction.from }}
+                  </td>
                 </ng-container>
 
                 <ng-container matColumnDef="recipient">
-                  <th mat-header-cell *matHeaderCellDef mat-sort-header> Recipient </th>
-                  <td mat-cell *matCellDef="let transaction"> {{transaction?.recipient?.vcard.fn[0].value || transaction.to}} </td>
+                  <th mat-header-cell *matHeaderCellDef mat-sort-header>Recipient</th>
+                  <td mat-cell *matCellDef="let transaction">
+                    {{ transaction?.recipient?.vcard.fn[0].value || transaction.to }}
+                  </td>
                 </ng-container>
 
                 <ng-container matColumnDef="value">
-                  <th mat-header-cell *matHeaderCellDef mat-sort-header> Value </th>
+                  <th mat-header-cell *matHeaderCellDef mat-sort-header>Value</th>
                   <td mat-cell *matCellDef="let transaction">
-                    <span *ngIf="transaction.type == 'transaction'">{{transaction?.value | tokenRatio}} {{ tokenSymbol | uppercase }}</span>
-                    <span *ngIf="transaction.type == 'conversion'">{{transaction?.toValue | tokenRatio}} {{ tokenSymbol | uppercase }}</span>
+                    <span *ngIf="transaction.type == 'transaction'"
+                      >{{ transaction?.value | tokenRatio }} {{ tokenSymbol | uppercase }}</span
+                    >
+                    <span *ngIf="transaction.type == 'conversion'"
+                      >{{ transaction?.toValue | tokenRatio }} {{ tokenSymbol | uppercase }}</span
+                    >
                   </td>
                 </ng-container>
 
                 <ng-container matColumnDef="created">
-                  <th mat-header-cell *matHeaderCellDef mat-sort-header> Created </th>
-                  <td mat-cell *matCellDef="let transaction"> {{transaction?.tx.timestamp | unixDate}} </td>
+                  <th mat-header-cell *matHeaderCellDef mat-sort-header>Created</th>
+                  <td mat-cell *matCellDef="let transaction">
+                    {{ transaction?.tx.timestamp | unixDate }}
+                  </td>
                 </ng-container>
 
                 <ng-container matColumnDef="type">
-                  <th mat-header-cell *matHeaderCellDef mat-sort-header> TYPE </th>
+                  <th mat-header-cell *matHeaderCellDef mat-sort-header>TYPE</th>
                   <td mat-cell *matCellDef="let transaction">
-                    <span class="badge badge-success badge-pill"> {{transaction?.type}} </span>
+                    <span class="badge badge-success badge-pill"> {{ transaction?.type }} </span>
                   </td>
                 </ng-container>
 
                 <tr mat-header-row *matHeaderRowDef="transactionsDisplayedColumns"></tr>
-                <tr mat-row *matRowDef="let transaction; columns: transactionsDisplayedColumns" matRipple
-                         (click)="viewTransaction(transaction)"></tr>
+                <tr
+                  mat-row
+                  *matRowDef="let transaction; columns: transactionsDisplayedColumns"
+                  matRipple
+                  (click)="viewTransaction(transaction)"
+                ></tr>
               </table>
 
-              <mat-paginator #TransactionTablePaginator="matPaginator" [pageSize]="transactionsDefaultPageSize"
-                             [pageSizeOptions]="transactionsPageSizeOptions" showFirstLastButtons></mat-paginator>
-
+              <mat-paginator
+                #TransactionTablePaginator="matPaginator"
+                [pageSize]="transactionsDefaultPageSize"
+                [pageSizeOptions]="transactionsPageSizeOptions"
+                showFirstLastButtons
+              ></mat-paginator>
             </div>
           </div>
         </mat-tab>
 
         <mat-tab label="Users">
           <div class="card mt-1">
-            <mat-card-title class="card-header">
-              Accounts
-            </mat-card-title>
+            <mat-card-title class="card-header"> Accounts </mat-card-title>
             <div class="card-body">
               <div class="row card-header">
                 <mat-form-field appearance="outline">
                   <mat-label> ACCOUNT TYPE </mat-label>
-                  <mat-select id="typeSelect" [(value)]="accountsType" (selectionChange)="filterAccounts()">
+                  <mat-select
+                    id="typeSelect"
+                    [(value)]="accountsType"
+                    (selectionChange)="filterAccounts()"
+                  >
                     <mat-option value="all">ALL</mat-option>
                     <mat-option *ngFor="let accountType of accountTypes" [value]="accountType">
-                      {{accountType | uppercase}}
+                      {{ accountType | uppercase }}
                     </mat-option>
                   </mat-select>
                 </mat-form-field>
-                <button mat-raised-button color="primary" type="button" class="btn btn-outline-primary ml-auto mr-2" (click)="downloadCsv(accounts, 'accounts')"> EXPORT </button>
+                <button
+                  mat-raised-button
+                  color="primary"
+                  type="button"
+                  class="btn btn-outline-primary ml-auto mr-2"
+                  (click)="downloadCsv(accounts, 'accounts')"
+                >
+                  EXPORT
+                </button>
               </div>
 
               <mat-form-field appearance="outline">
                 <mat-label> Filter </mat-label>
-                <input matInput type="text" (keyup)="doUserFilter($event.target.value)" placeholder="Filter">
+                <input
+                  matInput
+                  type="text"
+                  (keyup)="doUserFilter($event.target.value)"
+                  placeholder="Filter"
+                />
                 <mat-icon matSuffix>search</mat-icon>
               </mat-form-field>
 
-              <mat-table class="mat-elevation-z10" [dataSource]="userDataSource" matSort #UserTableSort="matSort"
-                         matSortActive="created" matSortDirection="desc" matSortDisableClear>
-
+              <mat-table
+                class="mat-elevation-z10"
+                [dataSource]="userDataSource"
+                matSort
+                #UserTableSort="matSort"
+                matSortActive="created"
+                matSortDirection="desc"
+                matSortDisableClear
+              >
                 <ng-container matColumnDef="name">
                   <mat-header-cell *matHeaderCellDef mat-sort-header> NAME </mat-header-cell>
-                  <mat-cell *matCellDef="let user"> {{user?.vcard.fn[0].value}} </mat-cell>
+                  <mat-cell *matCellDef="let user"> {{ user?.vcard.fn[0].value }} </mat-cell>
                 </ng-container>
 
                 <ng-container matColumnDef="phone">
-                  <mat-header-cell *matHeaderCellDef mat-sort-header> PHONE NUMBER </mat-header-cell>
-                  <mat-cell *matCellDef="let user"> {{user?.vcard.tel[0].value}} </mat-cell>
+                  <mat-header-cell *matHeaderCellDef mat-sort-header>
+                    PHONE NUMBER
+                  </mat-header-cell>
+                  <mat-cell *matCellDef="let user"> {{ user?.vcard.tel[0].value }} </mat-cell>
                 </ng-container>
 
                 <ng-container matColumnDef="created">
                   <mat-header-cell *matHeaderCellDef mat-sort-header> CREATED </mat-header-cell>
-                  <mat-cell *matCellDef="let user"> {{user?.date_registered | unixDate}} </mat-cell>
+                  <mat-cell *matCellDef="let user">
+                    {{ user?.date_registered | unixDate }}
+                  </mat-cell>
                 </ng-container>
 
                 <ng-container matColumnDef="balance">
                   <mat-header-cell *matHeaderCellDef mat-sort-header> BALANCE </mat-header-cell>
-                  <mat-cell *matCellDef="let user"> {{user?.balance | tokenRatio}} {{tokenSymbol | uppercase}} </mat-cell>
+                  <mat-cell *matCellDef="let user">
+                    {{ user?.balance | tokenRatio }} {{ tokenSymbol | uppercase }}
+                  </mat-cell>
                 </ng-container>
 
                 <ng-container matColumnDef="location">
                   <mat-header-cell *matHeaderCellDef mat-sort-header> LOCATION </mat-header-cell>
-                  <mat-cell *matCellDef="let user"> {{user?.location.area_name}} </mat-cell>
+                  <mat-cell *matCellDef="let user"> {{ user?.location.area_name }} </mat-cell>
                 </ng-container>
 
-                <mat-header-row *matHeaderRowDef=userDisplayedColumns></mat-header-row>
-                <mat-row *matRowDef="let account; columns: userDisplayedColumns" (click)="viewAccount(account)"
-                         matRipple></mat-row>
+                <mat-header-row *matHeaderRowDef="userDisplayedColumns"></mat-header-row>
+                <mat-row
+                  *matRowDef="let account; columns: userDisplayedColumns"
+                  (click)="viewAccount(account)"
+                  matRipple
+                ></mat-row>
               </mat-table>
 
-              <mat-paginator #UserTablePaginator="matPaginator" [pageSize]="usersDefaultPageSize"
-                             [pageSizeOptions]="usersPageSizeOptions" showFirstLastButtons></mat-paginator>
+              <mat-paginator
+                #UserTablePaginator="matPaginator"
+                [pageSize]="usersDefaultPageSize"
+                [pageSizeOptions]="usersPageSizeOptions"
+                showFirstLastButtons
+              ></mat-paginator>
             </div>
           </div>
         </mat-tab>
@@ -2743,7 +2936,7 @@ export class AccountDetailsComponent implements OnInit {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
-  
-  
-
+  
+    
+    CICADA
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+    
+  
+  
+    
+    
+    
+    
+  
 
diff --git a/src/main.ts b/src/main.ts
index c59cf5b..f7f798e 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -8,5 +8,6 @@ if (environment.production) {
   enableProdMode();
 }
 
-platformBrowserDynamic().bootstrapModule(AppModule)
-  .catch(err => console.error(err));
+platformBrowserDynamic()
+  .bootstrapModule(AppModule)
+  .catch((err) => console.error(err));
diff --git a/src/polyfills.ts b/src/polyfills.ts
index 03711e5..e49856e 100644
--- a/src/polyfills.ts
+++ b/src/polyfills.ts
@@ -55,8 +55,7 @@
 /***************************************************************************************************
  * Zone JS is required by default for Angular itself.
  */
-import 'zone.js/dist/zone';  // Included with Angular CLI.
-
+import 'zone.js/dist/zone'; // Included with Angular CLI.
 
 /***************************************************************************************************
  * APPLICATION IMPORTS
diff --git a/src/styles.scss b/src/styles.scss
index 1a4a579..9508c62 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -6,11 +6,13 @@
   /* Visual */
   /* Misc */
 }
-@import "~bootstrap/dist/css/bootstrap.css";
-@import "https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap";
+@import '~bootstrap/dist/css/bootstrap.css';
+@import 'https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap';
 
 html,
-body { height: 100%; }
+body {
+  height: 100%;
+}
 
 body {
   margin: 0;
@@ -35,7 +37,7 @@ a:hover,
 a:focus {
   text-decoration: none;
   color: inherit;
-  transition: all .3s;
+  transition: all 0.3s;
 }
 
 .wrapper {
@@ -50,15 +52,19 @@ a:focus {
 
 ul ul a {
   padding-left: 30px;
-  font-size: .9em;
+  font-size: 0.9em;
   background: #6d7fcc;
 }
 
 .full-width,
-table { width: 100%; }
+table {
+  width: 100%;
+}
 
 li.breadcrumb-item.active,
-footer.footer { color: black; }
+footer.footer {
+  color: black;
+}
 
 .clipboard {
   position: absolute;
@@ -91,9 +97,13 @@ footer.footer { color: black; }
 }
 
 #sidebar .sidebar-header strong,
-#sidebar.active .sidebar-header h3 { display: none; }
+#sidebar.active .sidebar-header h3 {
+  display: none;
+}
 
-#sidebar.active .sidebar-header strong { display: block; }
+#sidebar.active .sidebar-header strong {
+  display: block;
+}
 
 #sidebar ul li a {
   display: block;
@@ -104,11 +114,13 @@ footer.footer { color: black; }
 }
 
 #sidebar ul li a.active,
-#sidebar.active ul li a.active { background: #000; }
+#sidebar.active ul li a.active {
+  background: #000;
+}
 
 #sidebar.active ul li a {
   padding: 20px 10px;
-  font-size: .85em;
+  font-size: 0.85em;
   text-align: center;
 }
 
@@ -119,7 +131,9 @@ footer.footer { color: black; }
   font-size: 1.8em;
 }
 
-#sidebar.active ul li a { padding: 10px; }
+#sidebar.active ul li a {
+  padding: 10px;
+}
 
 #sidebar.active .dropdown-toggle::after {
   top: auto;
@@ -146,12 +160,14 @@ footer.footer { color: black; }
 }
 
 #sidebar ul li.active > a,
-a[aria-expanded="true"] {
+a[aria-expanded='true'] {
   color: #fff;
   background: #313a46;
 }
 
-a[data-toggle="collapse"] { position: relative; }
+a[data-toggle='collapse'] {
+  position: relative;
+}
 
 .dropdown-toggle::after {
   position: absolute;
@@ -170,7 +186,9 @@ a[data-toggle="collapse"] { position: relative; }
 #sidebar,
 #sidebar.active,
 #content,
-#content.active { transition: all .3s cubic-bezier(.945, .020, .270, .665); }
+#content.active {
+  transition: all 0.3s cubic-bezier(0.945, 0.02, 0.27, 0.665);
+}
 
 #sidebarCollapse {
   width: 40px;
@@ -184,14 +202,20 @@ a[data-toggle="collapse"] { position: relative; }
   height: 2px;
   margin: 0 auto;
   background: #555;
-  transition: all .8s cubic-bezier(.810, -.330, .345, 1.375);
+  transition: all 0.8s cubic-bezier(0.81, -0.33, 0.345, 1.375);
 }
 
-#sidebarCollapse span:first-of-type { transform: rotate(45deg) translate(2px, 2px); }
+#sidebarCollapse span:first-of-type {
+  transform: rotate(45deg) translate(2px, 2px);
+}
 
-#sidebarCollapse span:nth-of-type(2) { opacity: 0; }
+#sidebarCollapse span:nth-of-type(2) {
+  opacity: 0;
+}
 
-#sidebarCollapse span:last-of-type { transform: rotate(-45deg) translate(1px, -1px); }
+#sidebarCollapse span:last-of-type {
+  transform: rotate(-45deg) translate(1px, -1px);
+}
 
 #sidebarCollapse.active span {
   margin: 5px auto;
@@ -205,11 +229,17 @@ a[data-toggle="collapse"] { position: relative; }
   color: #98a6ad;
 }
 
-.mat-column-select { overflow: initial; }
+.mat-column-select {
+  overflow: initial;
+}
 
-button { height: 2.5rem; }
+button {
+  height: 2.5rem;
+}
 
-.badge-pill { width: 5rem; }
+.badge-pill {
+  width: 5rem;
+}
 
 .mat-column {
   word-wrap: break-word;
@@ -246,14 +276,18 @@ button { height: 2.5rem; }
   }
 
   #sidebar .sidebar-header strong,
-  #sidebar.active .sidebar-header h3 { display: none; }
+  #sidebar.active .sidebar-header h3 {
+    display: none;
+  }
 
   #sidebar.active .sidebar-header strong,
-  #sidebar.active ul li a i { display: block; }
+  #sidebar.active ul li a i {
+    display: block;
+  }
 
   #sidebar.active ul li a {
     padding: 20px 10px;
-    font-size: .85em;
+    font-size: 0.85em;
   }
 
   #sidebar.active ul li a i {
@@ -262,7 +296,9 @@ button { height: 2.5rem; }
     font-size: 1.8em;
   }
 
-  #sidebar.active ul ul a { padding: 10px; }
+  #sidebar.active ul ul a {
+    padding: 10px;
+  }
 
   .dropdown-toggle::after {
     top: auto;
@@ -279,15 +315,21 @@ button { height: 2.5rem; }
     height: 100vh;
   }
 
-  #content .menutoggle { margin-left: 250px; }
+  #content .menutoggle {
+    margin-left: 250px;
+  }
 
   #sidebar,
   #content,
   #content.active,
-  #content.active .menutoggle { margin-left: 0; }
+  #content.active .menutoggle {
+    margin-left: 0;
+  }
 
   #content .menutoggle,
-  #content.active .menutoggle { transition: all .3s cubic-bezier(.945, .020, .270, .665); }
+  #content.active .menutoggle {
+    transition: all 0.3s cubic-bezier(0.945, 0.02, 0.27, 0.665);
+  }
 
   #sidebarCollapse span:first-of-type,
   #sidebarCollapse span:nth-of-type(2),
@@ -297,11 +339,19 @@ button { height: 2.5rem; }
     opacity: 1;
   }
 
-  #sidebarCollapse.active span { margin: 0 auto; }
+  #sidebarCollapse.active span {
+    margin: 0 auto;
+  }
 
-  #sidebarCollapse.active span:first-of-type { transform: rotate(45deg) translate(2px, 2px); }
+  #sidebarCollapse.active span:first-of-type {
+    transform: rotate(45deg) translate(2px, 2px);
+  }
 
-  #sidebarCollapse.active span:nth-of-type(2) { opacity: 0; }
+  #sidebarCollapse.active span:nth-of-type(2) {
+    opacity: 0;
+  }
 
-  #sidebarCollapse.active span:last-of-type { transform: rotate(-45deg) translate(1px, -1px); }
+  #sidebarCollapse.active span:last-of-type {
+    transform: rotate(-45deg) translate(1px, -1px);
+  }
 }
diff --git a/src/test.ts b/src/test.ts
index 50193eb..a37d89c 100644
--- a/src/test.ts
+++ b/src/test.ts
@@ -4,21 +4,22 @@ import 'zone.js/dist/zone-testing';
 import { getTestBed } from '@angular/core/testing';
 import {
   BrowserDynamicTestingModule,
-  platformBrowserDynamicTesting
+  platformBrowserDynamicTesting,
 } from '@angular/platform-browser-dynamic/testing';
 
 declare const require: {
-  context(path: string, deep?: boolean, filter?: RegExp): {
+  context(
+    path: string,
+    deep?: boolean,
+    filter?: RegExp
+  ): {
     keys(): string[];
     (id: string): T;
   };
 };
 
 // First, initialize the Angular testing environment.
-getTestBed().initTestEnvironment(
-  BrowserDynamicTestingModule,
-  platformBrowserDynamicTesting()
-);
+getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
 // Then we find all the tests.
 const context = require.context('./', true, /\.spec\.ts$/);
 // And load the modules.
diff --git a/src/testing/router-link-directive-stub.ts b/src/testing/router-link-directive-stub.ts
index 1311caa..a7ba6df 100644
--- a/src/testing/router-link-directive-stub.ts
+++ b/src/testing/router-link-directive-stub.ts
@@ -1,7 +1,7 @@
-import {Directive, HostListener, Input} from '@angular/core';
+import { Directive, HostListener, Input } from '@angular/core';
 
 @Directive({
-  selector: '[appRouterLink]'
+  selector: '[appRouterLink]',
 })
 // tslint:disable-next-line:directive-class-suffix
 export class RouterLinkDirectiveStub {
diff --git a/src/testing/shared-module-stub.ts b/src/testing/shared-module-stub.ts
index d74d246..088f463 100644
--- a/src/testing/shared-module-stub.ts
+++ b/src/testing/shared-module-stub.ts
@@ -1,10 +1,10 @@
-import {Component} from '@angular/core';
+import { Component } from '@angular/core';
 
-@Component({selector: 'app-sidebar', template: ''})
+@Component({ selector: 'app-sidebar', template: '' })
 export class SidebarStubComponent {}
 
-@Component({selector: 'app-topbar', template: ''})
+@Component({ selector: 'app-topbar', template: '' })
 export class TopbarStubComponent {}
 
-@Component({selector: 'app-footer', template: ''})
+@Component({ selector: 'app-footer', template: '' })
 export class FooterStubComponent {}
diff --git a/src/testing/token-service-stub.ts b/src/testing/token-service-stub.ts
index 7cba280..ba96686 100644
--- a/src/testing/token-service-stub.ts
+++ b/src/testing/token-service-stub.ts
@@ -2,7 +2,7 @@ export class TokenServiceStub {
   getBySymbol(symbol: string): any {
     return {
       name: 'Reserve',
-      symbol: 'RSV'
+      symbol: 'RSV',
     };
   }
 }
diff --git a/src/testing/transaction-service-stub.ts b/src/testing/transaction-service-stub.ts
index 5b05098..d026e3c 100644
--- a/src/testing/transaction-service-stub.ts
+++ b/src/testing/transaction-service-stub.ts
@@ -1,4 +1,4 @@
-import {Observable, of} from 'rxjs';
+import { Observable, of } from 'rxjs';
 
 export class TransactionServiceStub {
   setTransaction(transaction: any, cacheSize: number): void {}
diff --git a/src/testing/user-service-stub.ts b/src/testing/user-service-stub.ts
index ba19a02..f549542 100644
--- a/src/testing/user-service-stub.ts
+++ b/src/testing/user-service-stub.ts
@@ -1,12 +1,72 @@
-import {Observable, of} from 'rxjs';
+import { Observable, of } from 'rxjs';
 
 export class UserServiceStub {
   users = [
-    {id: 1, name: 'John Doe', phone: '+25412345678', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'user', created: '08/16/2020', balance: '12987', failedPinAttempts: 1, status: 'approved', bio: 'Bodaboda', gender: 'male'},
-    {id: 2, name: 'Jane Buck', phone: '+25412341234', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'vendor', created: '04/02/2020', balance: '56281', failedPinAttempts: 0, status: 'approved', bio: 'Groceries', gender: 'female'},
-    {id: 3, name: 'Mc Donald', phone: '+25498765432', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'group', created: '11/16/2020', balance: '450', failedPinAttempts: 2, status: 'unapproved', bio: 'Food', gender: 'male'},
-    {id: 4, name: 'Hera Cles', phone: '+25498769876', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'user', created: '05/28/2020', balance: '5621', failedPinAttempts: 3, status: 'approved', bio: 'Shop', gender: 'female'},
-    {id: 5, name: 'Silver Fia', phone: '+25462518374', address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865', type: 'token agent', created: '10/10/2020', balance: '817', failedPinAttempts: 0, status: 'unapproved', bio: 'Electronics', gender: 'male'},
+    {
+      id: 1,
+      name: 'John Doe',
+      phone: '+25412345678',
+      address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865',
+      type: 'user',
+      created: '08/16/2020',
+      balance: '12987',
+      failedPinAttempts: 1,
+      status: 'approved',
+      bio: 'Bodaboda',
+      gender: 'male',
+    },
+    {
+      id: 2,
+      name: 'Jane Buck',
+      phone: '+25412341234',
+      address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865',
+      type: 'vendor',
+      created: '04/02/2020',
+      balance: '56281',
+      failedPinAttempts: 0,
+      status: 'approved',
+      bio: 'Groceries',
+      gender: 'female',
+    },
+    {
+      id: 3,
+      name: 'Mc Donald',
+      phone: '+25498765432',
+      address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865',
+      type: 'group',
+      created: '11/16/2020',
+      balance: '450',
+      failedPinAttempts: 2,
+      status: 'unapproved',
+      bio: 'Food',
+      gender: 'male',
+    },
+    {
+      id: 4,
+      name: 'Hera Cles',
+      phone: '+25498769876',
+      address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865',
+      type: 'user',
+      created: '05/28/2020',
+      balance: '5621',
+      failedPinAttempts: 3,
+      status: 'approved',
+      bio: 'Shop',
+      gender: 'female',
+    },
+    {
+      id: 5,
+      name: 'Silver Fia',
+      phone: '+25462518374',
+      address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865',
+      type: 'token agent',
+      created: '10/10/2020',
+      balance: '817',
+      failedPinAttempts: 0,
+      status: 'unapproved',
+      bio: 'Electronics',
+      gender: 'male',
+    },
   ];
 
   actions = [
@@ -15,7 +75,13 @@ export class UserServiceStub {
     { id: 3, user: 'Will', role: 'superadmin', action: 'Reclaim RSV 1000', approval: true },
     { id: 4, user: 'Vivian', role: 'enroller', action: 'Complete user profile', approval: true },
     { id: 5, user: 'Jack', role: 'enroller', action: 'Reclaim RSV 200', approval: false },
-    { id: 6, user: 'Patience', role: 'enroller', action: 'Change user information', approval: false }
+    {
+      id: 6,
+      user: 'Patience',
+      role: 'enroller',
+      action: 'Change user information',
+      approval: false,
+    },
   ];
 
   getUserById(id: string): any {
@@ -30,7 +96,7 @@ export class UserServiceStub {
       failedPinAttempts: 1,
       status: 'approved',
       bio: 'Bodaboda',
-      gender: 'male'
+      gender: 'male',
     };
   }
 
@@ -41,20 +107,17 @@ export class UserServiceStub {
       key: {
         ethereum: [
           '0x51d3c8e2e421604e2b644117a362d589c5434739',
-          '0x9D7c284907acbd4a0cE2dDD0AA69147A921a573D'
-        ]
+          '0x9D7c284907acbd4a0cE2dDD0AA69147A921a573D',
+        ],
       },
       location: {
         external: {},
         latitude: '22.430670',
-        longitude: '151.002995'
+        longitude: '151.002995',
       },
-      selling: [
-        'environment',
-        'health',
-        'transport'
-      ],
-      vcard: 'QkVHSU46VkNBUkQNClZFUlNJT046My4wDQpFTUFJTDphYXJuZXNlbkBob3RtYWlsLmNvbQ0KRk46S3VydMKgS3JhbmpjDQpOOktyYW5qYztLdXJ0Ozs7DQpURUw7VFlQPUNFTEw6NjkyNTAzMzQ5ODE5Ng0KRU5EOlZDQVJEDQo='
+      selling: ['environment', 'health', 'transport'],
+      vcard:
+        'QkVHSU46VkNBUkQNClZFUlNJT046My4wDQpFTUFJTDphYXJuZXNlbkBob3RtYWlsLmNvbQ0KRk46S3VydMKgS3JhbmpjDQpOOktyYW5qYztLdXJ0Ozs7DQpURUw7VFlQPUNFTEw6NjkyNTAzMzQ5ODE5Ng0KRU5EOlZDQVJEDQo=',
     });
   }
 
@@ -64,7 +127,7 @@ export class UserServiceStub {
       user: 'Tom',
       role: 'enroller',
       action: 'Disburse RSV 100',
-      approval: false
+      approval: false,
     };
   }
 
@@ -74,7 +137,7 @@ export class UserServiceStub {
       user: 'Tom',
       role: 'enroller',
       action: 'Disburse RSV 100',
-      approval: true
+      approval: true,
     };
   }
 }