Merge branch 'master' into spencer/keystore-singleton
# Conflicts: # src/app/_services/transaction.service.ts
This commit is contained in:
@@ -1,14 +1,36 @@
|
||||
// Third party imports
|
||||
import Web3 from 'web3';
|
||||
import { Web3Service } from '@app/_services/web3.service';
|
||||
|
||||
// Application imports
|
||||
import { Web3Service } from '@app/_services/web3.service';
|
||||
import { environment } from '@src/environments/environment';
|
||||
|
||||
/** Fetch the account registry contract's ABI. */
|
||||
const abi: Array<any> = require('@src/assets/js/block-sync/data/AccountsIndex.json');
|
||||
/** Establish a connection to the blockchain network. */
|
||||
const web3: Web3 = Web3Service.getInstance();
|
||||
|
||||
/**
|
||||
* Provides an instance of the accounts registry contract.
|
||||
* Allows querying of accounts that have been registered as valid accounts in the network.
|
||||
*
|
||||
* @remarks
|
||||
* This is our interface to the accounts registry contract.
|
||||
*/
|
||||
export class AccountIndex {
|
||||
contractAddress: string;
|
||||
signerAddress: string;
|
||||
/** The instance of the account registry contract. */
|
||||
contract: any;
|
||||
/** The deployed account registry contract's address. */
|
||||
contractAddress: string;
|
||||
/** The account address of the account that deployed the account registry contract. */
|
||||
signerAddress: string;
|
||||
|
||||
/**
|
||||
* Create a connection to the deployed account registry contract.
|
||||
*
|
||||
* @param contractAddress - The deployed account registry contract's address.
|
||||
* @param signerAddress - The account address of the account that deployed the account registry contract.
|
||||
*/
|
||||
constructor(contractAddress: string, signerAddress?: string) {
|
||||
this.contractAddress = contractAddress;
|
||||
this.contract = new web3.eth.Contract(abi, this.contractAddress);
|
||||
@@ -19,14 +41,58 @@ export class AccountIndex {
|
||||
}
|
||||
}
|
||||
|
||||
public async totalAccounts(): Promise<number> {
|
||||
return await this.contract.methods.entryCount().call();
|
||||
/**
|
||||
* Registers an account to the accounts registry.
|
||||
* Requires availability of the signer address.
|
||||
*
|
||||
* @async
|
||||
* @example
|
||||
* Prints "true" for registration of '0xc0ffee254729296a45a3885639AC7E10F9d54979':
|
||||
* ```typescript
|
||||
* console.log(await addToAccountRegistry('0xc0ffee254729296a45a3885639AC7E10F9d54979'));
|
||||
* ```
|
||||
*
|
||||
* @param address - The account address to be registered to the accounts registry contract.
|
||||
* @returns true - If registration is successful or account had already been registered.
|
||||
*/
|
||||
public async addToAccountRegistry(address: string): Promise<boolean> {
|
||||
if (!(await this.haveAccount(address))) {
|
||||
return await this.contract.methods.add(address).send({ from: this.signerAddress });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a specific account address has been registered in the accounts registry.
|
||||
* Returns "true" for available and "false" otherwise.
|
||||
*
|
||||
* @async
|
||||
* @example
|
||||
* Prints "true" or "false" depending on whether '0xc0ffee254729296a45a3885639AC7E10F9d54979' has been registered:
|
||||
* ```typescript
|
||||
* console.log(await haveAccount('0xc0ffee254729296a45a3885639AC7E10F9d54979'));
|
||||
* ```
|
||||
*
|
||||
* @param address - The account address to be validated.
|
||||
* @returns true - If the address has been registered in the accounts registry.
|
||||
*/
|
||||
public async haveAccount(address: string): Promise<boolean> {
|
||||
return (await this.contract.methods.have(address).call()) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specified number of the most recently registered accounts.
|
||||
*
|
||||
* @async
|
||||
* @example
|
||||
* Prints an array of accounts:
|
||||
* ```typescript
|
||||
* console.log(await last(5));
|
||||
* ```
|
||||
*
|
||||
* @param numberOfAccounts - The number of accounts to return from the accounts registry.
|
||||
* @returns An array of registered account addresses.
|
||||
*/
|
||||
public async last(numberOfAccounts: number): Promise<Array<string>> {
|
||||
const count: number = await this.totalAccounts();
|
||||
let lowest: number = count - numberOfAccounts;
|
||||
@@ -41,10 +107,19 @@ export class AccountIndex {
|
||||
return accounts;
|
||||
}
|
||||
|
||||
public async addToAccountRegistry(address: string): Promise<boolean> {
|
||||
if (!(await this.haveAccount(address))) {
|
||||
return await this.contract.methods.add(address).send({ from: this.signerAddress });
|
||||
}
|
||||
return true;
|
||||
/**
|
||||
* Returns the total number of accounts that have been registered in the network.
|
||||
*
|
||||
* @async
|
||||
* @example
|
||||
* Prints the total number of registered accounts:
|
||||
* ```typescript
|
||||
* console.log(await totalAccounts());
|
||||
* ```
|
||||
*
|
||||
* @returns The total number of registered accounts.
|
||||
*/
|
||||
public async totalAccounts(): Promise<number> {
|
||||
return await this.contract.methods.entryCount().call();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Application imports
|
||||
import { TokenRegistry } from '@app/_eth/token-registry';
|
||||
import { environment } from '@src/environments/environment';
|
||||
|
||||
|
||||
@@ -1,14 +1,36 @@
|
||||
// Third party imports
|
||||
import Web3 from 'web3';
|
||||
|
||||
// Application imports
|
||||
import { environment } from '@src/environments/environment';
|
||||
import { Web3Service } from '@app/_services/web3.service';
|
||||
|
||||
/** Fetch the token registry contract's ABI. */
|
||||
const abi: Array<any> = require('@src/assets/js/block-sync/data/TokenUniqueSymbolIndex.json');
|
||||
/** Establish a connection to the blockchain network. */
|
||||
const web3: Web3 = Web3Service.getInstance();
|
||||
|
||||
/**
|
||||
* Provides an instance of the token registry contract.
|
||||
* Allows querying of tokens that have been registered as valid tokens in the network.
|
||||
*
|
||||
* @remarks
|
||||
* This is our interface to the token registry contract.
|
||||
*/
|
||||
export class TokenRegistry {
|
||||
contractAddress: string;
|
||||
signerAddress: string;
|
||||
/** The instance of the token registry contract. */
|
||||
contract: any;
|
||||
/** The deployed token registry contract's address. */
|
||||
contractAddress: string;
|
||||
/** The account address of the account that deployed the token registry contract. */
|
||||
signerAddress: string;
|
||||
|
||||
/**
|
||||
* Create a connection to the deployed token registry contract.
|
||||
*
|
||||
* @param contractAddress - The deployed token registry contract's address.
|
||||
* @param signerAddress - The account address of the account that deployed the token registry contract.
|
||||
*/
|
||||
constructor(contractAddress: string, signerAddress?: string) {
|
||||
this.contractAddress = contractAddress;
|
||||
this.contract = new web3.eth.Contract(abi, this.contractAddress);
|
||||
@@ -19,16 +41,54 @@ export class TokenRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
public async totalTokens(): Promise<number> {
|
||||
return await this.contract.methods.entryCount().call();
|
||||
}
|
||||
|
||||
public async entry(serial: number): Promise<string> {
|
||||
return await this.contract.methods.entry(serial).call();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address of the token with a given identifier.
|
||||
*
|
||||
* @async
|
||||
* @example
|
||||
* Prints the address of the token with the identifier 'sarafu':
|
||||
* ```typescript
|
||||
* console.log(await addressOf('sarafu'));
|
||||
* ```
|
||||
*
|
||||
* @param identifier - The name or identifier of the token to be fetched from the token registry.
|
||||
* @returns The address of the token assigned the specified identifier in the token registry.
|
||||
*/
|
||||
public async addressOf(identifier: string): Promise<string> {
|
||||
const id: string = web3.eth.abi.encodeParameter('bytes32', web3.utils.toHex(identifier));
|
||||
return await this.contract.methods.addressOf(id).call();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address of a token with the given serial in the token registry.
|
||||
*
|
||||
* @async
|
||||
* @example
|
||||
* Prints the address of the token with the serial '2':
|
||||
* ```typescript
|
||||
* console.log(await entry(2));
|
||||
* ```
|
||||
*
|
||||
* @param serial - The serial number of the token to be fetched.
|
||||
* @return The address of the token with the specified serial number.
|
||||
*/
|
||||
public async entry(serial: number): Promise<string> {
|
||||
return await this.contract.methods.entry(serial).call();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of tokens that have been registered in the network.
|
||||
*
|
||||
* @async
|
||||
* @example
|
||||
* Prints the total number of registered tokens:
|
||||
* ```typescript
|
||||
* console.log(await totalTokens());
|
||||
* ```
|
||||
*
|
||||
* @returns The total number of registered tokens.
|
||||
*/
|
||||
public async totalTokens(): Promise<number> {
|
||||
return await this.contract.methods.entryCount().call();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Core imports
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
// Application imports
|
||||
import { AuthGuard } from '@app/_guards/auth.guard';
|
||||
|
||||
describe('AuthGuard', () => {
|
||||
|
||||
@@ -1,19 +1,40 @@
|
||||
// Core imports
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
CanActivate,
|
||||
ActivatedRouteSnapshot,
|
||||
CanActivate,
|
||||
Router,
|
||||
RouterStateSnapshot,
|
||||
UrlTree,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
|
||||
// Third party imports
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
/**
|
||||
* Auth guard implementation.
|
||||
* Dictates access to routes depending on the authentication status.
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthGuard implements CanActivate {
|
||||
/**
|
||||
* Instantiates the auth guard class.
|
||||
*
|
||||
* @param router - A service that provides navigation among views and URL manipulation capabilities.
|
||||
*/
|
||||
constructor(private router: Router) {}
|
||||
|
||||
/**
|
||||
* Returns whether navigation to a specific route is acceptable.
|
||||
* Checks if the user has uploaded a private key.
|
||||
*
|
||||
* @param route - Contains the information about a route associated with a component loaded in an outlet at a particular moment in time.
|
||||
* ActivatedRouteSnapshot can also be used to traverse the router state tree.
|
||||
* @param state - Represents the state of the router at a moment in time.
|
||||
* @returns true - If there is an active private key in the user's localStorage.
|
||||
*/
|
||||
canActivate(
|
||||
route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Core imports
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
// Application imports
|
||||
import { RoleGuard } from '@app/_guards/role.guard';
|
||||
|
||||
describe('RoleGuard', () => {
|
||||
|
||||
@@ -1,19 +1,40 @@
|
||||
// Core imports
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
CanActivate,
|
||||
ActivatedRouteSnapshot,
|
||||
CanActivate,
|
||||
Router,
|
||||
RouterStateSnapshot,
|
||||
UrlTree,
|
||||
Router,
|
||||
} from '@angular/router';
|
||||
|
||||
// Third party imports
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
/**
|
||||
* Role guard implementation.
|
||||
* Dictates access to routes depending on the user's role.
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class RoleGuard implements CanActivate {
|
||||
/**
|
||||
* Instantiates the role guard class.
|
||||
*
|
||||
* @param router - A service that provides navigation among views and URL manipulation capabilities.
|
||||
*/
|
||||
constructor(private router: Router) {}
|
||||
|
||||
/**
|
||||
* Returns whether navigation to a specific route is acceptable.
|
||||
* Checks if the user has the required role to access the route.
|
||||
*
|
||||
* @param route - Contains the information about a route associated with a component loaded in an outlet at a particular moment in time.
|
||||
* ActivatedRouteSnapshot can also be used to traverse the router state tree.
|
||||
* @param state - Represents the state of the router at a moment in time.
|
||||
* @returns true - If the user's role matches with accepted roles.
|
||||
*/
|
||||
canActivate(
|
||||
route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
/**
|
||||
* Returns the sum of all values in an array.
|
||||
*
|
||||
* @example
|
||||
* Prints 6 for the array [1, 2, 3]:
|
||||
* ```typescript
|
||||
* console.log(arraySum([1, 2, 3]));
|
||||
* ```
|
||||
*
|
||||
* @param arr - An array of numbers.
|
||||
* @return The sum of all values in the array.
|
||||
*/
|
||||
function arraySum(arr: Array<number>): number {
|
||||
return arr.reduce((accumulator, current) => accumulator + current, 0);
|
||||
}
|
||||
|
||||
/** @exports */
|
||||
export { arraySum };
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
/**
|
||||
* Copies set text to clipboard.
|
||||
*
|
||||
* @example
|
||||
* copies 'Hello World!' to the clipboard and prints "true":
|
||||
* ```typescript
|
||||
* console.log(copyToClipboard('Hello World!'));
|
||||
* ```
|
||||
*
|
||||
* @param text - The text to be copied to the clipboard.
|
||||
* @returns true - If the copy operation is successful.
|
||||
*/
|
||||
function copyToClipboard(text: any): boolean {
|
||||
// create our hidden div element
|
||||
const hiddenCopy: HTMLDivElement = document.createElement('div');
|
||||
@@ -48,4 +60,5 @@ function copyToClipboard(text: any): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @exports */
|
||||
export { copyToClipboard };
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Application imports
|
||||
import { CustomErrorStateMatcher } from '@app/_helpers/custom-error-state-matcher';
|
||||
|
||||
describe('CustomErrorStateMatcher', () => {
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
// Core imports
|
||||
import { ErrorStateMatcher } from '@angular/material/core';
|
||||
import { FormControl, FormGroupDirective, NgForm } from '@angular/forms';
|
||||
|
||||
/**
|
||||
* Custom provider that defines how form controls behave with regards to displaying error messages.
|
||||
*
|
||||
*/
|
||||
export class CustomErrorStateMatcher implements ErrorStateMatcher {
|
||||
/**
|
||||
* Checks whether an invalid input has been made and an error should be made.
|
||||
*
|
||||
* @param control - Tracks the value and validation status of an individual form control.
|
||||
* @param form - Binding of an existing FormGroup to a DOM element.
|
||||
* @returns true - If an invalid input has been made to the form control.
|
||||
*/
|
||||
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
|
||||
const isSubmitted: boolean = form && form.submitted;
|
||||
return !!(control && control.invalid && (control.dirty || control.touched || isSubmitted));
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Application imports
|
||||
import { CustomValidator } from '@app/_helpers/custom.validator';
|
||||
|
||||
describe('Custom.Validator', () => {
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
// Core imports
|
||||
import { AbstractControl, ValidationErrors } from '@angular/forms';
|
||||
|
||||
/**
|
||||
* Provides methods to perform custom validation to form inputs.
|
||||
*/
|
||||
export class CustomValidator {
|
||||
/**
|
||||
* Sets errors to the confirm password input field if it does not match with the value in the password input field.
|
||||
*
|
||||
* @param control - The control object of the form being validated.
|
||||
*/
|
||||
static passwordMatchValidator(control: AbstractControl): void {
|
||||
const password: string = control.get('password').value;
|
||||
const confirmPassword: string = control.get('confirmPassword').value;
|
||||
@@ -9,6 +18,13 @@ export class CustomValidator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets errors to a form field if it does not match with the regular expression given.
|
||||
*
|
||||
* @param regex - The regular expression to match with the form field.
|
||||
* @param error - Defines the map of errors to return from failed validation checks.
|
||||
* @returns The map of errors returned from failed validation checks.
|
||||
*/
|
||||
static patternValidator(regex: RegExp, error: ValidationErrors): ValidationErrors | null {
|
||||
return (control: AbstractControl): { [key: string]: any } => {
|
||||
if (!control.value) {
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
/**
|
||||
* Exports data to a CSV format and provides a download file.
|
||||
*
|
||||
* @param arrayData - An array of data to be converted to CSV format.
|
||||
* @param filename - The name of the file to be downloaded.
|
||||
* @param delimiter - The delimiter to be used when converting to CSV format.
|
||||
* Defaults to commas.
|
||||
*/
|
||||
function exportCsv(arrayData: Array<any>, filename: string, delimiter: string = ','): void {
|
||||
if (arrayData === undefined || arrayData.length === 0) {
|
||||
alert('No data to be exported!');
|
||||
@@ -26,11 +34,5 @@ function exportCsv(arrayData: Array<any>, filename: string, delimiter: string =
|
||||
downloadLink.click();
|
||||
}
|
||||
|
||||
function removeSpecialChar(str: string): string {
|
||||
if (str === null || str === '') {
|
||||
return '';
|
||||
}
|
||||
return str.replace(/[^a-zA-Z0-9 ]/g, '');
|
||||
}
|
||||
|
||||
/** @exports */
|
||||
export { exportCsv };
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { GlobalErrorHandler } from './global-error-handler';
|
||||
// Application imports
|
||||
import { GlobalErrorHandler } from '@app/_helpers/global-error-handler';
|
||||
|
||||
describe('GlobalErrorHandler', () => {
|
||||
it('should create an instance', () => {
|
||||
|
||||
@@ -1,11 +1,26 @@
|
||||
import { ErrorHandler, Injectable } from '@angular/core';
|
||||
import { LoggingService } from '@app/_services/logging.service';
|
||||
// Core imports
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { ErrorHandler, Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
// A generalized http response error
|
||||
// Application imports
|
||||
import { LoggingService } from '@app/_services/logging.service';
|
||||
|
||||
/**
|
||||
* A generalized http response error.
|
||||
*
|
||||
* @extends Error
|
||||
*/
|
||||
export class HttpError extends Error {
|
||||
/** The error's status code. */
|
||||
public status: number;
|
||||
|
||||
/**
|
||||
* Initialize the HttpError class.
|
||||
*
|
||||
* @param message - The message given by the error.
|
||||
* @param status - The status code given by the error.
|
||||
*/
|
||||
constructor(message: string, status: number) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
@@ -13,14 +28,33 @@ export class HttpError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a hook for centralized exception handling.
|
||||
*
|
||||
* @extends ErrorHandler
|
||||
*/
|
||||
@Injectable()
|
||||
export class GlobalErrorHandler extends ErrorHandler {
|
||||
/**
|
||||
* An array of sentence sections that denote warnings.
|
||||
*/
|
||||
private sentencesForWarningLogging: Array<string> = [];
|
||||
|
||||
/**
|
||||
* Initialization of the Global Error Handler.
|
||||
*
|
||||
* @param loggingService - A service that provides logging capabilities.
|
||||
* @param router - A service that provides navigation among views and URL manipulation capabilities.
|
||||
*/
|
||||
constructor(private loggingService: LoggingService, private router: Router) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles different types of errors.
|
||||
*
|
||||
* @param error - An error objects thrown when a runtime errors occurs.
|
||||
*/
|
||||
handleError(error: Error): void {
|
||||
this.logError(error);
|
||||
const message: string = error.message ? error.message : error.toString();
|
||||
@@ -41,6 +75,32 @@ export class GlobalErrorHandler extends ErrorHandler {
|
||||
throw error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an error is of type warning.
|
||||
*
|
||||
* @param errorTraceString - A description of the error and it's stack trace.
|
||||
* @returns true - If the error is of type warning.
|
||||
*/
|
||||
private isWarning(errorTraceString: string): boolean {
|
||||
let isWarning: boolean = true;
|
||||
if (errorTraceString.includes('/src/app/')) {
|
||||
isWarning = false;
|
||||
}
|
||||
|
||||
this.sentencesForWarningLogging.forEach((whiteListSentence: string) => {
|
||||
if (errorTraceString.includes(whiteListSentence)) {
|
||||
isWarning = true;
|
||||
}
|
||||
});
|
||||
|
||||
return isWarning;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write appropriate logs according to the type of error.
|
||||
*
|
||||
* @param error - An error objects thrown when a runtime errors occurs.
|
||||
*/
|
||||
logError(error: any): void {
|
||||
const route: string = this.router.url;
|
||||
if (error instanceof HttpErrorResponse) {
|
||||
@@ -71,21 +131,6 @@ export class GlobalErrorHandler extends ErrorHandler {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private isWarning(errorTraceString: string): boolean {
|
||||
let isWarning: boolean = true;
|
||||
if (errorTraceString.includes('/src/app/')) {
|
||||
isWarning = false;
|
||||
}
|
||||
|
||||
this.sentencesForWarningLogging.forEach((whiteListSentence: string) => {
|
||||
if (errorTraceString.includes(whiteListSentence)) {
|
||||
isWarning = true;
|
||||
}
|
||||
});
|
||||
|
||||
return isWarning;
|
||||
}
|
||||
}
|
||||
|
||||
export function rejectBody(error): { status: any; statusText: any } {
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { rejectBody } from '@app/_helpers/global-error-handler';
|
||||
|
||||
/** Provides an avenue of fetching resources via HTTP calls. */
|
||||
function HttpGetter(): void {}
|
||||
|
||||
/**
|
||||
* Fetches files using HTTP get requests.
|
||||
*
|
||||
* @param filename - The filename to fetch.
|
||||
* @returns The HTTP response text.
|
||||
*/
|
||||
HttpGetter.prototype.get = (filename) =>
|
||||
new Promise((resolve, reject) => {
|
||||
fetch(filename).then((response) => {
|
||||
@@ -14,4 +21,5 @@ HttpGetter.prototype.get = (filename) =>
|
||||
});
|
||||
});
|
||||
|
||||
/** @exports */
|
||||
export { HttpGetter };
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
export * from '@app/_helpers/array-sum';
|
||||
export * from '@app/_helpers/clipboard-copy';
|
||||
export * from '@app/_helpers/custom.validator';
|
||||
export * from '@app/_helpers/custom-error-state-matcher';
|
||||
export * from '@app/_helpers/mock-backend';
|
||||
export * from '@app/_helpers/array-sum';
|
||||
export * from '@app/_helpers/http-getter';
|
||||
export * from '@app/_helpers/global-error-handler';
|
||||
export * from '@app/_helpers/export-csv';
|
||||
export * from '@app/_helpers/global-error-handler';
|
||||
export * from '@app/_helpers/http-getter';
|
||||
export * from '@app/_helpers/mock-backend';
|
||||
export * from '@app/_helpers/read-csv';
|
||||
export * from '@app/_helpers/clipboard-copy';
|
||||
export * from '@app/_helpers/schema-validation';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Core imports
|
||||
import {
|
||||
HTTP_INTERCEPTORS,
|
||||
HttpEvent,
|
||||
@@ -7,10 +8,18 @@ import {
|
||||
HttpResponse,
|
||||
} from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
// Third party imports
|
||||
import { Observable, of, throwError } from 'rxjs';
|
||||
import { delay, dematerialize, materialize, mergeMap } from 'rxjs/operators';
|
||||
|
||||
// Application imports
|
||||
import { Action, AreaName, AreaType, Category, Token } from '@app/_models';
|
||||
|
||||
/** A mock of the curated account types. */
|
||||
const accountTypes: Array<string> = ['user', 'cashier', 'vendor', 'tokenagent', 'group'];
|
||||
|
||||
/** A mock of actions made by the admin staff. */
|
||||
const actions: Array<Action> = [
|
||||
{ id: 1, user: 'Tom', role: 'enroller', action: 'Disburse RSV 100', approval: false },
|
||||
{ id: 2, user: 'Christine', role: 'admin', action: 'Change user phone number', approval: true },
|
||||
@@ -20,83 +29,335 @@ const actions: Array<Action> = [
|
||||
{ id: 6, user: 'Patience', role: 'enroller', action: 'Change user information', approval: false },
|
||||
];
|
||||
|
||||
const tokens: Array<Token> = [
|
||||
/** A mock of curated area names. */
|
||||
const areaNames: Array<AreaName> = [
|
||||
{
|
||||
name: 'Giftable Reserve',
|
||||
symbol: 'GRZ',
|
||||
address: '0xa686005CE37Dce7738436256982C3903f2E4ea8E',
|
||||
supply: '1000000001000000000000000000',
|
||||
decimals: '18',
|
||||
reserves: {},
|
||||
name: 'Mukuru Nairobi',
|
||||
locations: [
|
||||
'kayaba',
|
||||
'kayba',
|
||||
'kambi',
|
||||
'mukuru',
|
||||
'masai',
|
||||
'hazina',
|
||||
'south',
|
||||
'tetra',
|
||||
'tetrapak',
|
||||
'ruben',
|
||||
'rueben',
|
||||
'kingston',
|
||||
'korokocho',
|
||||
'kingstone',
|
||||
'kamongo',
|
||||
'lungalunga',
|
||||
'sinai',
|
||||
'sigei',
|
||||
'lungu',
|
||||
'lunga lunga',
|
||||
'owino road',
|
||||
'seigei',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Demo Token',
|
||||
symbol: 'DEMO',
|
||||
address: '0xc80D6aFF8194114c52AEcD84c9f15fd5c8abb187',
|
||||
supply: '99999999999999998976',
|
||||
decimals: '18',
|
||||
reserves: {
|
||||
'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {
|
||||
weight: '1000000',
|
||||
balance: '99999999999999998976',
|
||||
},
|
||||
},
|
||||
reserveRatio: '1000000',
|
||||
owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a',
|
||||
name: 'Kinango Kwale',
|
||||
locations: [
|
||||
'amani',
|
||||
'bofu',
|
||||
'chibuga',
|
||||
'chikomani',
|
||||
'chilongoni',
|
||||
'chigojoni',
|
||||
'chinguluni',
|
||||
'chigato',
|
||||
'chigale',
|
||||
'chikole',
|
||||
'chilongoni',
|
||||
'chilumani',
|
||||
'chigojoni',
|
||||
'chikomani',
|
||||
'chizini',
|
||||
'chikomeni',
|
||||
'chidzuvini',
|
||||
'chidzivuni',
|
||||
'chikuyu',
|
||||
'chizingo',
|
||||
'doti',
|
||||
'dzugwe',
|
||||
'dzivani',
|
||||
'dzovuni',
|
||||
'hanje',
|
||||
'kasemeni',
|
||||
'katundani',
|
||||
'kibandaogo',
|
||||
'kibandaongo',
|
||||
'kwale',
|
||||
'kinango',
|
||||
'kidzuvini',
|
||||
'kalalani',
|
||||
'kafuduni',
|
||||
'kaloleni',
|
||||
'kilibole',
|
||||
'lutsangani',
|
||||
'peku',
|
||||
'gona',
|
||||
'guro',
|
||||
'gandini',
|
||||
'mkanyeni',
|
||||
'myenzeni',
|
||||
'miyenzeni',
|
||||
'miatsiani',
|
||||
'mienzeni',
|
||||
'mnyenzeni',
|
||||
'minyenzeni',
|
||||
'miyani',
|
||||
'mioleni',
|
||||
'makuluni',
|
||||
'mariakani',
|
||||
'makobeni',
|
||||
'madewani',
|
||||
'mwangaraba',
|
||||
'mwashanga',
|
||||
'miloeni',
|
||||
'mabesheni',
|
||||
'mazeras',
|
||||
'mazera',
|
||||
'mlola',
|
||||
'muugano',
|
||||
'mulunguni',
|
||||
'mabesheni',
|
||||
'miatsani',
|
||||
'miatsiani',
|
||||
'mwache',
|
||||
'mwangani',
|
||||
'mwehavikonje',
|
||||
'miguneni',
|
||||
'nzora',
|
||||
'nzovuni',
|
||||
'vikinduni',
|
||||
'vikolani',
|
||||
'vitangani',
|
||||
'viogato',
|
||||
'vyogato',
|
||||
'vistangani',
|
||||
'yapha',
|
||||
'yava',
|
||||
'yowani',
|
||||
'ziwani',
|
||||
'majengo',
|
||||
'matuga',
|
||||
'vigungani',
|
||||
'vidziweni',
|
||||
'vinyunduni',
|
||||
'ukunda',
|
||||
'kokotoni',
|
||||
'mikindani',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Foo Token',
|
||||
symbol: 'FOO',
|
||||
address: '0x9ceD86089f7aBB5A97B40eb0E7521e7aa308d354',
|
||||
supply: '1000000000000000001014',
|
||||
decimals: '18',
|
||||
reserves: {
|
||||
'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {
|
||||
weight: '1000000',
|
||||
balance: '1000000000000000001014',
|
||||
},
|
||||
},
|
||||
reserveRatio: '1000000',
|
||||
owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a',
|
||||
name: 'Misc Nairobi',
|
||||
locations: [
|
||||
'nairobi',
|
||||
'west',
|
||||
'lindi',
|
||||
'kibera',
|
||||
'kibira',
|
||||
'kibra',
|
||||
'makina',
|
||||
'soweto',
|
||||
'olympic',
|
||||
'kangemi',
|
||||
'ruiru',
|
||||
'congo',
|
||||
'kawangware',
|
||||
'kwangware',
|
||||
'donholm',
|
||||
'dagoreti',
|
||||
'dandora',
|
||||
'kabete',
|
||||
'sinai',
|
||||
'donhom',
|
||||
'donholm',
|
||||
'huruma',
|
||||
'kitengela',
|
||||
'makadara',
|
||||
',mlolongo',
|
||||
'kenyatta',
|
||||
'mlolongo',
|
||||
'tassia',
|
||||
'tasia',
|
||||
'gatina',
|
||||
'56',
|
||||
'industrial',
|
||||
'kariobangi',
|
||||
'kasarani',
|
||||
'kayole',
|
||||
'mathare',
|
||||
'pipe',
|
||||
'juja',
|
||||
'uchumi',
|
||||
'jogoo',
|
||||
'umoja',
|
||||
'thika',
|
||||
'kikuyu',
|
||||
'stadium',
|
||||
'buru buru',
|
||||
'ngong',
|
||||
'starehe',
|
||||
'mwiki',
|
||||
'fuata',
|
||||
'kware',
|
||||
'kabiro',
|
||||
'embakassi',
|
||||
'embakasi',
|
||||
'kmoja',
|
||||
'east',
|
||||
'githurai',
|
||||
'landi',
|
||||
'langata',
|
||||
'limuru',
|
||||
'mathere',
|
||||
'dagoretti',
|
||||
'kirembe',
|
||||
'muugano',
|
||||
'mwiki',
|
||||
'toi market',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'testb',
|
||||
symbol: 'tstb',
|
||||
address: '0xC63cFA91A3BFf41cE31Ff436f67D3ACBC977DB95',
|
||||
supply: '99000',
|
||||
decimals: '18',
|
||||
reserves: {
|
||||
'0xa686005CE37Dce7738436256982C3903f2E4ea8E': { weight: '1000000', balance: '99000' },
|
||||
},
|
||||
reserveRatio: '1000000',
|
||||
owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a',
|
||||
name: 'Misc Mombasa',
|
||||
locations: [
|
||||
'mombasa',
|
||||
'likoni',
|
||||
'bangla',
|
||||
'bangladesh',
|
||||
'kizingo',
|
||||
'old town',
|
||||
'makupa',
|
||||
'mvita',
|
||||
'ngombeni',
|
||||
'ngómbeni',
|
||||
'ombeni',
|
||||
'magongo',
|
||||
'miritini',
|
||||
'changamwe',
|
||||
'jomvu',
|
||||
'ohuru',
|
||||
'tudor',
|
||||
'diani',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'testa',
|
||||
symbol: 'tsta',
|
||||
address: '0x8fA4101ef19D0a078239d035659e92b278bD083C',
|
||||
supply: '9981',
|
||||
decimals: '18',
|
||||
reserves: {
|
||||
'0xa686005CE37Dce7738436256982C3903f2E4ea8E': { weight: '1000000', balance: '9981' },
|
||||
},
|
||||
reserveRatio: '1000000',
|
||||
owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a',
|
||||
name: 'Kisauni',
|
||||
locations: [
|
||||
'bamburi',
|
||||
'kisauni',
|
||||
'mworoni',
|
||||
'nyali',
|
||||
'shanzu',
|
||||
'bombolulu',
|
||||
'mtopanga',
|
||||
'mjambere',
|
||||
'majaoni',
|
||||
'manyani',
|
||||
'magogoni',
|
||||
'junda',
|
||||
'mwakirunge',
|
||||
'mshomoroni',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'testc',
|
||||
symbol: 'tstc',
|
||||
address: '0x4A6fA6bc3BfE4C9661bC692D9798425350C9e3D4',
|
||||
supply: '100990',
|
||||
decimals: '18',
|
||||
reserves: {
|
||||
'0xa686005CE37Dce7738436256982C3903f2E4ea8E': { weight: '1000000', balance: '100990' },
|
||||
},
|
||||
reserveRatio: '1000000',
|
||||
owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a',
|
||||
name: 'Kilifi',
|
||||
locations: [
|
||||
'kilfi',
|
||||
'kilifi',
|
||||
'mtwapa',
|
||||
'takaungu',
|
||||
'makongeni',
|
||||
'mnarani',
|
||||
'mnarani',
|
||||
'office',
|
||||
'g.e',
|
||||
'ge',
|
||||
'raibai',
|
||||
'ribe',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Kakuma',
|
||||
locations: ['kakuma'],
|
||||
},
|
||||
{
|
||||
name: 'Kitui',
|
||||
locations: ['kitui', 'mwingi'],
|
||||
},
|
||||
{
|
||||
name: 'Nyanza',
|
||||
locations: [
|
||||
'busia',
|
||||
'nyalgunga',
|
||||
'mbita',
|
||||
'siaya',
|
||||
'kisumu',
|
||||
'nyalenda',
|
||||
'hawinga',
|
||||
'rangala',
|
||||
'uyoma',
|
||||
'mumias',
|
||||
'homabay',
|
||||
'homaboy',
|
||||
'migori',
|
||||
'kusumu',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Misc Rural Counties',
|
||||
locations: [
|
||||
'makueni',
|
||||
'meru',
|
||||
'kisii',
|
||||
'bomet',
|
||||
'machakos',
|
||||
'bungoma',
|
||||
'eldoret',
|
||||
'kakamega',
|
||||
'kericho',
|
||||
'kajiado',
|
||||
'nandi',
|
||||
'nyeri',
|
||||
'wote',
|
||||
'kiambu',
|
||||
'mwea',
|
||||
'nakuru',
|
||||
'narok',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'other',
|
||||
locations: ['other', 'none', 'unknown'],
|
||||
},
|
||||
];
|
||||
|
||||
/** A mock of curated area types. */
|
||||
const areaTypes: Array<AreaType> = [
|
||||
{
|
||||
name: 'urban',
|
||||
area: ['urban', 'nairobi', 'mombasa'],
|
||||
},
|
||||
{
|
||||
name: 'rural',
|
||||
area: ['rural', 'kakuma', 'kwale', 'kinango', 'kitui', 'nyanza'],
|
||||
},
|
||||
{
|
||||
name: 'periurban',
|
||||
area: ['kilifi', 'periurban'],
|
||||
},
|
||||
{
|
||||
name: 'other',
|
||||
area: ['other'],
|
||||
},
|
||||
];
|
||||
|
||||
/** A mock of the user's business categories */
|
||||
const categories: Array<Category> = [
|
||||
{
|
||||
name: 'system',
|
||||
@@ -730,333 +991,88 @@ const categories: Array<Category> = [
|
||||
},
|
||||
];
|
||||
|
||||
const areaNames: Array<AreaName> = [
|
||||
/** A mock of curated genders */
|
||||
const genders: Array<string> = ['male', 'female', 'other'];
|
||||
|
||||
/** A mock of the tokens in the system. */
|
||||
const tokens: Array<Token> = [
|
||||
{
|
||||
name: 'Mukuru Nairobi',
|
||||
locations: [
|
||||
'kayaba',
|
||||
'kayba',
|
||||
'kambi',
|
||||
'mukuru',
|
||||
'masai',
|
||||
'hazina',
|
||||
'south',
|
||||
'tetra',
|
||||
'tetrapak',
|
||||
'ruben',
|
||||
'rueben',
|
||||
'kingston',
|
||||
'korokocho',
|
||||
'kingstone',
|
||||
'kamongo',
|
||||
'lungalunga',
|
||||
'sinai',
|
||||
'sigei',
|
||||
'lungu',
|
||||
'lunga lunga',
|
||||
'owino road',
|
||||
'seigei',
|
||||
],
|
||||
name: 'Giftable Reserve',
|
||||
symbol: 'GRZ',
|
||||
address: '0xa686005CE37Dce7738436256982C3903f2E4ea8E',
|
||||
supply: '1000000001000000000000000000',
|
||||
decimals: '18',
|
||||
reserves: {},
|
||||
},
|
||||
{
|
||||
name: 'Kinango Kwale',
|
||||
locations: [
|
||||
'amani',
|
||||
'bofu',
|
||||
'chibuga',
|
||||
'chikomani',
|
||||
'chilongoni',
|
||||
'chigojoni',
|
||||
'chinguluni',
|
||||
'chigato',
|
||||
'chigale',
|
||||
'chikole',
|
||||
'chilongoni',
|
||||
'chilumani',
|
||||
'chigojoni',
|
||||
'chikomani',
|
||||
'chizini',
|
||||
'chikomeni',
|
||||
'chidzuvini',
|
||||
'chidzivuni',
|
||||
'chikuyu',
|
||||
'chizingo',
|
||||
'doti',
|
||||
'dzugwe',
|
||||
'dzivani',
|
||||
'dzovuni',
|
||||
'hanje',
|
||||
'kasemeni',
|
||||
'katundani',
|
||||
'kibandaogo',
|
||||
'kibandaongo',
|
||||
'kwale',
|
||||
'kinango',
|
||||
'kidzuvini',
|
||||
'kalalani',
|
||||
'kafuduni',
|
||||
'kaloleni',
|
||||
'kilibole',
|
||||
'lutsangani',
|
||||
'peku',
|
||||
'gona',
|
||||
'guro',
|
||||
'gandini',
|
||||
'mkanyeni',
|
||||
'myenzeni',
|
||||
'miyenzeni',
|
||||
'miatsiani',
|
||||
'mienzeni',
|
||||
'mnyenzeni',
|
||||
'minyenzeni',
|
||||
'miyani',
|
||||
'mioleni',
|
||||
'makuluni',
|
||||
'mariakani',
|
||||
'makobeni',
|
||||
'madewani',
|
||||
'mwangaraba',
|
||||
'mwashanga',
|
||||
'miloeni',
|
||||
'mabesheni',
|
||||
'mazeras',
|
||||
'mazera',
|
||||
'mlola',
|
||||
'muugano',
|
||||
'mulunguni',
|
||||
'mabesheni',
|
||||
'miatsani',
|
||||
'miatsiani',
|
||||
'mwache',
|
||||
'mwangani',
|
||||
'mwehavikonje',
|
||||
'miguneni',
|
||||
'nzora',
|
||||
'nzovuni',
|
||||
'vikinduni',
|
||||
'vikolani',
|
||||
'vitangani',
|
||||
'viogato',
|
||||
'vyogato',
|
||||
'vistangani',
|
||||
'yapha',
|
||||
'yava',
|
||||
'yowani',
|
||||
'ziwani',
|
||||
'majengo',
|
||||
'matuga',
|
||||
'vigungani',
|
||||
'vidziweni',
|
||||
'vinyunduni',
|
||||
'ukunda',
|
||||
'kokotoni',
|
||||
'mikindani',
|
||||
],
|
||||
name: 'Demo Token',
|
||||
symbol: 'DEMO',
|
||||
address: '0xc80D6aFF8194114c52AEcD84c9f15fd5c8abb187',
|
||||
supply: '99999999999999998976',
|
||||
decimals: '18',
|
||||
reserves: {
|
||||
'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {
|
||||
weight: '1000000',
|
||||
balance: '99999999999999998976',
|
||||
},
|
||||
},
|
||||
reserveRatio: '1000000',
|
||||
owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a',
|
||||
},
|
||||
{
|
||||
name: 'Misc Nairobi',
|
||||
locations: [
|
||||
'nairobi',
|
||||
'west',
|
||||
'lindi',
|
||||
'kibera',
|
||||
'kibira',
|
||||
'kibra',
|
||||
'makina',
|
||||
'soweto',
|
||||
'olympic',
|
||||
'kangemi',
|
||||
'ruiru',
|
||||
'congo',
|
||||
'kawangware',
|
||||
'kwangware',
|
||||
'donholm',
|
||||
'dagoreti',
|
||||
'dandora',
|
||||
'kabete',
|
||||
'sinai',
|
||||
'donhom',
|
||||
'donholm',
|
||||
'huruma',
|
||||
'kitengela',
|
||||
'makadara',
|
||||
',mlolongo',
|
||||
'kenyatta',
|
||||
'mlolongo',
|
||||
'tassia',
|
||||
'tasia',
|
||||
'gatina',
|
||||
'56',
|
||||
'industrial',
|
||||
'kariobangi',
|
||||
'kasarani',
|
||||
'kayole',
|
||||
'mathare',
|
||||
'pipe',
|
||||
'juja',
|
||||
'uchumi',
|
||||
'jogoo',
|
||||
'umoja',
|
||||
'thika',
|
||||
'kikuyu',
|
||||
'stadium',
|
||||
'buru buru',
|
||||
'ngong',
|
||||
'starehe',
|
||||
'mwiki',
|
||||
'fuata',
|
||||
'kware',
|
||||
'kabiro',
|
||||
'embakassi',
|
||||
'embakasi',
|
||||
'kmoja',
|
||||
'east',
|
||||
'githurai',
|
||||
'landi',
|
||||
'langata',
|
||||
'limuru',
|
||||
'mathere',
|
||||
'dagoretti',
|
||||
'kirembe',
|
||||
'muugano',
|
||||
'mwiki',
|
||||
'toi market',
|
||||
],
|
||||
name: 'Foo Token',
|
||||
symbol: 'FOO',
|
||||
address: '0x9ceD86089f7aBB5A97B40eb0E7521e7aa308d354',
|
||||
supply: '1000000000000000001014',
|
||||
decimals: '18',
|
||||
reserves: {
|
||||
'0xa686005CE37Dce7738436256982C3903f2E4ea8E': {
|
||||
weight: '1000000',
|
||||
balance: '1000000000000000001014',
|
||||
},
|
||||
},
|
||||
reserveRatio: '1000000',
|
||||
owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a',
|
||||
},
|
||||
{
|
||||
name: 'Misc Mombasa',
|
||||
locations: [
|
||||
'mombasa',
|
||||
'likoni',
|
||||
'bangla',
|
||||
'bangladesh',
|
||||
'kizingo',
|
||||
'old town',
|
||||
'makupa',
|
||||
'mvita',
|
||||
'ngombeni',
|
||||
'ngómbeni',
|
||||
'ombeni',
|
||||
'magongo',
|
||||
'miritini',
|
||||
'changamwe',
|
||||
'jomvu',
|
||||
'ohuru',
|
||||
'tudor',
|
||||
'diani',
|
||||
],
|
||||
name: 'testb',
|
||||
symbol: 'tstb',
|
||||
address: '0xC63cFA91A3BFf41cE31Ff436f67D3ACBC977DB95',
|
||||
supply: '99000',
|
||||
decimals: '18',
|
||||
reserves: {
|
||||
'0xa686005CE37Dce7738436256982C3903f2E4ea8E': { weight: '1000000', balance: '99000' },
|
||||
},
|
||||
reserveRatio: '1000000',
|
||||
owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a',
|
||||
},
|
||||
{
|
||||
name: 'Kisauni',
|
||||
locations: [
|
||||
'bamburi',
|
||||
'kisauni',
|
||||
'mworoni',
|
||||
'nyali',
|
||||
'shanzu',
|
||||
'bombolulu',
|
||||
'mtopanga',
|
||||
'mjambere',
|
||||
'majaoni',
|
||||
'manyani',
|
||||
'magogoni',
|
||||
'junda',
|
||||
'mwakirunge',
|
||||
'mshomoroni',
|
||||
],
|
||||
name: 'testa',
|
||||
symbol: 'tsta',
|
||||
address: '0x8fA4101ef19D0a078239d035659e92b278bD083C',
|
||||
supply: '9981',
|
||||
decimals: '18',
|
||||
reserves: {
|
||||
'0xa686005CE37Dce7738436256982C3903f2E4ea8E': { weight: '1000000', balance: '9981' },
|
||||
},
|
||||
reserveRatio: '1000000',
|
||||
owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a',
|
||||
},
|
||||
{
|
||||
name: 'Kilifi',
|
||||
locations: [
|
||||
'kilfi',
|
||||
'kilifi',
|
||||
'mtwapa',
|
||||
'takaungu',
|
||||
'makongeni',
|
||||
'mnarani',
|
||||
'mnarani',
|
||||
'office',
|
||||
'g.e',
|
||||
'ge',
|
||||
'raibai',
|
||||
'ribe',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Kakuma',
|
||||
locations: ['kakuma'],
|
||||
},
|
||||
{
|
||||
name: 'Kitui',
|
||||
locations: ['kitui', 'mwingi'],
|
||||
},
|
||||
{
|
||||
name: 'Nyanza',
|
||||
locations: [
|
||||
'busia',
|
||||
'nyalgunga',
|
||||
'mbita',
|
||||
'siaya',
|
||||
'kisumu',
|
||||
'nyalenda',
|
||||
'hawinga',
|
||||
'rangala',
|
||||
'uyoma',
|
||||
'mumias',
|
||||
'homabay',
|
||||
'homaboy',
|
||||
'migori',
|
||||
'kusumu',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Misc Rural Counties',
|
||||
locations: [
|
||||
'makueni',
|
||||
'meru',
|
||||
'kisii',
|
||||
'bomet',
|
||||
'machakos',
|
||||
'bungoma',
|
||||
'eldoret',
|
||||
'kakamega',
|
||||
'kericho',
|
||||
'kajiado',
|
||||
'nandi',
|
||||
'nyeri',
|
||||
'wote',
|
||||
'kiambu',
|
||||
'mwea',
|
||||
'nakuru',
|
||||
'narok',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'other',
|
||||
locations: ['other', 'none', 'unknown'],
|
||||
name: 'testc',
|
||||
symbol: 'tstc',
|
||||
address: '0x4A6fA6bc3BfE4C9661bC692D9798425350C9e3D4',
|
||||
supply: '100990',
|
||||
decimals: '18',
|
||||
reserves: {
|
||||
'0xa686005CE37Dce7738436256982C3903f2E4ea8E': { weight: '1000000', balance: '100990' },
|
||||
},
|
||||
reserveRatio: '1000000',
|
||||
owner: '0x3Da99AAD2D9CA01D131eFc3B17444b832B31Ff4a',
|
||||
},
|
||||
];
|
||||
|
||||
const areaTypes: Array<AreaType> = [
|
||||
{
|
||||
name: 'urban',
|
||||
area: ['urban', 'nairobi', 'mombasa'],
|
||||
},
|
||||
{
|
||||
name: 'rural',
|
||||
area: ['rural', 'kakuma', 'kwale', 'kinango', 'kitui', 'nyanza'],
|
||||
},
|
||||
{
|
||||
name: 'periurban',
|
||||
area: ['kilifi', 'periurban'],
|
||||
},
|
||||
{
|
||||
name: 'other',
|
||||
area: ['other'],
|
||||
},
|
||||
];
|
||||
|
||||
const accountTypes: Array<string> = ['user', 'cashier', 'vendor', 'tokenagent', 'group'];
|
||||
/** A mock of curated transaction types. */
|
||||
const transactionTypes: Array<string> = [
|
||||
'transactions',
|
||||
'conversions',
|
||||
@@ -1064,10 +1080,20 @@ const transactionTypes: Array<string> = [
|
||||
'rewards',
|
||||
'reclamation',
|
||||
];
|
||||
const genders: Array<string> = ['male', 'female', 'other'];
|
||||
|
||||
/**
|
||||
* Intercepts HTTP requests and handles some specified requests internally.
|
||||
* Provides a backend that can handle requests for certain data items.
|
||||
*/
|
||||
@Injectable()
|
||||
export class MockBackendInterceptor implements HttpInterceptor {
|
||||
/**
|
||||
* Intercepts HTTP requests.
|
||||
*
|
||||
* @param request - An outgoing HTTP request with an optional typed body.
|
||||
* @param next - The next HTTP handler or the outgoing request dispatcher.
|
||||
* @returns The response from the resolved request.
|
||||
*/
|
||||
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
const { url, method, headers, body } = request;
|
||||
|
||||
@@ -1079,22 +1105,17 @@ export class MockBackendInterceptor implements HttpInterceptor {
|
||||
.pipe(delay(500))
|
||||
.pipe(dematerialize());
|
||||
|
||||
/** Forward requests from select routes to their internal handlers. */
|
||||
function handleRoute(): Observable<any> {
|
||||
switch (true) {
|
||||
case url.endsWith('/accounttypes') && method === 'GET':
|
||||
return getAccountTypes();
|
||||
case url.endsWith('/actions') && method === 'GET':
|
||||
return getActions();
|
||||
case url.match(/\/actions\/\d+$/) && method === 'GET':
|
||||
return getActionById();
|
||||
case url.match(/\/actions\/\d+$/) && method === 'POST':
|
||||
return approveAction();
|
||||
case url.endsWith('/tokens') && method === 'GET':
|
||||
return getTokens();
|
||||
case url.match(/\/tokens\/\w+$/) && method === 'GET':
|
||||
return getTokenBySymbol();
|
||||
case url.endsWith('/categories') && method === 'GET':
|
||||
return getCategories();
|
||||
case url.match(/\/categories\/\w+$/) && method === 'GET':
|
||||
return getCategoryByProduct();
|
||||
case url.endsWith('/areanames') && method === 'GET':
|
||||
return getAreaNames();
|
||||
case url.match(/\/areanames\/\w+$/) && method === 'GET':
|
||||
@@ -1103,12 +1124,18 @@ export class MockBackendInterceptor implements HttpInterceptor {
|
||||
return getAreaTypes();
|
||||
case url.match(/\/areatypes\/\w+$/) && method === 'GET':
|
||||
return getAreaTypeByArea();
|
||||
case url.endsWith('/accounttypes') && method === 'GET':
|
||||
return getAccountTypes();
|
||||
case url.endsWith('/transactiontypes') && method === 'GET':
|
||||
return getTransactionTypes();
|
||||
case url.endsWith('/categories') && method === 'GET':
|
||||
return getCategories();
|
||||
case url.match(/\/categories\/\w+$/) && method === 'GET':
|
||||
return getCategoryByProduct();
|
||||
case url.endsWith('/genders') && method === 'GET':
|
||||
return getGenders();
|
||||
case url.endsWith('/tokens') && method === 'GET':
|
||||
return getTokens();
|
||||
case url.match(/\/tokens\/\w+$/) && method === 'GET':
|
||||
return getTokenBySymbol();
|
||||
case url.endsWith('/transactiontypes') && method === 'GET':
|
||||
return getTransactionTypes();
|
||||
default:
|
||||
// pass through any requests not handled above
|
||||
return next.handle(request);
|
||||
@@ -1117,15 +1144,6 @@ export class MockBackendInterceptor implements HttpInterceptor {
|
||||
|
||||
// route functions
|
||||
|
||||
function getActions(): Observable<HttpResponse<any>> {
|
||||
return ok(actions);
|
||||
}
|
||||
|
||||
function getActionById(): Observable<HttpResponse<any>> {
|
||||
const queriedAction: Action = actions.find((action) => action.id === idFromUrl());
|
||||
return ok(queriedAction);
|
||||
}
|
||||
|
||||
function approveAction(): Observable<HttpResponse<any>> {
|
||||
const queriedAction: Action = actions.find((action) => action.id === idFromUrl());
|
||||
queriedAction.approval = body.approval;
|
||||
@@ -1133,25 +1151,17 @@ export class MockBackendInterceptor implements HttpInterceptor {
|
||||
return ok(message);
|
||||
}
|
||||
|
||||
function getTokens(): Observable<HttpResponse<any>> {
|
||||
return ok(tokens);
|
||||
function getAccountTypes(): Observable<HttpResponse<any>> {
|
||||
return ok(accountTypes);
|
||||
}
|
||||
|
||||
function getTokenBySymbol(): Observable<HttpResponse<any>> {
|
||||
const queriedToken: Token = tokens.find((token) => token.symbol === stringFromUrl());
|
||||
return ok(queriedToken);
|
||||
function getActions(): Observable<HttpResponse<any>> {
|
||||
return ok(actions);
|
||||
}
|
||||
|
||||
function getCategories(): Observable<HttpResponse<any>> {
|
||||
const categoryList: Array<string> = categories.map((category) => category.name);
|
||||
return ok(categoryList);
|
||||
}
|
||||
|
||||
function getCategoryByProduct(): Observable<HttpResponse<any>> {
|
||||
const queriedCategory: Category = categories.find((category) =>
|
||||
category.products.includes(stringFromUrl())
|
||||
);
|
||||
return ok(queriedCategory.name || 'other');
|
||||
function getActionById(): Observable<HttpResponse<any>> {
|
||||
const queriedAction: Action = actions.find((action) => action.id === idFromUrl());
|
||||
return ok(queriedAction);
|
||||
}
|
||||
|
||||
function getAreaNames(): Observable<HttpResponse<any>> {
|
||||
@@ -1178,24 +1188,37 @@ export class MockBackendInterceptor implements HttpInterceptor {
|
||||
return ok(queriedAreaType.name || 'other');
|
||||
}
|
||||
|
||||
function getAccountTypes(): Observable<HttpResponse<any>> {
|
||||
return ok(accountTypes);
|
||||
function getCategories(): Observable<HttpResponse<any>> {
|
||||
const categoryList: Array<string> = categories.map((category) => category.name);
|
||||
return ok(categoryList);
|
||||
}
|
||||
|
||||
function getTransactionTypes(): Observable<HttpResponse<any>> {
|
||||
return ok(transactionTypes);
|
||||
function getCategoryByProduct(): Observable<HttpResponse<any>> {
|
||||
const queriedCategory: Category = categories.find((category) =>
|
||||
category.products.includes(stringFromUrl())
|
||||
);
|
||||
return ok(queriedCategory.name || 'other');
|
||||
}
|
||||
|
||||
function getGenders(): Observable<HttpResponse<any>> {
|
||||
return ok(genders);
|
||||
}
|
||||
|
||||
// helper functions
|
||||
|
||||
function ok(responseBody: any): Observable<HttpResponse<any>> {
|
||||
return of(new HttpResponse({ status: 200, body: responseBody }));
|
||||
function getTokens(): Observable<HttpResponse<any>> {
|
||||
return ok(tokens);
|
||||
}
|
||||
|
||||
function getTokenBySymbol(): Observable<HttpResponse<any>> {
|
||||
const queriedToken: Token = tokens.find((token) => token.symbol === stringFromUrl());
|
||||
return ok(queriedToken);
|
||||
}
|
||||
|
||||
function getTransactionTypes(): Observable<HttpResponse<any>> {
|
||||
return ok(transactionTypes);
|
||||
}
|
||||
|
||||
// helper functions
|
||||
|
||||
function error(message): Observable<any> {
|
||||
return throwError({ status: 400, error: { message } });
|
||||
}
|
||||
@@ -1205,6 +1228,10 @@ export class MockBackendInterceptor implements HttpInterceptor {
|
||||
return parseInt(urlParts[urlParts.length - 1], 10);
|
||||
}
|
||||
|
||||
function ok(responseBody: any): Observable<HttpResponse<any>> {
|
||||
return of(new HttpResponse({ status: 200, body: responseBody }));
|
||||
}
|
||||
|
||||
function stringFromUrl(): string {
|
||||
const urlParts: Array<string> = url.split('/');
|
||||
return urlParts[urlParts.length - 1];
|
||||
@@ -1212,6 +1239,7 @@ export class MockBackendInterceptor implements HttpInterceptor {
|
||||
}
|
||||
}
|
||||
|
||||
/** Exports the MockBackendInterceptor as an Angular provider. */
|
||||
export const MockBackendProvider = {
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
useClass: MockBackendInterceptor,
|
||||
|
||||
@@ -1,8 +1,31 @@
|
||||
/** An object defining the properties of the data read. */
|
||||
const objCsv: { size: number; dataFile: any } = {
|
||||
size: 0,
|
||||
dataFile: [],
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses data to CSV format.
|
||||
*
|
||||
* @param data - The data to be parsed.
|
||||
* @returns An array of the parsed data.
|
||||
*/
|
||||
function parseData(data: any): Array<any> {
|
||||
const csvData: Array<any> = [];
|
||||
const lineBreak: Array<any> = data.split('\n');
|
||||
lineBreak.forEach((res) => {
|
||||
csvData.push(res.split(','));
|
||||
});
|
||||
console.table(csvData);
|
||||
return csvData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a csv file and converts it to an array.
|
||||
*
|
||||
* @param input - The file to be read.
|
||||
* @returns An array of the read data.
|
||||
*/
|
||||
function readCsv(input: any): Array<any> | void {
|
||||
if (input.files && input.files[0]) {
|
||||
const reader: FileReader = new FileReader();
|
||||
@@ -15,14 +38,5 @@ function readCsv(input: any): Array<any> | void {
|
||||
}
|
||||
}
|
||||
|
||||
function parseData(data: any): Array<any> {
|
||||
const csvData: Array<any> = [];
|
||||
const lineBreak: Array<any> = data.split('\n');
|
||||
lineBreak.forEach((res) => {
|
||||
csvData.push(res.split(','));
|
||||
});
|
||||
console.table(csvData);
|
||||
return csvData;
|
||||
}
|
||||
|
||||
/** @exports */
|
||||
export { readCsv };
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
// Third party imports
|
||||
import { validatePerson, validateVcard } from '@cicnet/schemas-data-validator';
|
||||
|
||||
/**
|
||||
* Validates a person object against the defined Person schema.
|
||||
*
|
||||
* @param person - A person object to be validated.
|
||||
*/
|
||||
async function personValidation(person: any): Promise<void> {
|
||||
const personValidationErrors: any = await validatePerson(person);
|
||||
|
||||
@@ -8,6 +14,11 @@ async function personValidation(person: any): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a vcard object against the defined Vcard schema.
|
||||
*
|
||||
* @param vcard - A vcard object to be validated.
|
||||
*/
|
||||
async function vcardValidation(vcard: any): Promise<void> {
|
||||
const vcardValidationErrors: any = await validateVcard(vcard);
|
||||
|
||||
@@ -16,4 +27,5 @@ async function vcardValidation(vcard: any): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
/** @exports */
|
||||
export { personValidation, vcardValidation };
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Core imports
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
// Application imports
|
||||
import { ErrorInterceptor } from '@app/_interceptors/error.interceptor';
|
||||
|
||||
describe('ErrorInterceptor', () => {
|
||||
|
||||
@@ -1,24 +1,44 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
// Core imports
|
||||
import {
|
||||
HttpRequest,
|
||||
HttpHandler,
|
||||
HttpEvent,
|
||||
HttpInterceptor,
|
||||
HttpErrorResponse,
|
||||
HttpEvent,
|
||||
HttpHandler,
|
||||
HttpInterceptor,
|
||||
HttpRequest,
|
||||
} from '@angular/common/http';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { ErrorDialogService, LoggingService } from '@app/_services';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
// Third party imports
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
|
||||
// Application imports
|
||||
import { ErrorDialogService, LoggingService } from '@app/_services';
|
||||
|
||||
/** Intercepts and handles errors from outgoing HTTP request. */
|
||||
@Injectable()
|
||||
export class ErrorInterceptor implements HttpInterceptor {
|
||||
/**
|
||||
* Initialization of the error interceptor.
|
||||
*
|
||||
* @param errorDialogService - A service that provides a dialog box for displaying errors to the user.
|
||||
* @param loggingService - A service that provides logging capabilities.
|
||||
* @param router - A service that provides navigation among views and URL manipulation capabilities.
|
||||
*/
|
||||
constructor(
|
||||
private errorDialogService: ErrorDialogService,
|
||||
private loggingService: LoggingService,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Intercepts HTTP requests.
|
||||
*
|
||||
* @param request - An outgoing HTTP request with an optional typed body.
|
||||
* @param next - The next HTTP handler or the outgoing request dispatcher.
|
||||
* @returns The error caught from the request.
|
||||
*/
|
||||
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||
return next.handle(request).pipe(
|
||||
catchError((err: HttpErrorResponse) => {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Core imports
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HttpConfigInterceptor } from './http-config.interceptor';
|
||||
// Application imports
|
||||
import { HttpConfigInterceptor } from '@app/_interceptors/http-config.interceptor';
|
||||
|
||||
describe('HttpConfigInterceptor', () => {
|
||||
beforeEach(() =>
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
// Core imports
|
||||
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
|
||||
|
||||
// Third party imports
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
/** Intercepts and handles setting of configurations to outgoing HTTP request. */
|
||||
@Injectable()
|
||||
export class HttpConfigInterceptor implements HttpInterceptor {
|
||||
/** Initialization of http config interceptor. */
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
* Intercepts HTTP requests.
|
||||
*
|
||||
* @param request - An outgoing HTTP request with an optional typed body.
|
||||
* @param next - The next HTTP handler or the outgoing request dispatcher.
|
||||
* @returns The forwarded request.
|
||||
*/
|
||||
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||
// const token: string = sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN'));
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Core imports
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoggingInterceptor } from './logging.interceptor';
|
||||
// Application imports
|
||||
import { LoggingInterceptor } from '@app/_interceptors/logging.interceptor';
|
||||
|
||||
describe('LoggingInterceptor', () => {
|
||||
beforeEach(() =>
|
||||
|
||||
@@ -1,19 +1,37 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
// Core imports
|
||||
import {
|
||||
HttpRequest,
|
||||
HttpHandler,
|
||||
HttpEvent,
|
||||
HttpHandler,
|
||||
HttpInterceptor,
|
||||
HttpRequest,
|
||||
HttpResponse,
|
||||
} from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
// Third party imports
|
||||
import { Observable } from 'rxjs';
|
||||
import { LoggingService } from '@app/_services/logging.service';
|
||||
import { finalize, tap } from 'rxjs/operators';
|
||||
|
||||
// Application imports
|
||||
import { LoggingService } from '@app/_services/logging.service';
|
||||
|
||||
/** Intercepts and handles of events from outgoing HTTP request. */
|
||||
@Injectable()
|
||||
export class LoggingInterceptor implements HttpInterceptor {
|
||||
/**
|
||||
* Initialization of the logging interceptor.
|
||||
*
|
||||
* @param loggingService - A service that provides logging capabilities.
|
||||
*/
|
||||
constructor(private loggingService: LoggingService) {}
|
||||
|
||||
/**
|
||||
* Intercepts HTTP requests.
|
||||
*
|
||||
* @param request - An outgoing HTTP request with an optional typed body.
|
||||
* @param next - The next HTTP handler or the outgoing request dispatcher.
|
||||
* @returns The forwarded request.
|
||||
*/
|
||||
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||
return next.handle(request);
|
||||
// this.loggingService.sendInfoLevelMessage(request);
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
/** Account data interface */
|
||||
interface AccountDetails {
|
||||
date_registered: number;
|
||||
gender: string;
|
||||
/** Age of user */
|
||||
age?: string;
|
||||
type?: string;
|
||||
/** Token balance on account */
|
||||
balance?: number;
|
||||
/** Business category of user. */
|
||||
category?: string;
|
||||
/** Account registration day */
|
||||
date_registered: number;
|
||||
/** User's gender */
|
||||
gender: string;
|
||||
/** Account identifiers */
|
||||
identities: {
|
||||
evm: {
|
||||
'bloxberg:8996': string[];
|
||||
@@ -12,13 +19,17 @@ interface AccountDetails {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
};
|
||||
/** User's location */
|
||||
location: {
|
||||
area?: string;
|
||||
area_name: string;
|
||||
area_type?: string;
|
||||
};
|
||||
/** Products or services provided by user. */
|
||||
products: string[];
|
||||
category?: string;
|
||||
/** Type of account */
|
||||
type?: string;
|
||||
/** Personal identifying information of user */
|
||||
vcard: {
|
||||
email: [
|
||||
{
|
||||
@@ -51,24 +62,37 @@ interface AccountDetails {
|
||||
};
|
||||
}
|
||||
|
||||
/** Meta signature interface */
|
||||
interface Signature {
|
||||
/** Algorithm used */
|
||||
algo: string;
|
||||
/** Data that was signed. */
|
||||
data: string;
|
||||
/** Message digest */
|
||||
digest: string;
|
||||
/** Encryption engine used. */
|
||||
engine: string;
|
||||
}
|
||||
|
||||
/** Meta object interface */
|
||||
interface Meta {
|
||||
/** Account details */
|
||||
data: AccountDetails;
|
||||
/** Meta store id */
|
||||
id: string;
|
||||
/** Signature used during write. */
|
||||
signature: Signature;
|
||||
}
|
||||
|
||||
/** Meta response interface */
|
||||
interface MetaResponse {
|
||||
/** Meta store id */
|
||||
id: string;
|
||||
/** Meta object */
|
||||
m: Meta;
|
||||
}
|
||||
|
||||
/** Default account data object */
|
||||
const defaultAccount: AccountDetails = {
|
||||
date_registered: Date.now(),
|
||||
gender: 'other',
|
||||
@@ -116,4 +140,5 @@ const defaultAccount: AccountDetails = {
|
||||
},
|
||||
};
|
||||
|
||||
export { AccountDetails, Signature, Meta, MetaResponse, defaultAccount };
|
||||
/** @exports */
|
||||
export { AccountDetails, Meta, MetaResponse, Signature, defaultAccount };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export * from '@app/_models/transaction';
|
||||
export * from '@app/_models/settings';
|
||||
export * from '@app/_models/account';
|
||||
export * from '@app/_models/mappings';
|
||||
export * from '@app/_models/settings';
|
||||
export * from '@app/_models/staff';
|
||||
export * from '@app/_models/token';
|
||||
export * from '@app/_models/mappings';
|
||||
export * from '@app/_models/transaction';
|
||||
|
||||
@@ -1,24 +1,40 @@
|
||||
/** Action object interface */
|
||||
interface Action {
|
||||
id: number;
|
||||
user: string;
|
||||
role: string;
|
||||
/** Action performed */
|
||||
action: string;
|
||||
/** Action approval status. */
|
||||
approval: boolean;
|
||||
/** Action ID */
|
||||
id: number;
|
||||
/** Admin's role in the system */
|
||||
role: string;
|
||||
/** Admin who initialized the action. */
|
||||
user: string;
|
||||
}
|
||||
|
||||
interface Category {
|
||||
/** Area name object interface */
|
||||
interface AreaName {
|
||||
/** Locations that map to that area name. */
|
||||
locations: Array<string>;
|
||||
/** Name of area */
|
||||
name: string;
|
||||
}
|
||||
|
||||
/** Area type object interface */
|
||||
interface AreaType {
|
||||
/** Areas that map to that area type. */
|
||||
area: Array<string>;
|
||||
/** Type of area */
|
||||
name: string;
|
||||
}
|
||||
|
||||
/** Category object interface */
|
||||
interface Category {
|
||||
/** Business category */
|
||||
name: string;
|
||||
/** Products that map to that category. */
|
||||
products: Array<string>;
|
||||
}
|
||||
|
||||
interface AreaName {
|
||||
name: string;
|
||||
locations: Array<string>;
|
||||
}
|
||||
|
||||
interface AreaType {
|
||||
name: string;
|
||||
area: Array<string>;
|
||||
}
|
||||
|
||||
export { Action, Category, AreaName, AreaType };
|
||||
/** @exports */
|
||||
export { Action, AreaName, AreaType, Category };
|
||||
|
||||
@@ -1,20 +1,34 @@
|
||||
/** Settings class */
|
||||
class Settings {
|
||||
/** CIC Registry instance */
|
||||
registry: any;
|
||||
/** A resource for searching through blocks on the blockchain network. */
|
||||
scanFilter: any;
|
||||
/** Transaction Helper instance */
|
||||
txHelper: any;
|
||||
/** Web3 Object */
|
||||
w3: W3 = {
|
||||
engine: undefined,
|
||||
provider: undefined,
|
||||
};
|
||||
scanFilter: any;
|
||||
registry: any;
|
||||
txHelper: any;
|
||||
|
||||
/**
|
||||
* Initialize the settings.
|
||||
*
|
||||
* @param scanFilter - A resource for searching through blocks on the blockchain network.
|
||||
*/
|
||||
constructor(scanFilter: any) {
|
||||
this.scanFilter = scanFilter;
|
||||
}
|
||||
}
|
||||
|
||||
class W3 {
|
||||
/** Web3 object interface */
|
||||
interface W3 {
|
||||
/** An active web3 instance connected to the blockchain network. */
|
||||
engine: any;
|
||||
/** The connection socket to the blockchain network. */
|
||||
provider: any;
|
||||
}
|
||||
|
||||
/** @exports */
|
||||
export { Settings, W3 };
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
/** Staff object interface */
|
||||
interface Staff {
|
||||
/** Comment made on the public key. */
|
||||
comment: string;
|
||||
/** Email used to create the public key. */
|
||||
email: string;
|
||||
/** Name of the owner of the public key */
|
||||
name: string;
|
||||
/** Tags added to the public key. */
|
||||
tag: number;
|
||||
/** User ID of the owner of the public key. */
|
||||
userid: string;
|
||||
}
|
||||
|
||||
/** @exports */
|
||||
export { Staff };
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
/** Token object interface */
|
||||
interface Token {
|
||||
name: string;
|
||||
symbol: string;
|
||||
/** Address of the deployed token contract. */
|
||||
address: string;
|
||||
supply: string;
|
||||
/** Number of decimals to convert to smallest denomination of the token. */
|
||||
decimals: string;
|
||||
/** Name of the token. */
|
||||
name: string;
|
||||
/** Address of account that deployed token. */
|
||||
owner?: string;
|
||||
/** Token reserve to token minting ratio. */
|
||||
reserveRatio?: string;
|
||||
/** Token reserve information */
|
||||
reserves?: {
|
||||
'0xa686005CE37Dce7738436256982C3903f2E4ea8E'?: {
|
||||
weight: string;
|
||||
balance: string;
|
||||
};
|
||||
};
|
||||
reserveRatio?: string;
|
||||
owner?: string;
|
||||
/** Total token supply. */
|
||||
supply: string;
|
||||
/** The unique token symbol. */
|
||||
symbol: string;
|
||||
}
|
||||
|
||||
/** @exports */
|
||||
export { Token };
|
||||
|
||||
@@ -1,46 +1,67 @@
|
||||
// Application imports
|
||||
import { AccountDetails } from '@app/_models/account';
|
||||
|
||||
class BlocksBloom {
|
||||
low: number;
|
||||
blockFilter: string;
|
||||
blocktxFilter: string;
|
||||
alg: string;
|
||||
filterRounds: number;
|
||||
/** Conversion object interface */
|
||||
interface Conversion {
|
||||
/** Final transaction token information. */
|
||||
destinationToken: TxToken;
|
||||
/** Initial transaction token amount. */
|
||||
fromValue: number;
|
||||
/** Initial transaction token information. */
|
||||
sourceToken: TxToken;
|
||||
/** Final transaction token amount. */
|
||||
toValue: number;
|
||||
/** Address of the initiator of the conversion. */
|
||||
trader: string;
|
||||
/** Conversion mining information. */
|
||||
tx: Tx;
|
||||
/** Account information of the initiator of the conversion. */
|
||||
user: AccountDetails;
|
||||
}
|
||||
|
||||
class TxToken {
|
||||
address: string;
|
||||
name: string;
|
||||
symbol: string;
|
||||
/** Transaction object interface */
|
||||
interface Transaction {
|
||||
/** Address of the transaction sender. */
|
||||
from: string;
|
||||
/** Account information of the transaction recipient. */
|
||||
recipient: AccountDetails;
|
||||
/** Account information of the transaction sender. */
|
||||
sender: AccountDetails;
|
||||
/** Address of the transaction recipient. */
|
||||
to: string;
|
||||
/** Transaction token information. */
|
||||
token: TxToken;
|
||||
/** Transaction mining information. */
|
||||
tx: Tx;
|
||||
/** Type of transaction. */
|
||||
type?: string;
|
||||
/** Amount of tokens transacted. */
|
||||
value: number;
|
||||
}
|
||||
|
||||
class Tx {
|
||||
/** Transaction data interface */
|
||||
interface Tx {
|
||||
/** Transaction block number. */
|
||||
block: number;
|
||||
/** Transaction mining status. */
|
||||
success: boolean;
|
||||
/** Time transaction was mined. */
|
||||
timestamp: number;
|
||||
/** Hash generated by transaction. */
|
||||
txHash: string;
|
||||
/** Index of transaction in block. */
|
||||
txIndex: number;
|
||||
}
|
||||
|
||||
class Transaction {
|
||||
from: string;
|
||||
sender: AccountDetails;
|
||||
to: string;
|
||||
recipient: AccountDetails;
|
||||
token: TxToken;
|
||||
tx: Tx;
|
||||
value: number;
|
||||
type?: string;
|
||||
/** Transaction token object interface */
|
||||
interface TxToken {
|
||||
/** Address of the deployed token contract. */
|
||||
address: string;
|
||||
/** Name of the token. */
|
||||
name: string;
|
||||
/** The unique token symbol. */
|
||||
symbol: string;
|
||||
}
|
||||
|
||||
class Conversion {
|
||||
destinationToken: TxToken;
|
||||
fromValue: number;
|
||||
sourceToken: TxToken;
|
||||
toValue: number;
|
||||
trader: string;
|
||||
user: AccountDetails;
|
||||
tx: Tx;
|
||||
}
|
||||
|
||||
export { BlocksBloom, TxToken, Tx, Transaction, Conversion };
|
||||
/** @exports */
|
||||
export { Conversion, Transaction, Tx, TxToken };
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// Application imports
|
||||
import { MutablePgpKeyStore } from '@app/_pgp/pgp-key-store';
|
||||
|
||||
describe('PgpKeyStore', () => {
|
||||
|
||||
@@ -1,95 +1,188 @@
|
||||
// Third party imports
|
||||
import { KeyStore } from 'cic-client-meta';
|
||||
// TODO should we put this on the mutable key store object
|
||||
import * as openpgp from 'openpgp';
|
||||
|
||||
/** An openpgp Keyring instance. */
|
||||
const keyring = new openpgp.Keyring();
|
||||
|
||||
/**
|
||||
* Mutable Key store interface.
|
||||
*
|
||||
* @extends KeyStore
|
||||
*/
|
||||
interface MutableKeyStore extends KeyStore {
|
||||
loadKeyring(): void;
|
||||
importKeyPair(publicKey: any, privateKey: any): Promise<void>;
|
||||
importPublicKey(publicKey: any): Promise<void>;
|
||||
importPrivateKey(privateKey: any): Promise<void>;
|
||||
getPublicKeys(): Array<any>;
|
||||
getTrustedKeys(): Array<any>;
|
||||
getTrustedActiveKeys(): Array<any>;
|
||||
getEncryptKeys(): Array<any>;
|
||||
getPrivateKeys(): Array<any>;
|
||||
getPrivateKey(): any;
|
||||
isValidKey(key: any): Promise<boolean>;
|
||||
isEncryptedPrivateKey(privateKey: any): Promise<boolean>;
|
||||
getFingerprint(): string;
|
||||
getKeyId(key: any): string;
|
||||
getPrivateKeyId(): string;
|
||||
getKeysForId(keyId: string): Array<any>;
|
||||
getPublicKeyForId(keyId: string): any;
|
||||
getPrivateKeyForId(keyId: string): any;
|
||||
getPublicKeyForSubkeyId(subkeyId: string): any;
|
||||
getPublicKeysForAddress(address: string): Array<any>;
|
||||
removeKeysForId(keyId: string): Array<any>;
|
||||
removePublicKeyForId(keyId: string): any;
|
||||
removePublicKey(publicKey: any): any;
|
||||
/** Remove all keys from the keyring. */
|
||||
clearKeysInKeyring(): void;
|
||||
/**
|
||||
* Get all the encryption keys.
|
||||
* @returns An array of encryption keys.
|
||||
* @remarks
|
||||
* Current implementation doesn't include encryption keys.
|
||||
* This is included to appease the implemented Keystore interface.
|
||||
*/
|
||||
getEncryptKeys(): Array<any>;
|
||||
/**
|
||||
* Get the first private key's fingerprint.
|
||||
* @returns The first private key's fingerprint.
|
||||
*/
|
||||
getFingerprint(): string;
|
||||
/**
|
||||
* Get a key's keyId.
|
||||
* @param key - The key to fetch the keyId from.
|
||||
* @returns The key's keyId.
|
||||
*/
|
||||
getKeyId(key: any): string;
|
||||
/**
|
||||
* Get keys from the keyring using their keyId.
|
||||
* @param keyId - The keyId of the keys to be fetched from the keyring.
|
||||
* @returns An array of the keys with that keyId.
|
||||
*/
|
||||
getKeysForId(keyId: string): Array<any>;
|
||||
/**
|
||||
* Get the first private key.
|
||||
* @returns The first private key.
|
||||
*/
|
||||
getPrivateKey(): any;
|
||||
/**
|
||||
* Get a private key from the keyring using it's keyId.
|
||||
* @param keyId - The keyId of the private key to be fetched from the keyring.
|
||||
* @returns The private key with that keyId.
|
||||
*/
|
||||
getPrivateKeyForId(keyId: string): any;
|
||||
/**
|
||||
* Get the first private key's keyID.
|
||||
* @returns The first private key's keyId.
|
||||
*/
|
||||
getPrivateKeyId(): string;
|
||||
/**
|
||||
* Get all private keys.
|
||||
* @returns An array of all private keys.
|
||||
*/
|
||||
getPrivateKeys(): Array<any>;
|
||||
/**
|
||||
* Get a public key from the keyring using it's keyId.
|
||||
* @param keyId - The keyId of the public key to be fetched from the keyring.
|
||||
* @returns The public key with that keyId.
|
||||
*/
|
||||
getPublicKeyForId(keyId: string): any;
|
||||
/**
|
||||
* Get a public key from the keyring using it's subkeyId.
|
||||
* @param subkeyId - The subkeyId of the public key to be fetched from the keyring.
|
||||
* @returns The public key with that subkeyId.
|
||||
*/
|
||||
getPublicKeyForSubkeyId(subkeyId: string): any;
|
||||
/**
|
||||
* Get all the public keys.
|
||||
* @returns An array of public keys.
|
||||
*/
|
||||
getPublicKeys(): Array<any>;
|
||||
/**
|
||||
* Get public keys from the keyring using their address.
|
||||
* @param address - The address of the public keys to be fetched from the keyring.
|
||||
* @returns An array of the public keys with that address.
|
||||
*/
|
||||
getPublicKeysForAddress(address: string): Array<any>;
|
||||
/**
|
||||
* Get all the trusted active keys.
|
||||
* @returns An array of trusted active keys.
|
||||
*/
|
||||
getTrustedActiveKeys(): Array<any>;
|
||||
/**
|
||||
* Get all the trusted keys.
|
||||
* @returns An array of trusted keys.
|
||||
*/
|
||||
getTrustedKeys(): Array<any>;
|
||||
/**
|
||||
* Add a key pair to keyring.
|
||||
* @async
|
||||
* @param publicKey - The public key to be added to the keyring.
|
||||
* @param privateKey - The private key to be added to the keyring.
|
||||
* @throws Error
|
||||
*/
|
||||
importKeyPair(publicKey: any, privateKey: any): Promise<void>;
|
||||
/**
|
||||
* Add private key to keyring.
|
||||
* @async
|
||||
* @param privateKey - The private key to be added to the keyring.
|
||||
* @throws Error
|
||||
*/
|
||||
importPrivateKey(privateKey: any): Promise<void>;
|
||||
/**
|
||||
* Add public key to keyring.
|
||||
* @async
|
||||
* @param publicKey - The public key to be added to the keyring.
|
||||
* @throws Error
|
||||
*/
|
||||
importPublicKey(publicKey: any): Promise<void>;
|
||||
/**
|
||||
* Verify that a private key is encrypted.
|
||||
* @async
|
||||
* @param privateKey - The private key to verify.
|
||||
* @returns true - If private key is encrypted.
|
||||
*/
|
||||
isEncryptedPrivateKey(privateKey: any): Promise<boolean>;
|
||||
/**
|
||||
* Test if the input is a valid key.
|
||||
* @async
|
||||
* @param key - The input to be validated.
|
||||
* @returns true - If the input is a valid key.
|
||||
*/
|
||||
isValidKey(key: any): Promise<boolean>;
|
||||
/**
|
||||
* Instantiate the keyring in the keystore.
|
||||
* @async
|
||||
*/
|
||||
loadKeyring(): void;
|
||||
/**
|
||||
* Remove a public key from the keyring using it's keyId.
|
||||
* @param keyId - The keyId of the keys to be removed from the keyring.
|
||||
* @returns An array of the removed keys.
|
||||
*/
|
||||
removeKeysForId(keyId: string): Array<any>;
|
||||
/**
|
||||
* Remove a public key from the keyring.
|
||||
* @param publicKey - The public key to be removed from the keyring.
|
||||
* @returns The removed public key.
|
||||
*/
|
||||
removePublicKey(publicKey: any): any;
|
||||
/**
|
||||
* Remove a public key from the keyring using it's keyId.
|
||||
* @param keyId - The keyId of the public key to be removed from the keyring.
|
||||
* @returns The removed public key.
|
||||
*/
|
||||
removePublicKeyForId(keyId: string): any;
|
||||
/**
|
||||
* Sign message using private key.
|
||||
* @async
|
||||
* @param plainText - The message to be signed.
|
||||
* @returns The generated signature.
|
||||
*/
|
||||
sign(plainText: string): Promise<any>;
|
||||
}
|
||||
|
||||
/** Provides a keyring for pgp keys. */
|
||||
class MutablePgpKeyStore implements MutableKeyStore {
|
||||
async loadKeyring(): Promise<void> {
|
||||
await keyring.load();
|
||||
await keyring.store();
|
||||
}
|
||||
|
||||
async importKeyPair(publicKey: any, privateKey: any): Promise<void> {
|
||||
await keyring.publicKeys.importKey(publicKey);
|
||||
await keyring.privateKeys.importKey(privateKey);
|
||||
}
|
||||
|
||||
async importPublicKey(publicKey: any): Promise<void> {
|
||||
await keyring.publicKeys.importKey(publicKey);
|
||||
}
|
||||
|
||||
async importPrivateKey(privateKey: any): Promise<void> {
|
||||
await keyring.privateKeys.importKey(privateKey);
|
||||
}
|
||||
|
||||
getPublicKeys(): Array<any> {
|
||||
return keyring.publicKeys.keys;
|
||||
}
|
||||
|
||||
getTrustedKeys(): Array<any> {
|
||||
return keyring.publicKeys.keys;
|
||||
}
|
||||
|
||||
getTrustedActiveKeys(): Array<any> {
|
||||
return keyring.publicKeys.keys;
|
||||
/** Remove all keys from the keyring. */
|
||||
clearKeysInKeyring(): void {
|
||||
keyring.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the encryption keys.
|
||||
* @returns An array of encryption keys.
|
||||
* @remarks
|
||||
* Current implementation doesn't include encryption keys.
|
||||
* This is included to appease the implemented Keystore interface.
|
||||
*/
|
||||
getEncryptKeys(): Array<any> {
|
||||
return [];
|
||||
}
|
||||
|
||||
getPrivateKeys(): Array<any> {
|
||||
return keyring.privateKeys.keys;
|
||||
}
|
||||
|
||||
getPrivateKey(): any {
|
||||
return keyring.privateKeys && keyring.privateKeys.keys[0];
|
||||
}
|
||||
|
||||
async isValidKey(key): Promise<boolean> {
|
||||
// There is supposed to be an openpgp.readKey() method but I can't find it?
|
||||
const testKey = await openpgp.key.readArmored(key);
|
||||
return !testKey.err;
|
||||
}
|
||||
|
||||
async isEncryptedPrivateKey(privateKey: any): Promise<boolean> {
|
||||
const imported = await openpgp.key.readArmored(privateKey);
|
||||
for (const key of imported.keys) {
|
||||
if (key.isDecrypted()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first private key's fingerprint.
|
||||
* @returns The first private key's fingerprint.
|
||||
*/
|
||||
getFingerprint(): string {
|
||||
// TODO Handle multiple keys
|
||||
return (
|
||||
@@ -100,10 +193,45 @@ class MutablePgpKeyStore implements MutableKeyStore {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a key's keyId.
|
||||
* @param key - The key to fetch the keyId from.
|
||||
* @returns The key's keyId.
|
||||
*/
|
||||
getKeyId(key: any): string {
|
||||
return key.getKeyId().toHex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get keys from the keyring using their keyId.
|
||||
* @param keyId - The keyId of the keys to be fetched from the keyring.
|
||||
* @returns An array of the keys with that keyId.
|
||||
*/
|
||||
getKeysForId(keyId: string): Array<any> {
|
||||
return keyring.getKeysForId(keyId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first private key.
|
||||
* @returns The first private key.
|
||||
*/
|
||||
getPrivateKey(): any {
|
||||
return keyring.privateKeys && keyring.privateKeys.keys[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a private key from the keyring using it's keyId.
|
||||
* @param keyId - The keyId of the private key to be fetched from the keyring.
|
||||
* @returns The private key with that keyId.
|
||||
*/
|
||||
getPrivateKeyForId(keyId): any {
|
||||
return keyring.privateKeys && keyring.privateKeys.getForId(keyId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first private key's keyID.
|
||||
* @returns The first private key's keyId.
|
||||
*/
|
||||
getPrivateKeyId(): string {
|
||||
// TODO is there a library that comes with angular for doing this?
|
||||
return (
|
||||
@@ -113,43 +241,180 @@ class MutablePgpKeyStore implements MutableKeyStore {
|
||||
);
|
||||
}
|
||||
|
||||
getKeysForId(keyId: string): Array<any> {
|
||||
return keyring.getKeysForId(keyId);
|
||||
/**
|
||||
* Get all private keys.
|
||||
* @returns An array of all private keys.
|
||||
*/
|
||||
getPrivateKeys(): Array<any> {
|
||||
return keyring.privateKeys && keyring.privateKeys.keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a public key from the keyring using it's keyId.
|
||||
* @param keyId - The keyId of the public key to be fetched from the keyring.
|
||||
* @returns The public key with that keyId.
|
||||
*/
|
||||
getPublicKeyForId(keyId): any {
|
||||
return keyring.publicKeys.getForId(keyId);
|
||||
}
|
||||
|
||||
getPrivateKeyForId(keyId): any {
|
||||
return keyring.privateKeys.getForId(keyId);
|
||||
return keyring.publicKeys && keyring.publicKeys.getForId(keyId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a public key from the keyring using it's subkeyId.
|
||||
* @param subkeyId - The subkeyId of the public key to be fetched from the keyring.
|
||||
* @returns The public key with that subkeyId.
|
||||
*/
|
||||
getPublicKeyForSubkeyId(subkeyId): any {
|
||||
return keyring.publicKeys.getForId(subkeyId, true);
|
||||
return keyring.publicKeys && keyring.publicKeys.getForId(subkeyId, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the public keys.
|
||||
* @returns An array of public keys.
|
||||
*/
|
||||
getPublicKeys(): Array<any> {
|
||||
return keyring.publicKeys && keyring.publicKeys.keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get public keys from the keyring using their address.
|
||||
* @param address - The address of the public keys to be fetched from the keyring.
|
||||
* @returns An array of the public keys with that address.
|
||||
*/
|
||||
getPublicKeysForAddress(address): Array<any> {
|
||||
return keyring.publicKeys.getForAddress(address);
|
||||
return keyring.publicKeys && keyring.publicKeys.getForAddress(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the trusted active keys.
|
||||
* @returns An array of trusted active keys.
|
||||
*/
|
||||
getTrustedActiveKeys(): Array<any> {
|
||||
return keyring.publicKeys && keyring.publicKeys.keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the trusted keys.
|
||||
* @returns An array of trusted keys.
|
||||
*/
|
||||
getTrustedKeys(): Array<any> {
|
||||
return keyring.publicKeys && keyring.publicKeys.keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a key pair to keyring.
|
||||
* @async
|
||||
* @param publicKey - The public key to be added to the keyring.
|
||||
* @param privateKey - The private key to be added to the keyring.
|
||||
* @throws Error
|
||||
*/
|
||||
async importKeyPair(publicKey: any, privateKey: any): Promise<void> {
|
||||
try {
|
||||
await keyring.publicKeys.importKey(publicKey);
|
||||
await keyring.privateKeys.importKey(privateKey);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add private key to keyring.
|
||||
* @async
|
||||
* @param privateKey - The private key to be added to the keyring.
|
||||
* @throws Error
|
||||
*/
|
||||
async importPrivateKey(privateKey: any): Promise<void> {
|
||||
try {
|
||||
await keyring.privateKeys.importKey(privateKey);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add public key to keyring.
|
||||
* @async
|
||||
* @param publicKey - The public key to be added to the keyring.
|
||||
* @throws Error
|
||||
*/
|
||||
async importPublicKey(publicKey: any): Promise<void> {
|
||||
try {
|
||||
await keyring.publicKeys.importKey(publicKey);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that a private key is encrypted.
|
||||
* @async
|
||||
* @param privateKey - The private key to verify.
|
||||
* @returns true - If private key is encrypted.
|
||||
*/
|
||||
async isEncryptedPrivateKey(privateKey: any): Promise<boolean> {
|
||||
const imported = await openpgp.key.readArmored(privateKey);
|
||||
for (const key of imported.keys) {
|
||||
if (key.isDecrypted()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the input is a valid key.
|
||||
* @async
|
||||
* @param key - The input to be validated.
|
||||
* @returns true - If the input is a valid key.
|
||||
*/
|
||||
async isValidKey(key): Promise<boolean> {
|
||||
// There is supposed to be an openpgp.readKey() method but I can't find it?
|
||||
const testKey = await openpgp.key.readArmored(key);
|
||||
return !testKey.err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate the keyring in the keystore.
|
||||
* @async
|
||||
*/
|
||||
async loadKeyring(): Promise<void> {
|
||||
await keyring.load();
|
||||
await keyring.store();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a public key from the keyring using it's keyId.
|
||||
* @param keyId - The keyId of the keys to be removed from the keyring.
|
||||
* @returns An array of the removed keys.
|
||||
*/
|
||||
removeKeysForId(keyId): Array<any> {
|
||||
return keyring.removeKeysForId(keyId);
|
||||
}
|
||||
|
||||
removePublicKeyForId(keyId): any {
|
||||
return keyring.publicKeys.removeForId(keyId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a public key from the keyring.
|
||||
* @param publicKey - The public key to be removed from the keyring.
|
||||
* @returns The removed public key.
|
||||
*/
|
||||
removePublicKey(publicKey: any): any {
|
||||
const keyId = publicKey.getKeyId().toHex();
|
||||
return keyring.publicKeys.removeForId(keyId);
|
||||
return keyring.publicKeys && keyring.publicKeys.removeForId(keyId);
|
||||
}
|
||||
|
||||
clearKeysInKeyring(): void {
|
||||
keyring.clear();
|
||||
/**
|
||||
* Remove a public key from the keyring using it's keyId.
|
||||
* @param keyId - The keyId of the public key to be removed from the keyring.
|
||||
* @returns The removed public key.
|
||||
*/
|
||||
removePublicKeyForId(keyId): any {
|
||||
return keyring.publicKeys && keyring.publicKeys.removeForId(keyId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign message using private key.
|
||||
* @async
|
||||
* @param plainText - The message to be signed.
|
||||
* @returns The generated signature.
|
||||
*/
|
||||
async sign(plainText): Promise<any> {
|
||||
const privateKey = this.getPrivateKey();
|
||||
if (!privateKey.isDecrypted()) {
|
||||
@@ -166,4 +431,5 @@ class MutablePgpKeyStore implements MutableKeyStore {
|
||||
}
|
||||
}
|
||||
|
||||
export { MutablePgpKeyStore, MutableKeyStore };
|
||||
/** @exports */
|
||||
export { MutableKeyStore, MutablePgpKeyStore };
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { PGPSigner } from '@app/_pgp/pgp-signer';
|
||||
// Application imports
|
||||
import { MutableKeyStore, MutablePgpKeyStore } from '@app/_pgp/pgp-key-store';
|
||||
import { PGPSigner } from '@app/_pgp/pgp-signer';
|
||||
|
||||
const keystore: MutableKeyStore = new MutablePgpKeyStore();
|
||||
|
||||
describe('PgpSigner', () => {
|
||||
|
||||
@@ -1,53 +1,146 @@
|
||||
// Third party imports
|
||||
import * as openpgp from 'openpgp';
|
||||
|
||||
// Application imports
|
||||
import { MutableKeyStore } from '@app/_pgp/pgp-key-store';
|
||||
import { LoggingService } from '@app/_services/logging.service';
|
||||
|
||||
const openpgp = require('openpgp');
|
||||
|
||||
/** Signable object interface */
|
||||
interface Signable {
|
||||
/** The message to be signed. */
|
||||
digest(): string;
|
||||
}
|
||||
|
||||
/** Signature object interface */
|
||||
interface Signature {
|
||||
engine: string;
|
||||
/** Encryption algorithm used */
|
||||
algo: string;
|
||||
/** Data to be signed. */
|
||||
data: string;
|
||||
/** Message digest */
|
||||
digest: string;
|
||||
/** Encryption engine used. */
|
||||
engine: string;
|
||||
}
|
||||
|
||||
/** Signer interface */
|
||||
interface Signer {
|
||||
onsign(signature: Signature): void;
|
||||
onverify(flag: boolean): void;
|
||||
/**
|
||||
* Get the private key fingerprint.
|
||||
* @returns A private key fingerprint.
|
||||
*/
|
||||
fingerprint(): string;
|
||||
/** Event triggered on successful signing of message. */
|
||||
onsign(signature: Signature): void;
|
||||
/** Event triggered on successful verification of a signature. */
|
||||
onverify(flag: boolean): void;
|
||||
/**
|
||||
* Load the message digest.
|
||||
* @param material - A signable object.
|
||||
* @returns true - If digest has been loaded successfully.
|
||||
*/
|
||||
prepare(material: Signable): boolean;
|
||||
verify(digest: string, signature: Signature): void;
|
||||
/**
|
||||
* Signs a message using a private key.
|
||||
* @async
|
||||
* @param digest - The message to be signed.
|
||||
*/
|
||||
sign(digest: string): Promise<void>;
|
||||
/**
|
||||
* Verify that signature is valid.
|
||||
* @param digest - The message that was signed.
|
||||
* @param signature - The generated signature.
|
||||
*/
|
||||
verify(digest: string, signature: Signature): void;
|
||||
}
|
||||
|
||||
/** Provides functionality for signing and verifying signed messages. */
|
||||
class PGPSigner implements Signer {
|
||||
engine = 'pgp';
|
||||
/** Encryption algorithm used */
|
||||
algo = 'sha256';
|
||||
/** Message digest */
|
||||
dgst: string;
|
||||
signature: Signature;
|
||||
/** Encryption engine used. */
|
||||
engine = 'pgp';
|
||||
/** A keystore holding pgp keys. */
|
||||
keyStore: MutableKeyStore;
|
||||
onsign: (signature: Signature) => void;
|
||||
onverify: (flag: boolean) => void;
|
||||
/** A service that provides logging capabilities. */
|
||||
loggingService: LoggingService;
|
||||
/** Event triggered on successful signing of message. */
|
||||
onsign: (signature: Signature) => void;
|
||||
/** Event triggered on successful verification of a signature. */
|
||||
onverify: (flag: boolean) => void;
|
||||
/** Generated signature */
|
||||
signature: Signature;
|
||||
|
||||
/**
|
||||
* Initializing the Signer.
|
||||
* @param keyStore - A keystore holding pgp keys.
|
||||
*/
|
||||
constructor(keyStore: MutableKeyStore) {
|
||||
this.keyStore = keyStore;
|
||||
this.onsign = (signature: Signature) => {};
|
||||
this.onverify = (flag: boolean) => {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the private key fingerprint.
|
||||
* @returns A private key fingerprint.
|
||||
*/
|
||||
public fingerprint(): string {
|
||||
return this.keyStore.getFingerprint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the message digest.
|
||||
* @param material - A signable object.
|
||||
* @returns true - If digest has been loaded successfully.
|
||||
*/
|
||||
public prepare(material: Signable): boolean {
|
||||
this.dgst = material.digest();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs a message using a private key.
|
||||
* @async
|
||||
* @param digest - The message to be signed.
|
||||
*/
|
||||
public async sign(digest: string): Promise<void> {
|
||||
const m = openpgp.cleartext.fromText(digest);
|
||||
const pk = this.keyStore.getPrivateKey();
|
||||
if (!pk.isDecrypted()) {
|
||||
const password = window.prompt('password');
|
||||
await pk.decrypt(password);
|
||||
}
|
||||
const opts = {
|
||||
message: m,
|
||||
privateKeys: [pk],
|
||||
detached: true,
|
||||
};
|
||||
openpgp
|
||||
.sign(opts)
|
||||
.then((s) => {
|
||||
this.signature = {
|
||||
engine: this.engine,
|
||||
algo: this.algo,
|
||||
data: s.signature,
|
||||
// TODO: fix for browser later
|
||||
digest,
|
||||
};
|
||||
this.onsign(this.signature);
|
||||
})
|
||||
.catch((e) => {
|
||||
this.loggingService.sendErrorLevelMessage(e.message, this, { error: e });
|
||||
this.onsign(undefined);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that signature is valid.
|
||||
* @param digest - The message that was signed.
|
||||
* @param signature - The generated signature.
|
||||
*/
|
||||
public verify(digest: string, signature: Signature): void {
|
||||
openpgp.signature
|
||||
.readArmored(signature.data)
|
||||
@@ -79,36 +172,7 @@ class PGPSigner implements Signer {
|
||||
this.onverify(false);
|
||||
});
|
||||
}
|
||||
|
||||
public async sign(digest: string): Promise<void> {
|
||||
const m = openpgp.cleartext.fromText(digest);
|
||||
const pk = this.keyStore.getPrivateKey();
|
||||
if (!pk.isDecrypted()) {
|
||||
const password = window.prompt('password');
|
||||
await pk.decrypt(password);
|
||||
}
|
||||
const opts = {
|
||||
message: m,
|
||||
privateKeys: [pk],
|
||||
detached: true,
|
||||
};
|
||||
openpgp
|
||||
.sign(opts)
|
||||
.then((s) => {
|
||||
this.signature = {
|
||||
engine: this.engine,
|
||||
algo: this.algo,
|
||||
data: s.signature,
|
||||
// TODO: fix for browser later
|
||||
digest,
|
||||
};
|
||||
this.onsign(this.signature);
|
||||
})
|
||||
.catch((e) => {
|
||||
this.loggingService.sendErrorLevelMessage(e.message, this, { error: e });
|
||||
this.onsign(undefined);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { Signable, Signature, Signer, PGPSigner };
|
||||
/** @exports */
|
||||
export { PGPSigner, Signable, Signature, Signer };
|
||||
|
||||
@@ -131,6 +131,10 @@ export class AuthService {
|
||||
this.setState('Click button to log in with PGP key ' + this.mutableKeyStore.getPrivateKeyId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws
|
||||
* @param privateKeyArmored - Private key.
|
||||
*/
|
||||
async setKey(privateKeyArmored): Promise<boolean> {
|
||||
try {
|
||||
const isValidKeyCheck = await this.mutableKeyStore.isValidKey(privateKeyArmored);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Settings } from '@app/_models';
|
||||
import { TransactionHelper } from 'cic-client';
|
||||
import { TransactionHelper } from '@cicnet/cic-client';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { TransactionService } from '@app/_services/transaction.service';
|
||||
import { environment } from '@src/environments/environment';
|
||||
@@ -39,10 +39,6 @@ export class BlockSyncService {
|
||||
settings.txHelper.onconversion = async (transaction: any): Promise<any> => {
|
||||
window.dispatchEvent(this.newEvent(transaction, 'cic_convert'));
|
||||
};
|
||||
// settings.registry.onload = (addressReturned: string): void => {
|
||||
// this.loggingService.sendInfoLevelMessage(`Loaded network contracts ${addressReturned}`);
|
||||
// this.readyStateProcessor(settings, readyStateElements.network, address, offset, limit);
|
||||
// };
|
||||
this.readyStateProcessor(settings, readyStateElements.network, address, offset, limit);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { environment } from '@src/environments/environment';
|
||||
import { CICRegistry, FileGetter } from 'cic-client';
|
||||
import { CICRegistry, FileGetter } from '@cicnet/cic-client';
|
||||
import { HttpGetter } from '@app/_helpers';
|
||||
import { Web3Service } from '@app/_services/web3.service';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CICRegistry } from 'cic-client';
|
||||
import { CICRegistry } from '@cicnet/cic-client';
|
||||
import { TokenRegistry } from '@app/_eth';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { RegistryService } from '@app/_services/registry.service';
|
||||
@@ -12,23 +12,21 @@ import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||
export class TokenService {
|
||||
registry: CICRegistry;
|
||||
tokenRegistry: TokenRegistry;
|
||||
onload: (status: boolean) => void;
|
||||
tokens: Array<Token> = [];
|
||||
private tokensList: BehaviorSubject<Array<Token>> = new BehaviorSubject<Array<Token>>(
|
||||
this.tokens
|
||||
);
|
||||
tokensSubject: Observable<Array<Token>> = this.tokensList.asObservable();
|
||||
load: BehaviorSubject<any> = new BehaviorSubject<any>(false);
|
||||
|
||||
constructor(private httpClient: HttpClient) {}
|
||||
|
||||
async init(): Promise<void> {
|
||||
this.registry = await RegistryService.getRegistry();
|
||||
this.registry.onload = async (address: string): Promise<void> => {
|
||||
this.tokenRegistry = new TokenRegistry(
|
||||
await this.registry.getContractAddressByName('TokenRegistry')
|
||||
);
|
||||
this.onload(this.tokenRegistry !== undefined);
|
||||
};
|
||||
this.tokenRegistry = new TokenRegistry(
|
||||
await this.registry.getContractAddressByName('TokenRegistry')
|
||||
);
|
||||
this.load.next(true);
|
||||
}
|
||||
|
||||
addToken(token: Token): void {
|
||||
@@ -73,7 +71,17 @@ export class TokenService {
|
||||
}
|
||||
|
||||
async getTokenBalance(address: string): Promise<(address: string) => Promise<number>> {
|
||||
const sarafuToken = await this.registry.addToken(await this.tokenRegistry.entry(0));
|
||||
return await sarafuToken.methods.balanceOf(address).call();
|
||||
const token = await this.registry.addToken(await this.tokenRegistry.entry(0));
|
||||
return await token.methods.balanceOf(address).call();
|
||||
}
|
||||
|
||||
async getTokenName(): Promise<string> {
|
||||
const token = await this.registry.addToken(await this.tokenRegistry.entry(0));
|
||||
return await token.methods.name().call();
|
||||
}
|
||||
|
||||
async getTokenSymbol(): Promise<string> {
|
||||
const token = await this.registry.addToken(await this.tokenRegistry.entry(0));
|
||||
return await token.methods.symbol().call();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { AuthService } from '@app/_services/auth.service';
|
||||
import { defaultAccount } from '@app/_models';
|
||||
import { LoggingService } from '@app/_services/logging.service';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { CICRegistry } from 'cic-client';
|
||||
import { CICRegistry } from '@cicnet/cic-client';
|
||||
import { RegistryService } from '@app/_services/registry.service';
|
||||
import Web3 from 'web3';
|
||||
import { Web3Service } from '@app/_services/web3.service';
|
||||
@@ -149,44 +149,42 @@ export class TransactionService {
|
||||
recipientAddress: string,
|
||||
value: number
|
||||
): Promise<any> {
|
||||
this.registry.onload = async (addressReturned: string): Promise<void> => {
|
||||
const transferAuthAddress = await this.registry.getContractAddressByName(
|
||||
'TransferAuthorization'
|
||||
);
|
||||
const hashFunction = new Keccak(256);
|
||||
hashFunction.update('createRequest(address,address,address,uint256)');
|
||||
const hash = hashFunction.digest();
|
||||
const methodSignature = hash.toString('hex').substring(0, 8);
|
||||
const abiCoder = new utils.AbiCoder();
|
||||
const abi = await abiCoder.encode(
|
||||
['address', 'address', 'address', 'uint256'],
|
||||
[senderAddress, recipientAddress, tokenAddress, value]
|
||||
);
|
||||
const data = fromHex(methodSignature + strip0x(abi));
|
||||
const tx = new Tx(environment.bloxbergChainId);
|
||||
tx.nonce = await this.web3.eth.getTransactionCount(senderAddress);
|
||||
tx.gasPrice = Number(await this.web3.eth.getGasPrice());
|
||||
tx.gasLimit = 8000000;
|
||||
tx.to = fromHex(strip0x(transferAuthAddress));
|
||||
tx.value = toValue(value);
|
||||
tx.data = data;
|
||||
const txMsg = tx.message();
|
||||
const keystore = await KeystoreService.getKeystore();
|
||||
const privateKey = keystore.getPrivateKey();
|
||||
if (!privateKey.isDecrypted()) {
|
||||
const password = window.prompt('password');
|
||||
await privateKey.decrypt(password);
|
||||
}
|
||||
const signatureObject = secp256k1.ecdsaSign(txMsg, privateKey.keyPacket.privateParams.d);
|
||||
const r = signatureObject.signature.slice(0, 32);
|
||||
const s = signatureObject.signature.slice(32);
|
||||
const v = signatureObject.recid;
|
||||
tx.setSignature(r, s, v);
|
||||
const txWire = add0x(toHex(tx.serializeRLP()));
|
||||
const result = await this.web3.eth.sendSignedTransaction(txWire);
|
||||
this.loggingService.sendInfoLevelMessage(`Result: ${result}`);
|
||||
const transaction = await this.web3.eth.getTransaction(result.transactionHash);
|
||||
this.loggingService.sendInfoLevelMessage(`Transaction: ${transaction}`);
|
||||
};
|
||||
const transferAuthAddress = await this.registry.getContractAddressByName(
|
||||
'TransferAuthorization'
|
||||
);
|
||||
const hashFunction = new Keccak(256);
|
||||
hashFunction.update('createRequest(address,address,address,uint256)');
|
||||
const hash = hashFunction.digest();
|
||||
const methodSignature = hash.toString('hex').substring(0, 8);
|
||||
const abiCoder = new utils.AbiCoder();
|
||||
const abi = await abiCoder.encode(
|
||||
['address', 'address', 'address', 'uint256'],
|
||||
[senderAddress, recipientAddress, tokenAddress, value]
|
||||
);
|
||||
const data = fromHex(methodSignature + strip0x(abi));
|
||||
const tx = new Tx(environment.bloxbergChainId);
|
||||
tx.nonce = await this.web3.eth.getTransactionCount(senderAddress);
|
||||
tx.gasPrice = Number(await this.web3.eth.getGasPrice());
|
||||
tx.gasLimit = 8000000;
|
||||
tx.to = fromHex(strip0x(transferAuthAddress));
|
||||
tx.value = toValue(value);
|
||||
tx.data = data;
|
||||
const txMsg = tx.message();
|
||||
const keystore = await KeystoreService.getKeystore();
|
||||
const privateKey = keystore.getPrivateKey();
|
||||
if (!privateKey.isDecrypted()) {
|
||||
const password = window.prompt('password');
|
||||
await privateKey.decrypt(password);
|
||||
}
|
||||
const signatureObject = secp256k1.ecdsaSign(txMsg, privateKey.keyPacket.privateParams.d);
|
||||
const r = signatureObject.signature.slice(0, 32);
|
||||
const s = signatureObject.signature.slice(32);
|
||||
const v = signatureObject.recid;
|
||||
tx.setSignature(r, s, v);
|
||||
const txWire = add0x(toHex(tx.serializeRLP()));
|
||||
const result = await this.web3.eth.sendSignedTransaction(txWire);
|
||||
this.loggingService.sendInfoLevelMessage(`Result: ${result}`);
|
||||
const transaction = await this.web3.eth.getTransaction(result.transactionHash);
|
||||
this.loggingService.sendInfoLevelMessage(`Transaction: ${transaction}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,26 +22,6 @@ describe('UserService', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return user for available id', () => {
|
||||
expect(service.getAccountById(1)).toEqual({
|
||||
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',
|
||||
});
|
||||
});
|
||||
|
||||
it('should not return user for unavailable id', () => {
|
||||
expect(service.getAccountById(9999999999)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return action for available id', () => {
|
||||
expect(service.getActionById('1')).toEqual({
|
||||
id: 1,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { TokenService } from '@app/_services/token.service';
|
||||
import { AccountIndex } from '@app/_eth';
|
||||
import { MutableKeyStore, PGPSigner, Signer } from '@app/_pgp';
|
||||
import { RegistryService } from '@app/_services/registry.service';
|
||||
import { CICRegistry } from 'cic-client';
|
||||
import { CICRegistry } from '@cicnet/cic-client';
|
||||
import { AuthService } from '@app/_services/auth.service';
|
||||
import { personValidation, vcardValidation } from '@app/_helpers';
|
||||
import { add0x } from '@src/assets/js/ethtx/dist/hex';
|
||||
@@ -202,11 +202,13 @@ export class UserService {
|
||||
const account: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
|
||||
const accountInfo = account.m.data;
|
||||
await personValidation(accountInfo);
|
||||
this.tokenService.onload = async (status: boolean): Promise<void> => {
|
||||
accountInfo.balance = await this.tokenService.getTokenBalance(
|
||||
accountInfo.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0]
|
||||
);
|
||||
};
|
||||
this.tokenService.load.subscribe(async (status: boolean) => {
|
||||
if (status) {
|
||||
accountInfo.balance = await this.tokenService.getTokenBalance(
|
||||
accountInfo.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0]
|
||||
);
|
||||
}
|
||||
});
|
||||
accountInfo.vcard = vCard.parse(atob(accountInfo.vcard));
|
||||
await vcardValidation(accountInfo.vcard);
|
||||
this.addAccount(accountInfo, limit);
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { PasswordToggleDirective } from '@app/auth/_directives/password-toggle.directive';
|
||||
// Core imports
|
||||
import { ElementRef, Renderer2 } from '@angular/core';
|
||||
|
||||
// Application imports
|
||||
import { PasswordToggleDirective } from '@app/auth/_directives/password-toggle.directive';
|
||||
|
||||
// tslint:disable-next-line:prefer-const
|
||||
let elementRef: ElementRef;
|
||||
// tslint:disable-next-line:prefer-const
|
||||
|
||||
@@ -1,21 +1,32 @@
|
||||
// Core imports
|
||||
import { Directive, ElementRef, Input, Renderer2 } from '@angular/core';
|
||||
|
||||
/** Toggle password form field input visibility */
|
||||
@Directive({
|
||||
selector: '[appPasswordToggle]',
|
||||
})
|
||||
export class PasswordToggleDirective {
|
||||
/** The password form field id */
|
||||
@Input()
|
||||
id: string;
|
||||
|
||||
/** The password form field icon id */
|
||||
@Input()
|
||||
iconId: string;
|
||||
|
||||
/**
|
||||
* Handle click events on the html element.
|
||||
*
|
||||
* @param elementRef - A wrapper around a native element inside of a View.
|
||||
* @param renderer - Extend this base class to implement custom rendering.
|
||||
*/
|
||||
constructor(private elementRef: ElementRef, private renderer: Renderer2) {
|
||||
this.renderer.listen(this.elementRef.nativeElement, 'click', () => {
|
||||
this.togglePasswordVisibility();
|
||||
});
|
||||
}
|
||||
|
||||
/** Toggle the visibility of the password input field value and accompanying icon. */
|
||||
togglePasswordVisibility(): void {
|
||||
const password: HTMLElement = document.getElementById(this.id);
|
||||
const icon: HTMLElement = document.getElementById(this.iconId);
|
||||
|
||||
@@ -29,6 +29,9 @@ export class AuthComponent implements OnInit {
|
||||
this.keyForm = this.formBuilder.group({
|
||||
key: ['', Validators.required],
|
||||
});
|
||||
if (this.authService.getPrivateKey()) {
|
||||
this.authService.loginView();
|
||||
}
|
||||
}
|
||||
|
||||
get keyFormStub(): any {
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<h3>
|
||||
<strong> {{account?.vcard?.fn[0].value}} </strong>
|
||||
</h3>
|
||||
<span class="ml-auto"><strong>Balance:</strong> {{account?.balance | tokenRatio}} SRF</span>
|
||||
<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>
|
||||
@@ -218,7 +218,7 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{account?.vcard?.fn[0].value}}</td>
|
||||
<td>{{account?.balance | tokenRatio}}</td>
|
||||
<td>{{account?.balance | tokenRatio}} {{ tokenSymbol | uppercase }}</td>
|
||||
<td>{{account?.date_registered | unixDate}}</td>
|
||||
<td>
|
||||
<span class="badge badge-success badge-pill">
|
||||
@@ -274,8 +274,8 @@
|
||||
<ng-container matColumnDef="value">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Value </th>
|
||||
<td mat-cell *matCellDef="let transaction">
|
||||
<span *ngIf="transaction.type == 'transaction'">{{transaction?.value | tokenRatio}}</span>
|
||||
<span *ngIf="transaction.type == 'conversion'">{{transaction?.toValue | tokenRatio}}</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>
|
||||
|
||||
@@ -348,7 +348,7 @@
|
||||
|
||||
<ng-container matColumnDef="balance">
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header> BALANCE </mat-header-cell>
|
||||
<mat-cell *matCellDef="let user"> {{user?.balance}} </mat-cell>
|
||||
<mat-cell *matCellDef="let user"> {{user?.balance | tokenRatio}} {{tokenSymbol | uppercase}} </mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="location">
|
||||
|
||||
@@ -64,6 +64,7 @@ export class AccountDetailsComponent implements OnInit {
|
||||
matcher: CustomErrorStateMatcher = new CustomErrorStateMatcher();
|
||||
submitted: boolean = false;
|
||||
bloxbergLink: string;
|
||||
tokenSymbol: string;
|
||||
|
||||
constructor(
|
||||
private formBuilder: FormBuilder,
|
||||
@@ -113,7 +114,7 @@ export class AccountDetailsComponent implements OnInit {
|
||||
this.loggingService.sendInfoLevelMessage(this.account);
|
||||
const fullName = this.account.vcard?.fn[0].value.split(' ');
|
||||
this.accountInfoForm.patchValue({
|
||||
firstName: fullName[0],
|
||||
firstName: fullName[0].split(',')[0],
|
||||
lastName: fullName.slice(1).join(' '),
|
||||
phoneNumber: this.account.vcard?.tel[0].value,
|
||||
age: this.account.age,
|
||||
@@ -189,6 +190,11 @@ export class AccountDetailsComponent implements OnInit {
|
||||
.getGenders()
|
||||
.pipe(first())
|
||||
.subscribe((res) => (this.genders = res));
|
||||
this.tokenService.load.subscribe(async (status: boolean) => {
|
||||
if (status) {
|
||||
this.tokenSymbol = await this.tokenService.getTokenSymbol();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
doTransactionFilter(value: string): void {
|
||||
@@ -220,7 +226,7 @@ export class AccountDetailsComponent implements OnInit {
|
||||
}
|
||||
const accountKey = await this.userService.changeAccountInfo(
|
||||
this.accountAddress,
|
||||
this.accountInfoFormStub.firstName.value + ' ' + this.accountInfoFormStub.lastName.value,
|
||||
this.accountInfoFormStub.firstName.value + ', ' + this.accountInfoFormStub.lastName.value,
|
||||
this.accountInfoFormStub.phoneNumber.value,
|
||||
this.accountInfoFormStub.age.value,
|
||||
this.accountInfoFormStub.type.value,
|
||||
|
||||
@@ -29,9 +29,11 @@ export class TokensComponent implements OnInit {
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
await this.tokenService.init();
|
||||
this.tokenService.onload = async (status: boolean): Promise<void> => {
|
||||
await this.tokenService.getTokens();
|
||||
};
|
||||
this.tokenService.load.subscribe(async (status: boolean) => {
|
||||
if (status) {
|
||||
await this.tokenService.getTokens();
|
||||
}
|
||||
});
|
||||
this.tokenService.tokensSubject.subscribe((tokens) => {
|
||||
this.loggingService.sendInfoLevelMessage(tokens);
|
||||
this.dataSource = new MatTableDataSource(tokens);
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<button mat-raised-button color="primary" class="btn btn-outline-info" (click)="viewRecipient()">View Recipient</button>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span>Amount: SRF {{transaction.value | tokenRatio}}</span>
|
||||
<span>Amount: {{transaction.value | tokenRatio}} {{ tokenSymbol | uppercase }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<h4 class="mt-2">Token: </h4>
|
||||
@@ -43,10 +43,10 @@
|
||||
</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span>Name: Sarafu Token</span>
|
||||
<span>Name: {{ tokenName }}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span>Symbol: SRF</span>
|
||||
<span>Symbol: {{ tokenSymbol | uppercase }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -109,10 +109,10 @@
|
||||
<span>Name: {{transaction.sourceToken.name}}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span>Symbol: {{transaction.sourceToken.symbol}}</span>
|
||||
<span>Symbol: {{transaction.sourceToken.symbol | uppercase}}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span>Amount: {{transaction.sourceToken.symbol + ' ' + transaction.fromValue}}</span>
|
||||
<span>Amount: {{transaction.fromValue}} {{transaction.sourceToken.symbol | uppercase}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -130,10 +130,10 @@
|
||||
<span>Name: {{transaction.destinationToken.name}}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span>Symbol: {{transaction.destinationToken.symbol}}</span>
|
||||
<span>Symbol: {{transaction.destinationToken.symbol | uppercase}}</span>
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span>Amount: {{transaction.destinationToken.symbol + ' ' + transaction.toValue}}</span>
|
||||
<span>Amount: {{transaction.toValue}} {{transaction.destinationToken.symbol | uppercase}}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { TransactionService } from '@app/_services';
|
||||
import { TokenService, TransactionService } from '@app/_services';
|
||||
import { copyToClipboard } from '@app/_helpers';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { strip0x } from '@src/assets/js/ethtx/dist/hex';
|
||||
@@ -26,15 +26,19 @@ export class TransactionDetailsComponent implements OnInit {
|
||||
senderBloxbergLink: string;
|
||||
recipientBloxbergLink: string;
|
||||
traderBloxbergLink: string;
|
||||
tokenName: string;
|
||||
tokenSymbol: string;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
private transactionService: TransactionService,
|
||||
private snackBar: MatSnackBar
|
||||
private snackBar: MatSnackBar,
|
||||
private tokenService: TokenService
|
||||
) {}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
await this.transactionService.init();
|
||||
await this.tokenService.init();
|
||||
if (this.transaction?.type === 'conversion') {
|
||||
this.traderBloxbergLink =
|
||||
'https://blockexplorer.bloxberg.org/address/' + this.transaction?.trader + '/transactions';
|
||||
@@ -44,6 +48,12 @@ export class TransactionDetailsComponent implements OnInit {
|
||||
this.recipientBloxbergLink =
|
||||
'https://blockexplorer.bloxberg.org/address/' + this.transaction?.to + '/transactions';
|
||||
}
|
||||
this.tokenService.load.subscribe(async (status: boolean) => {
|
||||
if (status) {
|
||||
this.tokenSymbol = await this.tokenService.getTokenSymbol();
|
||||
this.tokenName = await this.tokenService.getTokenName();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async viewSender(): Promise<void> {
|
||||
|
||||
@@ -59,8 +59,8 @@
|
||||
<ng-container matColumnDef="value">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Value </th>
|
||||
<td mat-cell *matCellDef="let transaction">
|
||||
<span *ngIf="transaction.type == 'transaction'">{{transaction?.value | tokenRatio}}</span>
|
||||
<span *ngIf="transaction.type == 'conversion'">{{transaction?.toValue | tokenRatio}}</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>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
OnInit,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { BlockSyncService, TransactionService, UserService } from '@app/_services';
|
||||
import { BlockSyncService, TokenService, TransactionService, UserService } from '@app/_services';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
@@ -28,6 +28,7 @@ export class TransactionsComponent implements OnInit, AfterViewInit {
|
||||
transaction: Transaction;
|
||||
transactionsType: string = 'all';
|
||||
transactionsTypes: Array<string>;
|
||||
tokenSymbol: string;
|
||||
|
||||
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||
@ViewChild(MatSort) sort: MatSort;
|
||||
@@ -35,7 +36,8 @@ export class TransactionsComponent implements OnInit, AfterViewInit {
|
||||
constructor(
|
||||
private blockSyncService: BlockSyncService,
|
||||
private transactionService: TransactionService,
|
||||
private userService: UserService
|
||||
private userService: UserService,
|
||||
private tokenService: TokenService
|
||||
) {}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
@@ -46,6 +48,7 @@ export class TransactionsComponent implements OnInit, AfterViewInit {
|
||||
this.transactions = transactions;
|
||||
});
|
||||
await this.blockSyncService.init();
|
||||
await this.tokenService.init();
|
||||
await this.transactionService.init();
|
||||
await this.userService.init();
|
||||
await this.blockSyncService.blockSync();
|
||||
@@ -53,6 +56,11 @@ export class TransactionsComponent implements OnInit, AfterViewInit {
|
||||
.getTransactionTypes()
|
||||
.pipe(first())
|
||||
.subscribe((res) => (this.transactionsTypes = res));
|
||||
this.tokenService.load.subscribe(async (status: boolean) => {
|
||||
if (status) {
|
||||
this.tokenSymbol = await this.tokenService.getTokenSymbol();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
viewTransaction(transaction): void {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { MenuSelectionDirective } from '@app/shared/_directives/menu-selection.directive';
|
||||
// Core imports
|
||||
import { ElementRef, Renderer2 } from '@angular/core';
|
||||
|
||||
// Application imports
|
||||
import { MenuSelectionDirective } from '@app/shared/_directives/menu-selection.directive';
|
||||
|
||||
describe('MenuSelectionDirective', () => {
|
||||
// tslint:disable-next-line:prefer-const
|
||||
let elementRef: ElementRef;
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
// Core imports
|
||||
import { Directive, ElementRef, Renderer2 } from '@angular/core';
|
||||
|
||||
/** Toggle availability of sidebar on menu item selection. */
|
||||
@Directive({
|
||||
selector: '[appMenuSelection]',
|
||||
})
|
||||
export class MenuSelectionDirective {
|
||||
/**
|
||||
* Handle click events on the html element.
|
||||
*
|
||||
* @param elementRef - A wrapper around a native element inside of a View.
|
||||
* @param renderer - Extend this base class to implement custom rendering.
|
||||
*/
|
||||
constructor(private elementRef: ElementRef, private renderer: Renderer2) {
|
||||
this.renderer.listen(this.elementRef.nativeElement, 'click', () => {
|
||||
const mediaQuery = window.matchMedia('(max-width: 768px)');
|
||||
@@ -13,6 +21,7 @@ export class MenuSelectionDirective {
|
||||
});
|
||||
}
|
||||
|
||||
/** Toggle the availability of the sidebar. */
|
||||
onMenuSelect(): void {
|
||||
const sidebar: HTMLElement = document.getElementById('sidebar');
|
||||
if (!sidebar?.classList.contains('active')) {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { MenuToggleDirective } from '@app/shared/_directives/menu-toggle.directive';
|
||||
// Core imports
|
||||
import { ElementRef, Renderer2 } from '@angular/core';
|
||||
|
||||
// Application imports
|
||||
import { MenuToggleDirective } from '@app/shared/_directives/menu-toggle.directive';
|
||||
|
||||
describe('MenuToggleDirective', () => {
|
||||
// tslint:disable-next-line:prefer-const
|
||||
let elementRef: ElementRef;
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
// Core imports
|
||||
import { Directive, ElementRef, Renderer2 } from '@angular/core';
|
||||
|
||||
/** Toggle availability of sidebar on menu toggle click. */
|
||||
@Directive({
|
||||
selector: '[appMenuToggle]',
|
||||
})
|
||||
export class MenuToggleDirective {
|
||||
/**
|
||||
* Handle click events on the html element.
|
||||
*
|
||||
* @param elementRef - A wrapper around a native element inside of a View.
|
||||
* @param renderer - Extend this base class to implement custom rendering.
|
||||
*/
|
||||
constructor(private elementRef: ElementRef, private renderer: Renderer2) {
|
||||
this.renderer.listen(this.elementRef.nativeElement, 'click', () => {
|
||||
this.onMenuToggle();
|
||||
});
|
||||
}
|
||||
|
||||
// Menu Trigger
|
||||
/** Toggle the availability of the sidebar. */
|
||||
onMenuToggle(): void {
|
||||
const sidebar: HTMLElement = document.getElementById('sidebar');
|
||||
sidebar?.classList.toggle('active');
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { SafePipe } from './safe.pipe';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
|
||||
// tslint:disable-next-line:prefer-const
|
||||
let sanitizer: DomSanitizer;
|
||||
|
||||
describe('SafePipe', () => {
|
||||
it('create an instance', () => {
|
||||
const pipe = new SafePipe();
|
||||
const pipe = new SafePipe(sanitizer);
|
||||
expect(pipe).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({ name: 'tokenRatio' })
|
||||
export class TokenRatioPipe implements PipeTransform {
|
||||
transform(value: any, ...args): any {
|
||||
transform(value: any = 0, ...args): any {
|
||||
return Number(value) / Math.pow(10, 6);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,13 @@
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [{ "internalType": "address", "name": "_holder", "type": "address" }],
|
||||
"name": "balanceOf",
|
||||
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "decimals",
|
||||
|
||||
Reference in New Issue
Block a user