diff --git a/src/app/_helpers/accountIndex.ts b/src/app/_helpers/accountIndex.ts index 72df009..0000757 100644 --- a/src/app/_helpers/accountIndex.ts +++ b/src/app/_helpers/accountIndex.ts @@ -22,7 +22,7 @@ export class AccountIndex { } public async totalAccounts(): Promise { - return this.contract.methods.count().call(); + return await this.contract.methods.count().call(); } public async haveAccount(address: string): Promise { diff --git a/src/app/_helpers/index.ts b/src/app/_helpers/index.ts index 6339460..42ab912 100644 --- a/src/app/_helpers/index.ts +++ b/src/app/_helpers/index.ts @@ -8,3 +8,4 @@ export * from '@app/_helpers/accountIndex'; export * from '@app/_helpers/http-getter'; export * from '@app/_helpers/pgp-signer'; export * from '@app/_helpers/registry'; +export * from '@app/_helpers/token-registry'; diff --git a/src/app/_helpers/registry.spec.ts b/src/app/_helpers/registry.spec.ts index e75820c..79ec68a 100644 --- a/src/app/_helpers/registry.spec.ts +++ b/src/app/_helpers/registry.spec.ts @@ -1,7 +1,8 @@ -import { Registry } from './registry'; +import { Registry } from '@app/_helpers/registry'; +import {environment} from '@src/environments/environment'; describe('Registry', () => { it('should create an instance', () => { - expect(new Registry()).toBeTruthy(); + expect(new Registry(environment.registryAddress)).toBeTruthy(); }); }); diff --git a/src/app/_helpers/token-registry.spec.ts b/src/app/_helpers/token-registry.spec.ts new file mode 100644 index 0000000..bc66a23 --- /dev/null +++ b/src/app/_helpers/token-registry.spec.ts @@ -0,0 +1,8 @@ +import { TokenRegistry } from '@app/_helpers/token-registry'; +import {environment} from '@src/environments/environment'; + +describe('TokenRegistry', () => { + it('should create an instance', () => { + expect(new TokenRegistry(environment.registryAddress)).toBeTruthy(); + }); +}); diff --git a/src/app/_helpers/token-registry.ts b/src/app/_helpers/token-registry.ts new file mode 100644 index 0000000..7b1807e --- /dev/null +++ b/src/app/_helpers/token-registry.ts @@ -0,0 +1,36 @@ +// @ts-ignore +import * as registryClient from '@src/assets/js/block-sync/data/RegistryClient.json'; +import Web3 from 'web3'; +import {environment} from '@src/environments/environment'; + +const web3 = new Web3(environment.web3Provider); +const abi = registryClient.default; + +export class TokenRegistry { + contractAddress: string; + signerAddress: string; + contract: any; + + constructor(contractAddress: string, signerAddress?: string) { + this.contractAddress = contractAddress; + this.contract = new web3.eth.Contract(abi, this.contractAddress); + if (signerAddress) { + this.signerAddress = signerAddress; + } else { + this.signerAddress = web3.eth.accounts[0]; + } + } + + public async totalTokens(): Promise { + return await this.contract.methods.registryCount().call(); + } + + public async entry(serial: number): Promise { + return await this.contract.methods.entry(serial).call(); + } + + public async addressOf(identifier: string): Promise { + const id = '0x' + web3.utils.padRight(new Buffer(identifier).toString('hex'), 64); + return await this.contract.methods.addressOf(id).call(); + } +} diff --git a/src/app/_models/account.ts b/src/app/_models/account.ts index 7e6eb06..feacdf4 100644 --- a/src/app/_models/account.ts +++ b/src/app/_models/account.ts @@ -52,3 +52,40 @@ export interface MetaResponse { id: string; m: Meta; } + +export const defaultAccount: AccountDetails = { + date_registered: Date.now(), + gender: 'other', + identities: { + evm: { + 'bloxberg:8996': [''], + 'oldchain:1': [''], + }, + latitude: 0, + longitude: 0, + }, + location: { + area_name: 'Kilifi', + }, + products: [], + vcard: { + email: [{ + value: '', + }], + fn: [{ + value: 'GE', + }], + n: [{ + value: ['GE'], + }], + tel: [{ + meta: { + TYP: [], + }, + value: '', + }], + version: [{ + value: '3.0', + }], + }, +}; diff --git a/src/app/_services/token.service.ts b/src/app/_services/token.service.ts index 781d70c..014e4a3 100644 --- a/src/app/_services/token.service.ts +++ b/src/app/_services/token.service.ts @@ -3,11 +3,18 @@ import {HttpClient} from '@angular/common/http'; import {environment} from '@src/environments/environment'; import {first} from 'rxjs/operators'; import {BehaviorSubject} from 'rxjs'; +import {HttpGetter, Registry, TokenRegistry} from '@app/_helpers'; +import {CICRegistry} from 'cic-client'; +import Web3 from 'web3'; @Injectable({ providedIn: 'root' }) export class TokenService { + web3 = new Web3(environment.web3Provider); + fileGetter = new HttpGetter(); + registry = new Registry(environment.registryAddress); + cicRegistry = new CICRegistry(this.web3, environment.registryAddress, this.fileGetter, ['../../assets/js/block-sync/data']); tokens: any = ''; private tokensList = new BehaviorSubject(this.tokens); tokensSubject = this.tokensList.asObservable(); @@ -16,11 +23,19 @@ export class TokenService { private http: HttpClient ) { } - getTokens(): any { - this.http.get(`${environment.cicCacheUrl}/tokens`).pipe(first()).subscribe(tokens => this.tokensList.next(tokens)); + async getTokens(): Promise { + const tokenRegistryQuery = new TokenRegistry(await this.registry.addressOf('TokenRegistry')); + const count = await tokenRegistryQuery.totalTokens(); + return Array.from({length: count}, async (v, i) => await tokenRegistryQuery.entry(i)); } getTokenBySymbol(symbol: string): any { return this.http.get(`${environment.cicCacheUrl}/tokens/${symbol}`); } + + async getTokenBalance(address: string): Promise { + const tokenRegistryQuery = new TokenRegistry(await this.registry.addressOf('TokenRegistry')); + const sarafuToken = await this.cicRegistry.addToken(await tokenRegistryQuery.entry(0)); + return await sarafuToken.methods.balanceOf(address).call(); + } } diff --git a/src/app/_services/transaction.service.ts b/src/app/_services/transaction.service.ts index 39f62e9..9d06493 100644 --- a/src/app/_services/transaction.service.ts +++ b/src/app/_services/transaction.service.ts @@ -13,6 +13,7 @@ import {toValue} from '@src/assets/js/ethtx/dist/tx'; import * as secp256k1 from 'secp256k1'; import {AuthService} from '@app/_services/auth.service'; import {Registry} from '@app/_helpers'; +import {defaultAccount} from '@app/_models'; const Web3 = require('web3'); const vCard = require('vcard-parser'); @@ -48,20 +49,39 @@ export class TransactionService { } async setTransaction(transaction, cacheSize: number): Promise { - const cachedTransaction = this.transactions.find(cachedTx => cachedTx.tx.txHash === transaction.tx.txHash); - if (cachedTransaction) { return; } + transaction.value = Number(transaction.value); transaction.type = 'transaction'; - transaction.sender = await this.getUser(transaction.from); - transaction.recipient = await this.getUser(transaction.to); - await this.addTransaction(transaction, cacheSize); + if (this.transactions.find(cachedTx => cachedTx.tx.txHash === transaction.tx.txHash)) { return; } + try { + this.userService.getAccountDetailsFromMeta(await User.toKey(transaction.from)).pipe(first()).subscribe(async (res) => { + transaction.sender = this.getAccountInfo(res); + }, error => { + transaction.sender = defaultAccount; + }); + this.userService.getAccountDetailsFromMeta(await User.toKey(transaction.to)).pipe(first()).subscribe(async (res) => { + transaction.recipient = this.getAccountInfo(res); + }, error => { + transaction.recipient = defaultAccount; + }); + } finally { + await this.addTransaction(transaction, cacheSize); + } } async setConversion(conversion, cacheSize): Promise { - const cachedConversion = this.transactions.find(cachedTx => cachedTx.tx.txHash === conversion.tx.txHash); - if (cachedConversion) { return; } + if (this.transactions.find(cachedTx => cachedTx.tx.txHash === conversion.tx.txHash)) { return; } conversion.type = 'conversion'; - conversion.sender = conversion.recipient = await this.getUser(conversion.trader); - await this.addTransaction(conversion, cacheSize); + conversion.fromValue = Number(conversion.fromValue); + conversion.toValue = Number(conversion.toValue); + try { + this.userService.getAccountDetailsFromMeta(await User.toKey(conversion.trader)).pipe(first()).subscribe(async (res) => { + conversion.sender = conversion.recipient = this.getAccountInfo(res); + }, error => { + conversion.sender = conversion.recipient = defaultAccount; + }); + } finally { + await this.addTransaction(conversion, cacheSize); + } } async addTransaction(transaction, cacheSize: number): Promise { @@ -72,14 +92,10 @@ export class TransactionService { this.transactionList.next(this.transactions); } - async getUser(address: string): Promise { - return this.userService.getAccountDetailsFromMeta(await User.toKey(address)).pipe(first()).subscribe(res => { - const account = Envelope.fromJSON(JSON.stringify(res)).unwrap(); - const accountInfo = account.m.data; - accountInfo.vcard = vCard.parse(atob(accountInfo.vcard)); - this.userInfo = accountInfo; - return accountInfo; - }); + getAccountInfo(account: string): any { + let accountInfo = Envelope.fromJSON(JSON.stringify(account)).unwrap().m.data; + accountInfo.vcard = vCard.parse(atob(accountInfo.vcard)); + return accountInfo; } async transferRequest(tokenAddress: string, senderAddress: string, recipientAddress: string, value: number): Promise { diff --git a/src/app/_services/user.service.ts b/src/app/_services/user.service.ts index f786db8..1048347 100644 --- a/src/app/_services/user.service.ts +++ b/src/app/_services/user.service.ts @@ -134,7 +134,7 @@ export class UserService { } getUser(userKey: string): any { - this.http.get(`${environment.cicMetaUrl}/${userKey}`, { headers: this.headers }).pipe(first()).subscribe(async res => { + return this.http.get(`${environment.cicMetaUrl}/${userKey}`, { headers: this.headers }).pipe(first()).subscribe(async res => { return Envelope.fromJSON(JSON.stringify(res)).unwrap(); }); } @@ -154,19 +154,27 @@ export class UserService { } async loadAccounts(limit: number, offset: number = 0): Promise { + this.resetAccountsList(); const accountIndexAddress = await this.registry.addressOf('AccountRegistry'); const accountIndexQuery = new AccountIndex(accountIndexAddress); const accountAddresses = await accountIndexQuery.last(await accountIndexQuery.totalAccounts()); - console.log(accountAddresses); for (const accountAddress of accountAddresses.slice(offset, offset + limit)) { this.getAccountDetailsFromMeta(await User.toKey(accountAddress)).pipe(first()).subscribe(res => { const account = Envelope.fromJSON(JSON.stringify(res)).unwrap(); this.accountsMeta.push(account); const accountInfo = account.m.data; accountInfo.vcard = vCard.parse(atob(accountInfo.vcard)); - this.accounts.push(accountInfo); + this.accounts.unshift(accountInfo); + if (this.accounts.length > limit) { + this.accounts.length = limit; + } this.accountsList.next(this.accounts); }); } } + + resetAccountsList(): void { + this.accounts = []; + this.accountsList.next(this.accounts); + } } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 0e797c9..1620b02 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,5 +1,5 @@ import {Component, HostListener} from '@angular/core'; -import {TransactionService} from '@app/_services'; +import {TokenService, TransactionService} from '@app/_services'; import {AuthService} from '@app/_services'; @Component({ @@ -15,9 +15,11 @@ export class AppComponent { constructor( private authService: AuthService, + private tokenService: TokenService, private transactionService: TransactionService, ) { this.authService.mutableKeyStore.loadKeyring().then(r => this.authService.getPublicKeys().then()); + this.tokenService.getTokens().then(async r => console.log('Tokens:', await r)); this.mediaQuery.addListener(this.onResize); this.onResize(this.mediaQuery); } diff --git a/src/app/pages/accounts/account-details/account-details.component.html b/src/app/pages/accounts/account-details/account-details.component.html index d210d4e..a6299ea 100644 --- a/src/app/pages/accounts/account-details/account-details.component.html +++ b/src/app/pages/accounts/account-details/account-details.component.html @@ -17,18 +17,6 @@ -
-
-

- {{account?.fn[0].value}} -

- Balance: {{account?.balance}} RCU - Created: {{account?.date_registered | date}} - -
-
- -
Loading... @@ -41,6 +29,18 @@
+
+
+

+ {{account?.vcard?.fn[0].value}} +

+ Balance: {{accountBalance}} RCU + Created: {{account?.date_registered | date}} + Address: {{account?.identities.evm['bloxberg:8996']}} +
+
+ +
DETAILS diff --git a/src/app/pages/accounts/account-details/account-details.component.ts b/src/app/pages/accounts/account-details/account-details.component.ts index c15093d..3cd5696 100644 --- a/src/app/pages/accounts/account-details/account-details.component.ts +++ b/src/app/pages/accounts/account-details/account-details.component.ts @@ -3,7 +3,7 @@ import {MatTableDataSource} from '@angular/material/table'; import {SelectionModel} from '@angular/cdk/collections'; import {MatPaginator} from '@angular/material/paginator'; import {MatSort} from '@angular/material/sort'; -import {LocationService, TransactionService, UserService} from '@app/_services'; +import {LocationService, TokenService, TransactionService, UserService} from '@app/_services'; import {ActivatedRoute, Params, Router} from '@angular/router'; import {first} from 'rxjs/operators'; import {FormBuilder, FormGroup, Validators} from '@angular/forms'; @@ -34,6 +34,7 @@ export class AccountDetailsComponent implements OnInit { accountInfoForm: FormGroup; account: any; + accountBalance: number; metaAccount: any; accounts: any[] = []; accountsType = 'all'; @@ -57,7 +58,8 @@ export class AccountDetailsComponent implements OnInit { private transactionService: TransactionService, private userService: UserService, private route: ActivatedRoute, - private router: Router + private router: Router, + private tokenService: TokenService ) { this.accountInfoForm = this.formBuilder.group({ status: ['', Validators.required], @@ -79,6 +81,7 @@ export class AccountDetailsComponent implements OnInit { this.userService.getAccountDetailsFromMeta(await User.toKey(params.get('id'))).pipe(first()).subscribe(res => { this.metaAccount = Envelope.fromJSON(JSON.stringify(res)).unwrap(); this.account = this.metaAccount.m.data; + this.tokenService.getTokenBalance(this.account.identities.evm['bloxberg:8996'][0]).then(r => this.accountBalance = r); this.account.vcard = vCard.parse(atob(this.account.vcard)); console.log(this.account); this.accountInfoForm.patchValue({ diff --git a/src/app/pages/transactions/transaction-details/transaction-details.component.html b/src/app/pages/transactions/transaction-details/transaction-details.component.html index 65d6822..2b0c2b7 100644 --- a/src/app/pages/transactions/transaction-details/transaction-details.component.html +++ b/src/app/pages/transactions/transaction-details/transaction-details.component.html @@ -14,27 +14,29 @@
  • Sender: {{transaction.sender?.vcard.fn[0].value}}

    Sender Address: {{transaction.from}}

    - +
  • Recipient: {{transaction.recipient?.vcard.fn[0].value}}

    Recipient Address: {{transaction.to}}

    - +
  • - Amount: {{transaction.token.symbol + ' ' + transaction.value}} + Amount: SRF {{transaction.value | tokenRatio}}
  • Token:

    • - Address: {{transaction.token.address}} + Address: {{transaction.token._address}}
    • - Name: {{transaction.token.name}} + + Name: Sarafu Token
    • - Symbol: {{transaction.token.symbol}} + + Symbol: SRF
    diff --git a/src/app/pages/transactions/transaction-details/transaction-details.component.ts b/src/app/pages/transactions/transaction-details/transaction-details.component.ts index 55f2ef9..229f5b9 100644 --- a/src/app/pages/transactions/transaction-details/transaction-details.component.ts +++ b/src/app/pages/transactions/transaction-details/transaction-details.component.ts @@ -1,4 +1,5 @@ import {Component, Input, OnInit} from '@angular/core'; +import {Router} from '@angular/router'; @Component({ selector: 'app-transaction-details', @@ -8,9 +9,16 @@ import {Component, Input, OnInit} from '@angular/core'; export class TransactionDetailsComponent implements OnInit { @Input() transaction; - constructor() { } + constructor(private router: Router) { } ngOnInit(): void { } + viewSender(): void { + this.router.navigateByUrl(`/accounts/${this.transaction.from}`).then(); + } + + viewRecipient(): void { + this.router.navigateByUrl(`/accounts/${this.transaction.to}`).then(); + } } diff --git a/src/app/pages/transactions/transactions.component.html b/src/app/pages/transactions/transactions.component.html index 1047b54..e83e6d7 100644 --- a/src/app/pages/transactions/transactions.component.html +++ b/src/app/pages/transactions/transactions.component.html @@ -56,82 +56,75 @@ search - - - View - - - - - - Sender - {{transaction.sender?.vcard.fn[0].value}} + Sender + {{transaction?.sender?.vcard.fn[0].value}} - - Sender Location - {{transaction.sender?.location.area_name}} - + + + + - Recipient - {{transaction.recipient?.vcard.fn[0].value}} + Recipient + {{transaction?.recipient?.vcard.fn[0].value}} - - Recipient Location - {{transaction.recipient?.location.area_name}} - + + + + - - Token - - {{transaction.token.name}} - {{transaction.destinationToken.name}} - - + + + + + + + - Value - - {{transaction.value | tokenRatio}} - {{transaction.toValue | tokenRatio}} - + Value + + {{transaction?.value | tokenRatio}} + {{transaction?.toValue | tokenRatio}} + - Created - {{transaction.tx.timestamp | date}} + Created + {{transaction?.tx.timestamp | date}} - TYPE - - {{transaction.type}} - + TYPE + + {{transaction?.type}} + - - - - - - - - - - + + + + + + + + + + + + + + - - - + + + diff --git a/src/app/pages/transactions/transactions.component.ts b/src/app/pages/transactions/transactions.component.ts index 3810dff..d98f5d6 100644 --- a/src/app/pages/transactions/transactions.component.ts +++ b/src/app/pages/transactions/transactions.component.ts @@ -12,7 +12,7 @@ import {MatSort} from '@angular/material/sort'; }) export class TransactionsComponent implements OnInit, AfterViewInit { transactionDataSource: MatTableDataSource; - transactionDisplayedColumns = ['view', 'sender', 'senderLocation', 'recipient', 'recipientLocation', 'token', 'value', 'created', 'type', 'select']; + transactionDisplayedColumns = ['sender', 'recipient', 'value', 'created', 'type']; initialSelection = []; allowMultiSelect = true; transactionSelection: SelectionModel; diff --git a/src/app/pages/transactions/transactions.module.ts b/src/app/pages/transactions/transactions.module.ts index 2d0f0c0..55dd2f9 100644 --- a/src/app/pages/transactions/transactions.module.ts +++ b/src/app/pages/transactions/transactions.module.ts @@ -16,6 +16,7 @@ import {MatButtonModule} from '@angular/material/button'; import {MatIconModule} from '@angular/material/icon'; import {MatSelectModule} from '@angular/material/select'; import {MatCardModule} from '@angular/material/card'; +import {MatRippleModule} from '@angular/material/core'; @NgModule({ @@ -23,21 +24,22 @@ import {MatCardModule} from '@angular/material/card'; exports: [ TransactionDetailsComponent ], - imports: [ - CommonModule, - TransactionsRoutingModule, - DataTablesModule, - SharedModule, - MatTableModule, - MatCheckboxModule, - MatPaginatorModule, - MatSortModule, - MatFormFieldModule, - MatInputModule, - MatButtonModule, - MatIconModule, - MatSelectModule, - MatCardModule - ] + imports: [ + CommonModule, + TransactionsRoutingModule, + DataTablesModule, + SharedModule, + MatTableModule, + MatCheckboxModule, + MatPaginatorModule, + MatSortModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + MatIconModule, + MatSelectModule, + MatCardModule, + MatRippleModule + ] }) export class TransactionsModule { } diff --git a/src/app/shared/_pipes/token-ratio.pipe.ts b/src/app/shared/_pipes/token-ratio.pipe.ts index 14400c0..c4cbc8e 100644 --- a/src/app/shared/_pipes/token-ratio.pipe.ts +++ b/src/app/shared/_pipes/token-ratio.pipe.ts @@ -3,6 +3,6 @@ import {Pipe, PipeTransform} from '@angular/core'; @Pipe({name: 'tokenRatio'}) export class TokenRatioPipe implements PipeTransform { transform(value: any, ...args): any { - return Number(value) / Math.pow(10, 18); + return Number(value) / Math.pow(10, 6); } } diff --git a/src/app/shared/topbar/topbar.component.html b/src/app/shared/topbar/topbar.component.html index cd0d34b..3959f87 100644 --- a/src/app/shared/topbar/topbar.component.html +++ b/src/app/shared/topbar/topbar.component.html @@ -1,7 +1,7 @@