Merge branch 'spencer/token-balance' into 'master'
Fix token service queries. See merge request grassrootseconomics/cic-staff-client!28
This commit is contained in:
commit
84a50ed814
1469
package-lock.json
generated
1469
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -33,10 +33,10 @@
|
|||||||
"@angular/platform-browser-dynamic": "~10.2.0",
|
"@angular/platform-browser-dynamic": "~10.2.0",
|
||||||
"@angular/router": "~10.2.0",
|
"@angular/router": "~10.2.0",
|
||||||
"@angular/service-worker": "~10.2.0",
|
"@angular/service-worker": "~10.2.0",
|
||||||
|
"@cicnet/cic-client": "^0.1.6",
|
||||||
"@cicnet/schemas-data-validator": "*",
|
"@cicnet/schemas-data-validator": "*",
|
||||||
"@popperjs/core": "^2.5.4",
|
"@popperjs/core": "^2.5.4",
|
||||||
"bootstrap": "^4.5.3",
|
"bootstrap": "^4.5.3",
|
||||||
"cic-client": "0.1.4",
|
|
||||||
"cic-client-meta": "0.0.7-alpha.6",
|
"cic-client-meta": "0.0.7-alpha.6",
|
||||||
"ethers": "^5.0.31",
|
"ethers": "^5.0.31",
|
||||||
"http-server": "^0.12.3",
|
"http-server": "^0.12.3",
|
||||||
|
@ -35,7 +35,7 @@ export class AuthService {
|
|||||||
await this.mutableKeyStore.importPrivateKey(localStorage.getItem(btoa('CICADA_PRIVATE_KEY')));
|
await this.mutableKeyStore.importPrivateKey(localStorage.getItem(btoa('CICADA_PRIVATE_KEY')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getSessionToken(): string {
|
getSessionToken(): string {
|
||||||
return sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN'));
|
return sessionStorage.getItem(btoa('CICADA_SESSION_TOKEN'));
|
||||||
}
|
}
|
||||||
@ -49,84 +49,80 @@ export class AuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getWithToken(): Promise<boolean> {
|
getWithToken(): Promise<boolean> {
|
||||||
const headers = {
|
const headers = {
|
||||||
Authorization: 'Bearer ' + this.getSessionToken,
|
Authorization: 'Bearer ' + this.getSessionToken,
|
||||||
'Content-Type': 'application/json;charset=utf-8',
|
'Content-Type': 'application/json;charset=utf-8',
|
||||||
'x-cic-automerge': 'none',
|
'x-cic-automerge': 'none',
|
||||||
};
|
};
|
||||||
const options = {
|
const options = {
|
||||||
headers,
|
headers,
|
||||||
};
|
};
|
||||||
return fetch(environment.cicMetaUrl, options).then((response) => {
|
return fetch(environment.cicMetaUrl, options).then((response) => {
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
this.loggingService.sendErrorLevelMessage('failed to get with auth token.',
|
this.loggingService.sendErrorLevelMessage('failed to get with auth token.', this, {
|
||||||
this,
|
error: '',
|
||||||
{ error: "" });
|
});
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO rename to send signed challenge and set session. Also separate these responsibilities
|
// TODO rename to send signed challenge and set session. Also separate these responsibilities
|
||||||
sendSignedChallenge(hobaResponseEncoded: any): Promise<any> {
|
sendSignedChallenge(hobaResponseEncoded: any): Promise<any> {
|
||||||
const headers = {
|
const headers = {
|
||||||
Authorization: 'HOBA ' + hobaResponseEncoded,
|
Authorization: 'HOBA ' + hobaResponseEncoded,
|
||||||
'Content-Type': 'application/json;charset=utf-8',
|
'Content-Type': 'application/json;charset=utf-8',
|
||||||
'x-cic-automerge': 'none',
|
'x-cic-automerge': 'none',
|
||||||
};
|
};
|
||||||
const options = {
|
const options = {
|
||||||
headers,
|
headers,
|
||||||
};
|
};
|
||||||
return fetch(environment.cicMetaUrl, options)
|
return fetch(environment.cicMetaUrl, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
getChallenge(): Promise<any> {
|
getChallenge(): Promise<any> {
|
||||||
return fetch(environment.cicMetaUrl)
|
return fetch(environment.cicMetaUrl).then((response) => {
|
||||||
.then(response => {
|
if (response.status === 401) {
|
||||||
if (response.status === 401) {
|
const authHeader: string = response.headers.get('WWW-Authenticate');
|
||||||
const authHeader: string = response.headers.get('WWW-Authenticate');
|
return hobaParseChallengeHeader(authHeader);
|
||||||
return hobaParseChallengeHeader(authHeader);
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async login(): Promise<boolean> {
|
async login(): Promise<boolean> {
|
||||||
if (this.getSessionToken()) {
|
if (this.getSessionToken()) {
|
||||||
sessionStorage.removeItem(btoa('CICADA_SESSION_TOKEN'));
|
sessionStorage.removeItem(btoa('CICADA_SESSION_TOKEN'));
|
||||||
} else {
|
} else {
|
||||||
const o = await this.getChallenge();
|
const o = await this.getChallenge();
|
||||||
|
|
||||||
const r = await signChallenge(
|
const r = await signChallenge(
|
||||||
o.challenge,
|
o.challenge,
|
||||||
o.realm,
|
o.realm,
|
||||||
environment.cicMetaUrl,
|
environment.cicMetaUrl,
|
||||||
this.mutableKeyStore
|
this.mutableKeyStore
|
||||||
);
|
);
|
||||||
|
|
||||||
const tokenResponse = await this.sendSignedChallenge(r)
|
const tokenResponse = await this.sendSignedChallenge(r).then((response) => {
|
||||||
.then(response => {
|
const token = response.headers.get('Token');
|
||||||
const token = response.headers.get('Token')
|
if (token) {
|
||||||
if (token) {
|
return token;
|
||||||
return token
|
|
||||||
}
|
|
||||||
if (response.status === 401) {
|
|
||||||
let e = new HttpError("You are not authorized to use this system", response.status)
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
if (!response.ok) {
|
|
||||||
let e = new HttpError("Unknown error from authentication server", response.status)
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (tokenResponse) {
|
|
||||||
this.setSessionToken(tokenResponse);
|
|
||||||
this.setState('Click button to log in');
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
return false
|
if (response.status === 401) {
|
||||||
|
throw new HttpError('You are not authorized to use this system', response.status);
|
||||||
|
}
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new HttpError('Unknown error from authentication server', response.status);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tokenResponse) {
|
||||||
|
this.setSessionToken(tokenResponse);
|
||||||
|
this.setState('Click button to log in');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Settings } from '@app/_models';
|
import { Settings } from '@app/_models';
|
||||||
import { TransactionHelper } from 'cic-client';
|
import { TransactionHelper } from '@cicnet/cic-client';
|
||||||
import { first } from 'rxjs/operators';
|
import { first } from 'rxjs/operators';
|
||||||
import { TransactionService } from '@app/_services/transaction.service';
|
import { TransactionService } from '@app/_services/transaction.service';
|
||||||
import { environment } from '@src/environments/environment';
|
import { environment } from '@src/environments/environment';
|
||||||
@ -39,10 +39,6 @@ export class BlockSyncService {
|
|||||||
settings.txHelper.onconversion = async (transaction: any): Promise<any> => {
|
settings.txHelper.onconversion = async (transaction: any): Promise<any> => {
|
||||||
window.dispatchEvent(this.newEvent(transaction, 'cic_convert'));
|
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);
|
this.readyStateProcessor(settings, readyStateElements.network, address, offset, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { environment } from '@src/environments/environment';
|
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 { HttpGetter } from '@app/_helpers';
|
||||||
import { Web3Service } from '@app/_services/web3.service';
|
import { Web3Service } from '@app/_services/web3.service';
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { CICRegistry } from 'cic-client';
|
import { CICRegistry } from '@cicnet/cic-client';
|
||||||
import { TokenRegistry } from '@app/_eth';
|
import { TokenRegistry } from '@app/_eth';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { RegistryService } from '@app/_services/registry.service';
|
import { RegistryService } from '@app/_services/registry.service';
|
||||||
import { Token } from '@app/_models';
|
import { Token } from '@app/_models';
|
||||||
import {BehaviorSubject, Observable, Subject} from 'rxjs';
|
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@ -12,21 +12,21 @@ import {BehaviorSubject, Observable, Subject} from 'rxjs';
|
|||||||
export class TokenService {
|
export class TokenService {
|
||||||
registry: CICRegistry;
|
registry: CICRegistry;
|
||||||
tokenRegistry: TokenRegistry;
|
tokenRegistry: TokenRegistry;
|
||||||
onload: (status: boolean) => void;
|
|
||||||
tokens: Array<Token> = [];
|
tokens: Array<Token> = [];
|
||||||
private tokensList: BehaviorSubject<Array<Token>> = new BehaviorSubject<Array<Token>>(this.tokens);
|
private tokensList: BehaviorSubject<Array<Token>> = new BehaviorSubject<Array<Token>>(
|
||||||
|
this.tokens
|
||||||
|
);
|
||||||
tokensSubject: Observable<Array<Token>> = this.tokensList.asObservable();
|
tokensSubject: Observable<Array<Token>> = this.tokensList.asObservable();
|
||||||
|
load: BehaviorSubject<any> = new BehaviorSubject<any>(false);
|
||||||
|
|
||||||
constructor(private httpClient: HttpClient) {}
|
constructor(private httpClient: HttpClient) {}
|
||||||
|
|
||||||
async init(): Promise<void> {
|
async init(): Promise<void> {
|
||||||
this.registry = await RegistryService.getRegistry();
|
this.registry = await RegistryService.getRegistry();
|
||||||
this.registry.onload = async (address: string): Promise<void> => {
|
this.tokenRegistry = new TokenRegistry(
|
||||||
this.tokenRegistry = new TokenRegistry(
|
await this.registry.getContractAddressByName('TokenRegistry')
|
||||||
await this.registry.getContractAddressByName('TokenRegistry')
|
);
|
||||||
);
|
this.load.next(true);
|
||||||
this.onload(this.tokenRegistry !== undefined);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addToken(token: Token): void {
|
addToken(token: Token): void {
|
||||||
@ -71,7 +71,17 @@ export class TokenService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getTokenBalance(address: string): Promise<(address: string) => Promise<number>> {
|
async getTokenBalance(address: string): Promise<(address: string) => Promise<number>> {
|
||||||
const sarafuToken = await this.registry.addToken(await this.tokenRegistry.entry(0));
|
const token = await this.registry.addToken(await this.tokenRegistry.entry(0));
|
||||||
return await sarafuToken.methods.balanceOf(address).call();
|
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 { defaultAccount } from '@app/_models';
|
||||||
import { LoggingService } from '@app/_services/logging.service';
|
import { LoggingService } from '@app/_services/logging.service';
|
||||||
import { HttpClient } from '@angular/common/http';
|
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 { RegistryService } from '@app/_services/registry.service';
|
||||||
import Web3 from 'web3';
|
import Web3 from 'web3';
|
||||||
import { Web3Service } from '@app/_services/web3.service';
|
import { Web3Service } from '@app/_services/web3.service';
|
||||||
@ -148,43 +148,41 @@ export class TransactionService {
|
|||||||
recipientAddress: string,
|
recipientAddress: string,
|
||||||
value: number
|
value: number
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
this.registry.onload = async (addressReturned: string): Promise<void> => {
|
const transferAuthAddress = await this.registry.getContractAddressByName(
|
||||||
const transferAuthAddress = await this.registry.getContractAddressByName(
|
'TransferAuthorization'
|
||||||
'TransferAuthorization'
|
);
|
||||||
);
|
const hashFunction = new Keccak(256);
|
||||||
const hashFunction = new Keccak(256);
|
hashFunction.update('createRequest(address,address,address,uint256)');
|
||||||
hashFunction.update('createRequest(address,address,address,uint256)');
|
const hash = hashFunction.digest();
|
||||||
const hash = hashFunction.digest();
|
const methodSignature = hash.toString('hex').substring(0, 8);
|
||||||
const methodSignature = hash.toString('hex').substring(0, 8);
|
const abiCoder = new utils.AbiCoder();
|
||||||
const abiCoder = new utils.AbiCoder();
|
const abi = await abiCoder.encode(
|
||||||
const abi = await abiCoder.encode(
|
['address', 'address', 'address', 'uint256'],
|
||||||
['address', 'address', 'address', 'uint256'],
|
[senderAddress, recipientAddress, tokenAddress, value]
|
||||||
[senderAddress, recipientAddress, tokenAddress, value]
|
);
|
||||||
);
|
const data = fromHex(methodSignature + strip0x(abi));
|
||||||
const data = fromHex(methodSignature + strip0x(abi));
|
const tx = new Tx(environment.bloxbergChainId);
|
||||||
const tx = new Tx(environment.bloxbergChainId);
|
tx.nonce = await this.web3.eth.getTransactionCount(senderAddress);
|
||||||
tx.nonce = await this.web3.eth.getTransactionCount(senderAddress);
|
tx.gasPrice = Number(await this.web3.eth.getGasPrice());
|
||||||
tx.gasPrice = Number(await this.web3.eth.getGasPrice());
|
tx.gasLimit = 8000000;
|
||||||
tx.gasLimit = 8000000;
|
tx.to = fromHex(strip0x(transferAuthAddress));
|
||||||
tx.to = fromHex(strip0x(transferAuthAddress));
|
tx.value = toValue(value);
|
||||||
tx.value = toValue(value);
|
tx.data = data;
|
||||||
tx.data = data;
|
const txMsg = tx.message();
|
||||||
const txMsg = tx.message();
|
const privateKey = this.authService.mutableKeyStore.getPrivateKey();
|
||||||
const privateKey = this.authService.mutableKeyStore.getPrivateKey();
|
if (!privateKey.isDecrypted()) {
|
||||||
if (!privateKey.isDecrypted()) {
|
const password = window.prompt('password');
|
||||||
const password = window.prompt('password');
|
await privateKey.decrypt(password);
|
||||||
await privateKey.decrypt(password);
|
}
|
||||||
}
|
const signatureObject = secp256k1.ecdsaSign(txMsg, privateKey.keyPacket.privateParams.d);
|
||||||
const signatureObject = secp256k1.ecdsaSign(txMsg, privateKey.keyPacket.privateParams.d);
|
const r = signatureObject.signature.slice(0, 32);
|
||||||
const r = signatureObject.signature.slice(0, 32);
|
const s = signatureObject.signature.slice(32);
|
||||||
const s = signatureObject.signature.slice(32);
|
const v = signatureObject.recid;
|
||||||
const v = signatureObject.recid;
|
tx.setSignature(r, s, v);
|
||||||
tx.setSignature(r, s, v);
|
const txWire = add0x(toHex(tx.serializeRLP()));
|
||||||
const txWire = add0x(toHex(tx.serializeRLP()));
|
const result = await this.web3.eth.sendSignedTransaction(txWire);
|
||||||
const result = await this.web3.eth.sendSignedTransaction(txWire);
|
this.loggingService.sendInfoLevelMessage(`Result: ${result}`);
|
||||||
this.loggingService.sendInfoLevelMessage(`Result: ${result}`);
|
const transaction = await this.web3.eth.getTransaction(result.transactionHash);
|
||||||
const transaction = await this.web3.eth.getTransaction(result.transactionHash);
|
this.loggingService.sendInfoLevelMessage(`Transaction: ${transaction}`);
|
||||||
this.loggingService.sendInfoLevelMessage(`Transaction: ${transaction}`);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import { TokenService } from '@app/_services/token.service';
|
|||||||
import { AccountIndex } from '@app/_eth';
|
import { AccountIndex } from '@app/_eth';
|
||||||
import { MutableKeyStore, PGPSigner, Signer } from '@app/_pgp';
|
import { MutableKeyStore, PGPSigner, Signer } from '@app/_pgp';
|
||||||
import { RegistryService } from '@app/_services/registry.service';
|
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 { AuthService } from '@app/_services/auth.service';
|
||||||
import { personValidation, vcardValidation } from '@app/_helpers';
|
import { personValidation, vcardValidation } from '@app/_helpers';
|
||||||
import { add0x } from '@src/assets/js/ethtx/dist/hex';
|
import { add0x } from '@src/assets/js/ethtx/dist/hex';
|
||||||
@ -201,11 +201,13 @@ export class UserService {
|
|||||||
const account: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
|
const account: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
|
||||||
const accountInfo = account.m.data;
|
const accountInfo = account.m.data;
|
||||||
await personValidation(accountInfo);
|
await personValidation(accountInfo);
|
||||||
this.tokenService.onload = async (status: boolean): Promise<void> => {
|
this.tokenService.load.subscribe(async (status: boolean) => {
|
||||||
accountInfo.balance = await this.tokenService.getTokenBalance(
|
if (status) {
|
||||||
accountInfo.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0]
|
accountInfo.balance = await this.tokenService.getTokenBalance(
|
||||||
);
|
accountInfo.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0]
|
||||||
};
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
accountInfo.vcard = vCard.parse(atob(accountInfo.vcard));
|
accountInfo.vcard = vCard.parse(atob(accountInfo.vcard));
|
||||||
await vcardValidation(accountInfo.vcard);
|
await vcardValidation(accountInfo.vcard);
|
||||||
this.addAccount(accountInfo, limit);
|
this.addAccount(accountInfo, limit);
|
||||||
|
@ -22,7 +22,7 @@ export class AuthComponent implements OnInit {
|
|||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private errorDialogService: ErrorDialogService,
|
private errorDialogService: ErrorDialogService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
@ -30,7 +30,7 @@ export class AuthComponent implements OnInit {
|
|||||||
key: ['', Validators.required],
|
key: ['', Validators.required],
|
||||||
});
|
});
|
||||||
if (this.authService.getPrivateKey()) {
|
if (this.authService.getPrivateKey()) {
|
||||||
this.authService.loginView();
|
this.authService.loginView();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,10 +52,10 @@ export class AuthComponent implements OnInit {
|
|||||||
|
|
||||||
async login(): Promise<void> {
|
async login(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const loginResult = await this.authService.login()
|
const loginResult = await this.authService.login();
|
||||||
if (loginResult) {
|
if (loginResult) {
|
||||||
this.router.navigate(['/home']);
|
this.router.navigate(['/home']);
|
||||||
}
|
}
|
||||||
} catch (HttpError) {
|
} catch (HttpError) {
|
||||||
this.errorDialogService.openDialog({
|
this.errorDialogService.openDialog({
|
||||||
message: HttpError.message,
|
message: HttpError.message,
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
<h3>
|
<h3>
|
||||||
<strong> {{account?.vcard?.fn[0].value}} </strong>
|
<strong> {{account?.vcard?.fn[0].value}} </strong>
|
||||||
</h3>
|
</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>Created:</strong> {{account?.date_registered | unixDate}}</span>
|
||||||
<span class="ml-2"><strong>Address:</strong>
|
<span class="ml-2"><strong>Address:</strong>
|
||||||
<a href="{{bloxbergLink}}" target="_blank"> {{accountAddress}} </a>
|
<a href="{{bloxbergLink}}" target="_blank"> {{accountAddress}} </a>
|
||||||
@ -218,7 +218,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{account?.vcard?.fn[0].value}}</td>
|
<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>{{account?.date_registered | unixDate}}</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="badge badge-success badge-pill">
|
<span class="badge badge-success badge-pill">
|
||||||
@ -274,8 +274,8 @@
|
|||||||
<ng-container matColumnDef="value">
|
<ng-container matColumnDef="value">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Value </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Value </th>
|
||||||
<td mat-cell *matCellDef="let transaction">
|
<td mat-cell *matCellDef="let transaction">
|
||||||
<span *ngIf="transaction.type == 'transaction'">{{transaction?.value | tokenRatio}}</span>
|
<span *ngIf="transaction.type == 'transaction'">{{transaction?.value | tokenRatio}} {{ tokenSymbol | uppercase }}</span>
|
||||||
<span *ngIf="transaction.type == 'conversion'">{{transaction?.toValue | tokenRatio}}</span>
|
<span *ngIf="transaction.type == 'conversion'">{{transaction?.toValue | tokenRatio}} {{ tokenSymbol | uppercase }}</span>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
@ -348,7 +348,7 @@
|
|||||||
|
|
||||||
<ng-container matColumnDef="balance">
|
<ng-container matColumnDef="balance">
|
||||||
<mat-header-cell *matHeaderCellDef mat-sort-header> BALANCE </mat-header-cell>
|
<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>
|
||||||
|
|
||||||
<ng-container matColumnDef="location">
|
<ng-container matColumnDef="location">
|
||||||
|
@ -64,6 +64,7 @@ export class AccountDetailsComponent implements OnInit {
|
|||||||
matcher: CustomErrorStateMatcher = new CustomErrorStateMatcher();
|
matcher: CustomErrorStateMatcher = new CustomErrorStateMatcher();
|
||||||
submitted: boolean = false;
|
submitted: boolean = false;
|
||||||
bloxbergLink: string;
|
bloxbergLink: string;
|
||||||
|
tokenSymbol: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
@ -113,7 +114,7 @@ export class AccountDetailsComponent implements OnInit {
|
|||||||
this.loggingService.sendInfoLevelMessage(this.account);
|
this.loggingService.sendInfoLevelMessage(this.account);
|
||||||
const fullName = this.account.vcard?.fn[0].value.split(' ');
|
const fullName = this.account.vcard?.fn[0].value.split(' ');
|
||||||
this.accountInfoForm.patchValue({
|
this.accountInfoForm.patchValue({
|
||||||
firstName: fullName[0],
|
firstName: fullName[0].split(',')[0],
|
||||||
lastName: fullName.slice(1).join(' '),
|
lastName: fullName.slice(1).join(' '),
|
||||||
phoneNumber: this.account.vcard?.tel[0].value,
|
phoneNumber: this.account.vcard?.tel[0].value,
|
||||||
age: this.account.age,
|
age: this.account.age,
|
||||||
@ -189,6 +190,11 @@ export class AccountDetailsComponent implements OnInit {
|
|||||||
.getGenders()
|
.getGenders()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((res) => (this.genders = res));
|
.subscribe((res) => (this.genders = res));
|
||||||
|
this.tokenService.load.subscribe(async (status: boolean) => {
|
||||||
|
if (status) {
|
||||||
|
this.tokenSymbol = await this.tokenService.getTokenSymbol();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
doTransactionFilter(value: string): void {
|
doTransactionFilter(value: string): void {
|
||||||
@ -220,7 +226,7 @@ export class AccountDetailsComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
const accountKey = await this.userService.changeAccountInfo(
|
const accountKey = await this.userService.changeAccountInfo(
|
||||||
this.accountAddress,
|
this.accountAddress,
|
||||||
this.accountInfoFormStub.firstName.value + ' ' + this.accountInfoFormStub.lastName.value,
|
this.accountInfoFormStub.firstName.value + ', ' + this.accountInfoFormStub.lastName.value,
|
||||||
this.accountInfoFormStub.phoneNumber.value,
|
this.accountInfoFormStub.phoneNumber.value,
|
||||||
this.accountInfoFormStub.age.value,
|
this.accountInfoFormStub.age.value,
|
||||||
this.accountInfoFormStub.type.value,
|
this.accountInfoFormStub.type.value,
|
||||||
|
@ -29,9 +29,11 @@ export class TokensComponent implements OnInit {
|
|||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
await this.tokenService.init();
|
await this.tokenService.init();
|
||||||
this.tokenService.onload = async (status: boolean): Promise<void> => {
|
this.tokenService.load.subscribe(async (status: boolean) => {
|
||||||
await this.tokenService.getTokens();
|
if (status) {
|
||||||
};
|
await this.tokenService.getTokens();
|
||||||
|
}
|
||||||
|
});
|
||||||
this.tokenService.tokensSubject.subscribe((tokens) => {
|
this.tokenService.tokensSubject.subscribe((tokens) => {
|
||||||
this.loggingService.sendInfoLevelMessage(tokens);
|
this.loggingService.sendInfoLevelMessage(tokens);
|
||||||
this.dataSource = new MatTableDataSource(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>
|
<button mat-raised-button color="primary" class="btn btn-outline-info" (click)="viewRecipient()">View Recipient</button>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<span>Amount: SRF {{transaction.value | tokenRatio}}</span>
|
<span>Amount: {{transaction.value | tokenRatio}} {{ tokenSymbol | uppercase }}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<h4 class="mt-2">Token: </h4>
|
<h4 class="mt-2">Token: </h4>
|
||||||
@ -43,10 +43,10 @@
|
|||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<span>Name: Sarafu Token</span>
|
<span>Name: {{ tokenName }}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<span>Symbol: SRF</span>
|
<span>Symbol: {{ tokenSymbol | uppercase }}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -109,10 +109,10 @@
|
|||||||
<span>Name: {{transaction.sourceToken.name}}</span>
|
<span>Name: {{transaction.sourceToken.name}}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<span>Symbol: {{transaction.sourceToken.symbol}}</span>
|
<span>Symbol: {{transaction.sourceToken.symbol | uppercase}}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<span>Amount: {{transaction.sourceToken.symbol + ' ' + transaction.fromValue}}</span>
|
<span>Amount: {{transaction.fromValue}} {{transaction.sourceToken.symbol | uppercase}}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -130,10 +130,10 @@
|
|||||||
<span>Name: {{transaction.destinationToken.name}}</span>
|
<span>Name: {{transaction.destinationToken.name}}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<span>Symbol: {{transaction.destinationToken.symbol}}</span>
|
<span>Symbol: {{transaction.destinationToken.symbol | uppercase}}</span>
|
||||||
</li>
|
</li>
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<span>Amount: {{transaction.destinationToken.symbol + ' ' + transaction.toValue}}</span>
|
<span>Amount: {{transaction.toValue}} {{transaction.destinationToken.symbol | uppercase}}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,7 +7,7 @@ import {
|
|||||||
Output,
|
Output,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { TransactionService } from '@app/_services';
|
import { TokenService, TransactionService } from '@app/_services';
|
||||||
import { copyToClipboard } from '@app/_helpers';
|
import { copyToClipboard } from '@app/_helpers';
|
||||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
import { strip0x } from '@src/assets/js/ethtx/dist/hex';
|
import { strip0x } from '@src/assets/js/ethtx/dist/hex';
|
||||||
@ -26,15 +26,19 @@ export class TransactionDetailsComponent implements OnInit {
|
|||||||
senderBloxbergLink: string;
|
senderBloxbergLink: string;
|
||||||
recipientBloxbergLink: string;
|
recipientBloxbergLink: string;
|
||||||
traderBloxbergLink: string;
|
traderBloxbergLink: string;
|
||||||
|
tokenName: string;
|
||||||
|
tokenSymbol: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private transactionService: TransactionService,
|
private transactionService: TransactionService,
|
||||||
private snackBar: MatSnackBar
|
private snackBar: MatSnackBar,
|
||||||
|
private tokenService: TokenService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
await this.transactionService.init();
|
await this.transactionService.init();
|
||||||
|
await this.tokenService.init();
|
||||||
if (this.transaction?.type === 'conversion') {
|
if (this.transaction?.type === 'conversion') {
|
||||||
this.traderBloxbergLink =
|
this.traderBloxbergLink =
|
||||||
'https://blockexplorer.bloxberg.org/address/' + this.transaction?.trader + '/transactions';
|
'https://blockexplorer.bloxberg.org/address/' + this.transaction?.trader + '/transactions';
|
||||||
@ -44,6 +48,12 @@ export class TransactionDetailsComponent implements OnInit {
|
|||||||
this.recipientBloxbergLink =
|
this.recipientBloxbergLink =
|
||||||
'https://blockexplorer.bloxberg.org/address/' + this.transaction?.to + '/transactions';
|
'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> {
|
async viewSender(): Promise<void> {
|
||||||
|
@ -59,8 +59,8 @@
|
|||||||
<ng-container matColumnDef="value">
|
<ng-container matColumnDef="value">
|
||||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Value </th>
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Value </th>
|
||||||
<td mat-cell *matCellDef="let transaction">
|
<td mat-cell *matCellDef="let transaction">
|
||||||
<span *ngIf="transaction.type == 'transaction'">{{transaction?.value | tokenRatio}}</span>
|
<span *ngIf="transaction.type == 'transaction'">{{transaction?.value | tokenRatio}} {{ tokenSymbol | uppercase }}</span>
|
||||||
<span *ngIf="transaction.type == 'conversion'">{{transaction?.toValue | tokenRatio}}</span>
|
<span *ngIf="transaction.type == 'conversion'">{{transaction?.toValue | tokenRatio}} {{ tokenSymbol | uppercase }}</span>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
OnInit,
|
OnInit,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
} from '@angular/core';
|
} 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 { MatTableDataSource } from '@angular/material/table';
|
||||||
import { MatPaginator } from '@angular/material/paginator';
|
import { MatPaginator } from '@angular/material/paginator';
|
||||||
import { MatSort } from '@angular/material/sort';
|
import { MatSort } from '@angular/material/sort';
|
||||||
@ -28,6 +28,7 @@ export class TransactionsComponent implements OnInit, AfterViewInit {
|
|||||||
transaction: Transaction;
|
transaction: Transaction;
|
||||||
transactionsType: string = 'all';
|
transactionsType: string = 'all';
|
||||||
transactionsTypes: Array<string>;
|
transactionsTypes: Array<string>;
|
||||||
|
tokenSymbol: string;
|
||||||
|
|
||||||
@ViewChild(MatPaginator) paginator: MatPaginator;
|
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||||
@ViewChild(MatSort) sort: MatSort;
|
@ViewChild(MatSort) sort: MatSort;
|
||||||
@ -35,7 +36,8 @@ export class TransactionsComponent implements OnInit, AfterViewInit {
|
|||||||
constructor(
|
constructor(
|
||||||
private blockSyncService: BlockSyncService,
|
private blockSyncService: BlockSyncService,
|
||||||
private transactionService: TransactionService,
|
private transactionService: TransactionService,
|
||||||
private userService: UserService
|
private userService: UserService,
|
||||||
|
private tokenService: TokenService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
@ -46,6 +48,7 @@ export class TransactionsComponent implements OnInit, AfterViewInit {
|
|||||||
this.transactions = transactions;
|
this.transactions = transactions;
|
||||||
});
|
});
|
||||||
await this.blockSyncService.init();
|
await this.blockSyncService.init();
|
||||||
|
await this.tokenService.init();
|
||||||
await this.transactionService.init();
|
await this.transactionService.init();
|
||||||
await this.userService.init();
|
await this.userService.init();
|
||||||
await this.blockSyncService.blockSync();
|
await this.blockSyncService.blockSync();
|
||||||
@ -53,6 +56,11 @@ export class TransactionsComponent implements OnInit, AfterViewInit {
|
|||||||
.getTransactionTypes()
|
.getTransactionTypes()
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe((res) => (this.transactionsTypes = res));
|
.subscribe((res) => (this.transactionsTypes = res));
|
||||||
|
this.tokenService.load.subscribe(async (status: boolean) => {
|
||||||
|
if (status) {
|
||||||
|
this.tokenSymbol = await this.tokenService.getTokenSymbol();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
viewTransaction(transaction): void {
|
viewTransaction(transaction): void {
|
||||||
|
@ -2,7 +2,7 @@ import { Pipe, PipeTransform } from '@angular/core';
|
|||||||
|
|
||||||
@Pipe({ name: 'tokenRatio' })
|
@Pipe({ name: 'tokenRatio' })
|
||||||
export class TokenRatioPipe implements PipeTransform {
|
export class TokenRatioPipe implements PipeTransform {
|
||||||
transform(value: any, ...args): any {
|
transform(value: any = 0, ...args): any {
|
||||||
return Number(value) / Math.pow(10, 6);
|
return Number(value) / Math.pow(10, 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,13 @@
|
|||||||
"stateMutability": "nonpayable",
|
"stateMutability": "nonpayable",
|
||||||
"type": "function"
|
"type": "function"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"inputs": [{ "internalType": "address", "name": "_holder", "type": "address" }],
|
||||||
|
"name": "balanceOf",
|
||||||
|
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"inputs": [],
|
"inputs": [],
|
||||||
"name": "decimals",
|
"name": "decimals",
|
||||||
|
Loading…
Reference in New Issue
Block a user