From eb3c35176a670e8086ceaff63fb36d6869e920ef Mon Sep 17 00:00:00 2001 From: James He Date: Thu, 23 Jun 2022 21:25:30 -0500 Subject: [PATCH 1/6] enabling list fee recipient method --- .../core/interceptors/mock.interceptor.ts | 11 ++++++ src/app/modules/core/mocks/index.ts | 14 ++++++- .../core/services/validator.service.ts | 6 +++ .../modules/core/services/wallet.service.ts | 2 +- .../validator-performance-list.component.html | 14 ++++--- .../validator-performance-list.component.ts | 36 +++++++++++++++--- .../accounts-table.component.html | 5 ++- .../accounts-table.component.ts | 2 + .../pages/accounts/accounts.component.ts | 38 ++++++++++++------- .../accounts/v2/web_api_keymanager-api 2.ts | 19 ---------- .../accounts/v2/web_api_keymanager-api.ts | 9 +++++ src/environments/environment.prod.ts | 2 +- src/environments/environment.staging.ts | 2 +- src/environments/environment.ts | 2 +- src/styles/layouts/_table.scss | 13 +++++++ src/styles/tailwind-generated.scss | 9 +---- 16 files changed, 126 insertions(+), 58 deletions(-) delete mode 100644 src/app/proto/validator/accounts/v2/web_api_keymanager-api 2.ts diff --git a/src/app/modules/core/interceptors/mock.interceptor.ts b/src/app/modules/core/interceptors/mock.interceptor.ts index 731c9da4..a2eb26c0 100644 --- a/src/app/modules/core/interceptors/mock.interceptor.ts +++ b/src/app/modules/core/interceptors/mock.interceptor.ts @@ -13,6 +13,7 @@ import { EnvironmenterService } from '../services/environmenter.service'; export const VALIDATOR_API_PREFIX = '/v2/validator'; export const KEYMANAGER_API_PREFIX = '/eth/v1/keystores'; +export const KEYMANAGER_API_VALIDATOR_PREFIX = '/eth/v1/validator'; @Injectable() export class MockInterceptor implements HttpInterceptor { @@ -29,6 +30,16 @@ export class MockInterceptor implements HttpInterceptor { body: mock[request.method as keyof RestObject], })); } + if (this.contains(request.url, KEYMANAGER_API_VALIDATOR_PREFIX )) { + if(this.contains(request.url,'feerecipient')){ + let mock = KeymanagerAPIMocks['/eth/v1/validator/{pubkey}/feerecipient']; + return of(new HttpResponse({ + status: 200, + body: mock[request.method as keyof RestObject], + })); + } + + } if (this.contains(request.url, VALIDATOR_API_PREFIX)) { endpoint = this.extractEndpoint(request.url, VALIDATOR_API_PREFIX); } diff --git a/src/app/modules/core/mocks/index.ts b/src/app/modules/core/mocks/index.ts index 50105d18..ba6e386d 100644 --- a/src/app/modules/core/mocks/index.ts +++ b/src/app/modules/core/mocks/index.ts @@ -9,7 +9,7 @@ import { ValidatorParticipation } from 'src/app/proto/eth/v1alpha1/validator'; import { Account, BackupAccountsResponse, BeaconStatusResponse, GenerateMnemonicResponse, ImportKeystoresResponse, InitializeAuthResponse, ListAccountsResponse, WalletResponse } from 'src/app/proto/validator/accounts/v2/web_api'; -import { DeleteAccountsResponse } from 'src/app/proto/validator/accounts/v2/web_api_keymanager-api'; +import { DeleteAccountsResponse, ListFeeRecipientResponse } from 'src/app/proto/validator/accounts/v2/web_api_keymanager-api'; import { GWEI_PER_ETHER } from '../constants'; @@ -583,7 +583,17 @@ export const KeymanagerAPIMocks = { }], slashing_protection: "{\"metadata\":{\"interchange_format_version\":\"5\",\"genesis_validators_root\":\"0x043db0d9a83813551ee2f33450d23797757d430911a9320530ad8a0eabc43efb\"},\"data\":[{\"pubkey\":\"0x8d65ffe7b65ee8e7c3e14a474a360f16722920ef8c0ef1da6f942fd6ddc3628c1edda7f57cf28d7ddc7fc9cb9745df60\",\"signed_blocks\":[],\"signed_attestations\":[{\"source_epoch\":\"71793\",\"target_epoch\":\"71794\",\"signing_root\":\"0xc996259c3456818eb1cc5102a88316ff505d940a9840a5205f54ba3334a60aa8\"},{\"source_epoch\":\"71794\",\"target_epoch\":\"71795\",\"signing_root\":\"0x0ea5c7312ebd4a0a009fd92780972a0ad7391eb12143218359b1019140da4e53\"},{\"source_epoch\":\"71795\",\"target_epoch\":\"71796\",\"signing_root\":\"0xe09c4e38834637c4588fde948827709959e9d177d93207f2d00951bb4ddc18ff\"},{\"source_epoch\":\"71796\",\"target_epoch\":\"71797\",\"signing_root\":\"0xf13f47d87685ffc6258cbd831dd1e375cf54cfa1702ef3ec92a8d230ca353c44\"},{\"source_epoch\":\"71797\",\"target_epoch\":\"71798\",\"signing_root\":\"0x57ab41e44ca77e9bb26a9a1a2950eda83ba1f900091a200bcab6bab4bc921c0c\"},{\"source_epoch\":\"71798\",\"target_epoch\":\"71799\",\"signing_root\":\"0x444eed11867e86d3ce6b97e08d7d8bb87f403583a9940f364eaba04c862e78f5\"},{\"source_epoch\":\"71799\",\"target_epoch\":\"71800\",\"signing_root\":\"0xa0fcc0fbf0b7f24d18fc3f4efbd474fc762fc4ed2ca3a0798d4b07aeee1b144b\"},{\"source_epoch\":\"71800\",\"target_epoch\":\"71801\",\"signing_root\":\"0xcdd9a1454f93632fb3f4da8f0654306c045af6b8490a8558780cc27b43d763a2\"},{\"source_epoch\":\"71801\",\"target_epoch\":\"71802\",\"signing_root\":\"0x4e473e9bcafd60f6c5c53eceb8cdcdfa0b2a165830fd82243e8b682e373b248e\"},{\"source_epoch\":\"71802\",\"target_epoch\":\"71803\",\"signing_root\":\"0xf98c0a28d46739bbdee389281b087635fbc7c58ddb27e9736d9376500b865674\"},{\"source_epoch\":\"71803\",\"target_epoch\":\"71804\",\"signing_root\":\"0x52ffb97763758fbe1a22783f98983b78f580815fb41fada3bb0cfc886e0838d1\"},{\"source_epoch\":\"71804\",\"target_epoch\":\"71805\",\"signing_root\":\"0x2388327613c16412baf6b3429603dbfc31fa84490877e57706d464b26a720241\"},{\"source_epoch\":\"71805\",\"target_epoch\":\"71806\",\"signing_root\":\"0xfc5b7a3bee9e71c76691ab392ed9e21730eba735f129eaf150c754eb9b0ebd56\"},{\"source_epoch\":\"71806\",\"target_epoch\":\"71807\",\"signing_root\":\"0xa4071f5852bbde14c2128027682dfa8e9c166d3affc42fd45b7b1120d16cb126\"},{\"source_epoch\":\"71807\",\"target_epoch\":\"71808\",\"signing_root\":\"0xb8ee3840e82dd2d8982d551b079dc77d64d74e93045207315ab67531c345db88\"},{\"source_epoch\":\"71808\",\"target_epoch\":\"71809\",\"signing_root\":\"0x700567d637657c615410e60e45ccc1f313173242480454cdc3ac1c239c54876e\"},{\"source_epoch\":\"71809\",\"target_epoch\":\"71810\",\"signing_root\":\"0xa6ad2005a5675a9b33cf08ac1f0fafb3b96afd0acb004e2daccee981e0bc6ca0\"},{\"source_epoch\":\"71810\",\"target_epoch\":\"71811\",\"signing_root\":\"0xf78c31f4158dce92c386261a48d77b47813deaaeefc8679790eb69decc6e2dcb\"},{\"source_epoch\":\"71811\",\"target_epoch\":\"71812\",\"signing_root\":\"0xfd21111aa0c2e2ddd7b599378b23095dd176d9fb6f86805e103ad9ea2b602a8a\"},{\"source_epoch\":\"71812\",\"target_epoch\":\"71813\",\"signing_root\":\"0xa50d236f1063b04b34a2b9d0e8f3fb56e495ebf26e992e8d8b89af66b7d28243\"},{\"source_epoch\":\"71813\",\"target_epoch\":\"71814\",\"signing_root\":\"0x0b9df665c3b717d5ffc7b14a6cad4b883edb9b3068c5ded219522d626f8b38ac\"},{\"source_epoch\":\"71814\",\"target_epoch\":\"71815\",\"signing_root\":\"0xfe9f887500d0797f5340d336495020795e896c95eed15a6d990c63bcffffca48\"},{\"source_epoch\":\"71815\",\"target_epoch\":\"71816\",\"signing_root\":\"0xfe5b638cade977c527bcb0cab500ad2bd1d992432c22641f08b376210f76322d\"},{\"source_epoch\":\"71816\",\"target_epoch\":\"71817\",\"signing_root\":\"0x0efb9c40d8771746a7bfeb8e50da9ee281c91b6e315f538b677251989592ccf4\"},{\"source_epoch\":\"71817\",\"target_epoch\":\"71818\",\"signing_root\":\"0x534238e82f5a637076166143acacccd45b629b5f271020d9917f2a307b5b3daf\"},{\"source_epoch\":\"71818\",\"target_epoch\":\"71819\",\"signing_root\":\"0x7bcb82fb0cb70e14e6d14981d8c372b096af2051048bb765644fee0b34e06af3\"},{\"source_epoch\":\"71819\",\"target_epoch\":\"71820\",\"signing_root\":\"0x79341fd23fac19f66b62729c675f0841269dd51b1796d982f726bb3697b945a3\"},{\"source_epoch\":\"71820\",\"target_epoch\":\"71821\",\"signing_root\":\"0x4cee1928b36d80d846ac4ad03798604415ce9df67f8aae2154a5143c467f7045\"},{\"source_epoch\":\"71821\",\"target_epoch\":\"71822\",\"signing_root\":\"0xd52fd16a994e5c0321acbe99f75050ab35f34a40f830bdf2c7e22639fde32bba\"},{\"source_epoch\":\"71822\",\"target_epoch\":\"71823\",\"signing_root\":\"0x72f7fabc617608017e405fb4b7d38a5cb32415d02f680aba4197399a4297e85c\"},{\"source_epoch\":\"71823\",\"target_epoch\":\"71824\",\"signing_root\":\"0x833d572c3175a3da0ff52daa77a1c43930f5cfb1bd76d949e709b7499f2ce976\"},{\"source_epoch\":\"71824\",\"target_epoch\":\"71825\",\"signing_root\":\"0xb41571cdf4b6bddac9de6fd9d8dd307db80f4bdaffd59f9a5453c9e369e63857\"},{\"source_epoch\":\"71825\",\"target_epoch\":\"71826\",\"signing_root\":\"0x6128c2914d23051a0dd850271ef82d5cee0ea060da153d9557b8d79ca4df0f96\"},{\"source_epoch\":\"71826\",\"target_epoch\":\"71827\",\"signing_root\":\"0xf360a01608e54d16da72133511b7d6a5e535bf2a6ec2f8e793424126808d50e5\"},{\"source_epoch\":\"71827\",\"target_epoch\":\"71828\",\"signing_root\":\"0xb54d8eaa2f26c61f3b63d4691f88970c6ee0d4180e704f7e1333a7de7381ebc7\"},{\"source_epoch\":\"71828\",\"target_epoch\":\"71829\",\"signing_root\":\"0x0b6dfce0ba24e05930e91ace94988785d3334d45f33356801895a07ba7a1c820\"},{\"source_epoch\":\"71829\",\"target_epoch\":\"71830\",\"signing_root\":\"0xb36a3059dac0dd3c76d7557b67c2134f7b01f40e636e37149a3656dd0b77bb63\"},{\"source_epoch\":\"71830\",\"target_epoch\":\"71831\",\"signing_root\":\"0xa82420dd16591544680bd55eb05491a69fe2ed37fab16d6f4b858c2f45dfbc90\"},{\"source_epoch\":\"71831\",\"target_epoch\":\"71832\",\"signing_root\":\"0x2a59b5be18b4223ccf1ccb895c9bd959808d96e252cb098485ef73816b818a4c\"},{\"source_epoch\":\"71832\",\"target_epoch\":\"71833\",\"signing_root\":\"0xbb031ce7ed980a545eccdf330a109c748003e816a9376a6ca51bf126037f1520\"},{\"source_epoch\":\"71833\",\"target_epoch\":\"71834\",\"signing_root\":\"0x9efcb9af1e9ed8b9b0c05726159d1bf9c8190a5b6b4630e4a0ba48ddb328e444\"},{\"source_epoch\":\"71834\",\"target_epoch\":\"71835\",\"signing_root\":\"0x1b48057c770f415b4c2a67197dcb7eebcf05a90f87ee818034291fa3e0f2cdbc\"},{\"source_epoch\":\"71835\",\"target_epoch\":\"71836\",\"signing_root\":\"0x33f7a8c54ef1e3e353537901a195a0be1ffbc0d9922f15fc18e836f7d9d49d13\"},{\"source_epoch\":\"71836\",\"target_epoch\":\"71837\",\"signing_root\":\"0x1289e1b831366e1dce42eacf93ce357ce4793c143251953d43d086f8ce738b93\"},{\"source_epoch\":\"71837\",\"target_epoch\":\"71838\",\"signing_root\":\"0x09abf84d9b8b528ce6fdfb4d6b456a380f6def97ac33600680c740a58c4824af\"},{\"source_epoch\":\"71838\",\"target_epoch\":\"71839\",\"signing_root\":\"0x8b5201fa5038f1aedd85258352a9380a70d423cd23300fd4cf27ae20957f65e0\"},{\"source_epoch\":\"71839\",\"target_epoch\":\"71840\",\"signing_root\":\"0x13ccfddfc270176a5379218974d7e62984b07cf23025cb4b579a4c6a209549cf\"},{\"source_epoch\":\"71840\",\"target_epoch\":\"71841\",\"signing_root\":\"0x9f2249c6bdfeec47b52609189c4befbb5b39debf5cafa25d7d280a65b08c6ccf\"},{\"source_epoch\":\"71841\",\"target_epoch\":\"71842\",\"signing_root\":\"0xe4a6f8bdd4d6cc255ad80c39618e8f3c2c64ace4d5bde8aedc90bcecad6713b8\"},{\"source_epoch\":\"71842\",\"target_epoch\":\"71843\",\"signing_root\":\"0xc495fe48309ebbccb7e888772a0174978a57e37f135f418d822081de62422bc9\"},{\"source_epoch\":\"72225\",\"target_epoch\":\"72226\",\"signing_root\":\"0x399d34a7be6120925668009f2fb6bc1cdcde7ffa9d96ef5bdf6f13db49bdef93\"},{\"source_epoch\":\"72226\",\"target_epoch\":\"72227\",\"signing_root\":\"0x0b81c94db5d4cf6a12c9198a407f811019464899126ae16b040bd25fa3f52a47\"}]}]}" } as DeleteAccountsResponse, - } as RestObject + } as RestObject, + '/eth/v1/validator/{pubkey}/feerecipient':{ + 'GET':{ + data:{ + pubkey:"0xaadaf653799229200378369ee7d6d9fdbdcdc2788143ed44f1ad5f2367c735e83a37c5bb80d7fb917de73a61bbcf00c4", + ethAddress: "0xasdfsadfsfsfsdfsadfsdafsadfsadsdafasdf" + } + } as ListFeeRecipientResponse, + 'POST':{}, + 'DELETE':{}, + }as RestObject } export interface RestObject { diff --git a/src/app/modules/core/services/validator.service.ts b/src/app/modules/core/services/validator.service.ts index 2c4238c0..69ab1f27 100644 --- a/src/app/modules/core/services/validator.service.ts +++ b/src/app/modules/core/services/validator.service.ts @@ -6,6 +6,7 @@ import { ValidatorBalances, Validators, ValidatorSummaryResponse } from 'src/app/proto/eth/v1alpha1/beacon_chain'; import { VersionResponse } from 'src/app/proto/validator/accounts/v2/web_api'; +import { ListFeeRecipientResponse } from 'src/app/proto/validator/accounts/v2/web_api_keymanager-api'; import { EnvironmenterService } from './environmenter.service'; import { WalletService } from './wallet.service'; @@ -24,6 +25,7 @@ export class ValidatorService { private environmenter: EnvironmenterService, ) { } private apiUrl = this.environmenter.env.validatorEndpoint; + private keymanagerUrl = this.environmenter.env.keymanagerEndpoint; version$: Observable = this.http.get(`${this.apiUrl}/health/version`).pipe( share(), @@ -57,6 +59,10 @@ export class ValidatorService { return this.http.get(`${this.apiUrl}/beacon/validators${params}`); } + getFeeRecipient(publicKey:string): Observable< ListFeeRecipientResponse>{ + return this.http.get(`${this.keymanagerUrl}/validator/${publicKey}/feerecipient`) + } + balances( publicKeys: string[], pageIndex: number, diff --git a/src/app/modules/core/services/wallet.service.ts b/src/app/modules/core/services/wallet.service.ts index 09386abb..0f105992 100644 --- a/src/app/modules/core/services/wallet.service.ts +++ b/src/app/modules/core/services/wallet.service.ts @@ -27,7 +27,7 @@ export class WalletService { ) {} private apiUrl = this.environmenter.env.validatorEndpoint; - private keymanagerApiUrl = this.environmenter.env.keymanagerEndpoint; + private keymanagerApiUrl = this.environmenter.env.keymanagerEndpoint+"/keystores"; // Observables. walletConfig$: Observable = this.http diff --git a/src/app/modules/dashboard/components/validator-performance-list/validator-performance-list.component.html b/src/app/modules/dashboard/components/validator-performance-list/validator-performance-list.component.html index d605e458..739f9ac9 100644 --- a/src/app/modules/dashboard/components/validator-performance-list/validator-performance-list.component.html +++ b/src/app/modules/dashboard/components/validator-performance-list/validator-performance-list.component.html @@ -18,8 +18,13 @@ {{element.publicKey | base64tohex}} - - + + Fee Recipient + + {{element.feeRecipient }} + + + Correctly voted source
+ Voted target
+ Voted head
diff --git a/src/app/modules/dashboard/components/validator-performance-list/validator-performance-list.component.ts b/src/app/modules/dashboard/components/validator-performance-list/validator-performance-list.component.ts index baf60493..0512180b 100644 --- a/src/app/modules/dashboard/components/validator-performance-list/validator-performance-list.component.ts +++ b/src/app/modules/dashboard/components/validator-performance-list/validator-performance-list.component.ts @@ -5,14 +5,17 @@ import { MatTableDataSource } from '@angular/material/table'; import { BigNumber } from 'ethers'; import { ValidatorSummaryResponse } from 'src/app/proto/eth/v1alpha1/beacon_chain'; -import { throwError } from 'rxjs'; -import { catchError, map, take, tap, takeUntil, filter } from 'rxjs/operators'; +import { EMPTY, forkJoin, Observable, throwError, zip } from 'rxjs'; +import { catchError, map, take, tap, takeUntil, filter, switchMap, flatMap, concatMap, mergeMap } from 'rxjs/operators'; import { ValidatorService } from '../../../core/services/validator.service'; import { BaseComponent } from '../../../shared/components/base.component'; import { UserService } from '../../../shared/services/user.service'; +import { ListFeeRecipientResponse } from 'src/app/proto/validator/accounts/v2/web_api_keymanager-api'; +import { base64ToHex } from 'src/app/modules/core/utils/hex-util'; export interface ValidatorListItem { publicKey: string; + feeRecipient: string; currentEffectiveBalances: string; correctlyVotedSource: boolean; correctlyVotedTarget: boolean; @@ -31,6 +34,7 @@ export class ValidatorPerformanceListComponent implements OnInit { displayedColumns: string[] = [ 'publicKey', + 'feeRecipient', 'correctlyVotedSource', 'correctlyVotedTarget', 'correctlyVotedHead', @@ -56,6 +60,7 @@ export class ValidatorPerformanceListComponent .pipe( map((performance: ValidatorSummaryResponse) => { const list: ValidatorListItem[] = []; + if (performance) { for (let i = 0; i < performance.public_keys.length; i++) { // converting snake_case to camelCase @@ -78,12 +83,29 @@ export class ValidatorPerformanceListComponent } return list; }), - tap((result) => { - this.dataSource = new MatTableDataSource(result); + switchMap((list:ValidatorListItem[]) => { + const arrayOfRequests: Observable[] = []; + list.forEach((item:ValidatorListItem)=>{ + arrayOfRequests.push(this.validatorService.getFeeRecipient(item.publicKey)); + }); + return forkJoin(arrayOfRequests).pipe( + map((res:ListFeeRecipientResponse[]) => { + res.forEach((r)=>{ + let item = list.find((obj)=>base64ToHex(obj.publicKey) === r.data.pubkey) + if(item){ + item.feeRecipient = r.data.ethAddress + } + }) + return list; + }) + ) + }), + tap((list:ValidatorListItem[])=>{ + this.dataSource = new MatTableDataSource(list); this.dataSource.paginator = this.paginator; this.dataSource.sort = this.sort; this.loading = false; - this.noData = result.length === 0; + this.noData = list.length === 0; }), catchError((err) => { this.loading = false; @@ -119,3 +141,7 @@ export class ValidatorPerformanceListComponent this.userService.changeGainsAndLosesPageSize(ev.pageSize); } } +function compactMap(arg0: (res: ListFeeRecipientResponse[]) => void): import("rxjs").OperatorFunction { + throw new Error('Function not implemented.'); +} + diff --git a/src/app/modules/wallet/components/accounts-table/accounts-table.component.html b/src/app/modules/wallet/components/accounts-table/accounts-table.component.html index 5e0db8d8..46aa3b3a 100644 --- a/src/app/modules/wallet/components/accounts-table/accounts-table.component.html +++ b/src/app/modules/wallet/components/accounts-table/accounts-table.component.html @@ -37,7 +37,10 @@ Validator Index {{row.index}} - + + Fee Recipient + {{row.feeRecipient | slice:0:8 }}... + ETH Balance diff --git a/src/app/modules/wallet/components/accounts-table/accounts-table.component.ts b/src/app/modules/wallet/components/accounts-table/accounts-table.component.ts index d48528c0..797349e2 100644 --- a/src/app/modules/wallet/components/accounts-table/accounts-table.component.ts +++ b/src/app/modules/wallet/components/accounts-table/accounts-table.component.ts @@ -16,6 +16,7 @@ export interface TableData { accountName: string; index: number; publicKey: string; + feeRecipient: string; balance: string; effectiveBalance: string; status: string; @@ -47,6 +48,7 @@ export class AccountsTableComponent implements AfterViewInit,OnChanges { 'accountName', 'publicKey', 'index', + 'feeRecipient', 'balance', 'effectiveBalance', 'activationEpoch', diff --git a/src/app/modules/wallet/pages/accounts/accounts.component.ts b/src/app/modules/wallet/pages/accounts/accounts.component.ts index d74f13e5..c0383c65 100644 --- a/src/app/modules/wallet/pages/accounts/accounts.component.ts +++ b/src/app/modules/wallet/pages/accounts/accounts.component.ts @@ -4,7 +4,7 @@ import { MatTableDataSource } from '@angular/material/table'; import { SelectionModel } from '@angular/cdk/collections'; import { BigNumber } from 'ethers'; -import { BehaviorSubject, Observable, throwError, zip } from 'rxjs'; +import { BehaviorSubject, forkJoin, Observable, of, throwError, zip } from 'rxjs'; import { catchError, debounceTime, @@ -34,6 +34,7 @@ import { formatUnits } from 'ethers/lib/utils'; import { UserService } from 'src/app/modules/shared/services/user.service'; import { BaseComponent } from '../../../shared/components/base.component'; import { filter, takeUntil } from 'rxjs/operators'; +import { FeeRecipientData, ListFeeRecipientResponse } from 'src/app/proto/validator/accounts/v2/web_api_keymanager-api'; @Component({ selector: 'app-accounts', @@ -74,18 +75,18 @@ export class AccountsComponent extends BaseComponent implements OnInit { zipMap((accs) => accs.accounts?.map((account) => account.validating_public_key) ), - switchMap(([accountsResponse, pubKeys]) => - // Combine the list of validators and their balances to display in the table. - zip( - this.validatorService.validatorList(pubKeys, 0, pubKeys.length), - this.validatorService.balances(pubKeys, 0, pubKeys.length) - ).pipe( - map(([validators, balances]) => - // Transform the data into a pretty format for our table. - this.transformTableData(accountsResponse, validators, balances) - ) - ) - ) + switchMap(([accountsResponse, pubKeys]) =>{ + return zip( + this.validatorService.validatorList(pubKeys, 0, pubKeys.length), + this.validatorService.balances(pubKeys, 0, pubKeys.length), + zip(this.feeRecipients$(pubKeys)) + ).pipe( + map(([validators, balances,feeRecipients]) =>{ + // Transform the data into a pretty format for our table. + return this.transformTableData(accountsResponse, validators, balances, feeRecipients.map(f=>f.data)) + }) + ) + }), ); }), share(), // Share the observable across all subscribers. @@ -122,17 +123,25 @@ export class AccountsComponent extends BaseComponent implements OnInit { this.userService.changeAccountListPerPage(event.pageSize); this.pageChanged$.next(event); } + + private feeRecipients$(pubkeys:string[]):Observable[]{ + return pubkeys.map(key=>this.validatorService.getFeeRecipient(key)); + } private transformTableData( accountsResponse: ListAccountsResponse, validators: Validators, - balances: ValidatorBalances + balances: ValidatorBalances, + feeRecipients: FeeRecipientData[] ): MatTableDataSource { this.totalData = accountsResponse.total_size; const tableData = accountsResponse.accounts.map((acc, idx) => { let val = validators?.validator_list?.find( (v) => acc.validating_public_key === v?.validator?.public_key ); + + let feeRecipient = feeRecipients.find(data=> data.pubkey === base64ToHex(acc.validating_public_key)); + if (!val) { val = { index: 0, @@ -171,6 +180,7 @@ export class AccountsComponent extends BaseComponent implements OnInit { exitEpoch: val?.validator?.exit_epoch, lowBalance: effectiveBalance.toNumber() < 32, options: acc.validating_public_key, + feeRecipient: feeRecipient?.ethAddress , } as TableData; }); const dataSource = new MatTableDataSource(tableData); diff --git a/src/app/proto/validator/accounts/v2/web_api_keymanager-api 2.ts b/src/app/proto/validator/accounts/v2/web_api_keymanager-api 2.ts deleted file mode 100644 index e5774d77..00000000 --- a/src/app/proto/validator/accounts/v2/web_api_keymanager-api 2.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* Keymanager API standard */ - - -export interface DeleteAccountsRequest { - /** - * Public keys to delete. - */ - pubkeys: string[]; -} - -export interface DeleteAccountsResponse { - data: DeleteAccountsData[], - slashing_protection: string -} - -export interface DeleteAccountsData { - status: string, - message: string -} \ No newline at end of file diff --git a/src/app/proto/validator/accounts/v2/web_api_keymanager-api.ts b/src/app/proto/validator/accounts/v2/web_api_keymanager-api.ts index e5774d77..c64fefa6 100644 --- a/src/app/proto/validator/accounts/v2/web_api_keymanager-api.ts +++ b/src/app/proto/validator/accounts/v2/web_api_keymanager-api.ts @@ -16,4 +16,13 @@ export interface DeleteAccountsResponse { export interface DeleteAccountsData { status: string, message: string +} + +export interface ListFeeRecipientResponse { + data: FeeRecipientData +} + +export interface FeeRecipientData { + pubkey: string, + ethAddress: string } \ No newline at end of file diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 53cb2063..ab7c14cf 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -4,5 +4,5 @@ import { IEnvironment } from './token'; export const environment: IEnvironment = { production: true, validatorEndpoint: '/api/v2/validator', - keymanagerEndpoint: '/eth/v1/keystores', + keymanagerEndpoint: '/eth/v1', }; diff --git a/src/environments/environment.staging.ts b/src/environments/environment.staging.ts index 344f2bf8..8b2f0de4 100644 --- a/src/environments/environment.staging.ts +++ b/src/environments/environment.staging.ts @@ -3,6 +3,6 @@ import { IEnvironment } from './token'; export const environment: IEnvironment = { production: false, validatorEndpoint: 'http://127.0.0.1:7500/api/v2/validator', - keymanagerEndpoint: 'http://127.0.0.1:7500/eth/v1/keystores', + keymanagerEndpoint: 'http://127.0.0.1:7500/eth/v1', mockInterceptor: false }; \ No newline at end of file diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 35aaed73..0400a11d 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -6,7 +6,7 @@ import { IEnvironment } from './token'; export const environment: IEnvironment = { production: false, validatorEndpoint: 'http://127.0.0.1:7500/api/v2/validator', - keymanagerEndpoint: 'http://127.0.0.1:7500/eth/v1/keystores', + keymanagerEndpoint: 'http://127.0.0.1:7500/eth/v1', mockInterceptor: true }; diff --git a/src/styles/layouts/_table.scss b/src/styles/layouts/_table.scss index 762495cd..598a796a 100644 --- a/src/styles/layouts/_table.scss +++ b/src/styles/layouts/_table.scss @@ -9,6 +9,19 @@ table.mat-table { justify-content: center; } + .mat-column-correctlyVotedSource { + max-width: 70px; + } + .mat-column-correctlyVotedTarget { + max-width: 50px; + } + .mat-column-correctlyVotedHead { + max-width: 50px; + } + .mat-column-gains{ + max-width: 75px; + } + th.mat-header-cell:first-of-type, td.mat-cell:first-of-type, td.mat-footer-cell:first-of-type { padding-left: 12px; padding-right: 20px; diff --git a/src/styles/tailwind-generated.scss b/src/styles/tailwind-generated.scss index def0a60a..18a7c2ce 100644 --- a/src/styles/tailwind-generated.scss +++ b/src/styles/tailwind-generated.scss @@ -1,4 +1,4 @@ -/*! tailwindcss v2.2.17 | MIT License | https://tailwindcss.com */ +/*! tailwindcss v2.2.19 | MIT License | https://tailwindcss.com */ /*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */ @@ -22,7 +22,6 @@ Use a more readable tab size (opinionated). */ html { - -moz-tab-size: 4; tab-size: 4; } @@ -10063,7 +10062,6 @@ video { .appearance-none { -webkit-appearance: none; - -moz-appearance: none; appearance: none; } @@ -27789,7 +27787,6 @@ video { .sm\:appearance-none { -webkit-appearance: none; - -moz-appearance: none; appearance: none; } @@ -45459,7 +45456,6 @@ video { .md\:appearance-none { -webkit-appearance: none; - -moz-appearance: none; appearance: none; } @@ -63129,7 +63125,6 @@ video { .lg\:appearance-none { -webkit-appearance: none; - -moz-appearance: none; appearance: none; } @@ -80799,7 +80794,6 @@ video { .xl\:appearance-none { -webkit-appearance: none; - -moz-appearance: none; appearance: none; } @@ -98469,7 +98463,6 @@ video { .\32xl\:appearance-none { -webkit-appearance: none; - -moz-appearance: none; appearance: none; } From 5df69e375d3d7c42699fb1732bc3783667f58f37 Mon Sep 17 00:00:00 2001 From: James He Date: Fri, 24 Jun 2022 17:15:31 -0500 Subject: [PATCH 2/6] adding new component for updating fee recipient --- .../core/services/validator.service.ts | 10 ++- .../accounts-table.component.html | 2 +- .../accounts-table.component.ts | 18 +++- .../fee-recipient-edit.component.html | 78 +++++++++++++++++ .../fee-recipient-edit.component.spec.ts | 52 ++++++++++++ .../fee-recipient-edit.component.ts | 84 +++++++++++++++++++ .../icon-trigger-select.component.ts | 5 +- src/app/modules/wallet/wallet.module.ts | 2 + .../accounts/v2/web_api_keymanager-api.ts | 4 + 9 files changed, 249 insertions(+), 6 deletions(-) create mode 100644 src/app/modules/wallet/components/fee-recipient/fee-recipient-edit.component.html create mode 100644 src/app/modules/wallet/components/fee-recipient/fee-recipient-edit.component.spec.ts create mode 100644 src/app/modules/wallet/components/fee-recipient/fee-recipient-edit.component.ts diff --git a/src/app/modules/core/services/validator.service.ts b/src/app/modules/core/services/validator.service.ts index 69ab1f27..0a1236bb 100644 --- a/src/app/modules/core/services/validator.service.ts +++ b/src/app/modules/core/services/validator.service.ts @@ -6,7 +6,7 @@ import { ValidatorBalances, Validators, ValidatorSummaryResponse } from 'src/app/proto/eth/v1alpha1/beacon_chain'; import { VersionResponse } from 'src/app/proto/validator/accounts/v2/web_api'; -import { ListFeeRecipientResponse } from 'src/app/proto/validator/accounts/v2/web_api_keymanager-api'; +import { ListFeeRecipientResponse, SetFeeRecipientRequest } from 'src/app/proto/validator/accounts/v2/web_api_keymanager-api'; import { EnvironmenterService } from './environmenter.service'; import { WalletService } from './wallet.service'; @@ -63,6 +63,14 @@ export class ValidatorService { return this.http.get(`${this.keymanagerUrl}/validator/${publicKey}/feerecipient`) } + setFeeRecipient(publicKey:string,request: SetFeeRecipientRequest){ + return this.http.post(`${this.keymanagerUrl}/validator/${publicKey}/feerecipient`,request) + } + + deleteFeeRecipient(publicKey:string){ + return this.http.delete(`${this.keymanagerUrl}/validator/${publicKey}/feerecipient`) + } + balances( publicKeys: string[], pageIndex: number, diff --git a/src/app/modules/wallet/components/accounts-table/accounts-table.component.html b/src/app/modules/wallet/components/accounts-table/accounts-table.component.html index 46aa3b3a..34ca83fc 100644 --- a/src/app/modules/wallet/components/accounts-table/accounts-table.component.html +++ b/src/app/modules/wallet/components/accounts-table/accounts-table.component.html @@ -89,7 +89,7 @@
+ +
+ + + {{publicKey | base64tohex | slice:0:16}}... + + +
+
+
+ + + DELETE FEE RECIPIENT + + + SET NEW FEE RECIPIENT + + +
+
+ + Fee Recipient + + + + must be a checksummed eth address + + + + Eth Address is required + + +
+ Type in the words "agree" to confirm update of your fee recipient. +

WARN: fee recipient update will be reflected until start of epoch

+
+ + Confirmation Text + + + + Type 'agree' if you want to delete the selected keys + + + + Confirmation text is required + + + You must type 'agree' + + +
+ +
+ + +
+
+ \ No newline at end of file diff --git a/src/app/modules/wallet/components/fee-recipient/fee-recipient-edit.component.spec.ts b/src/app/modules/wallet/components/fee-recipient/fee-recipient-edit.component.spec.ts new file mode 100644 index 00000000..7d1f1a98 --- /dev/null +++ b/src/app/modules/wallet/components/fee-recipient/fee-recipient-edit.component.spec.ts @@ -0,0 +1,52 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { EditFeeRecipientComponent } from './fee-recipient-edit.component'; +import { MatSnackBar } from '@angular/material/snack-bar'; +import { MockService } from 'ng-mocks'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { mockPublicKeys } from '../../../core/mocks/index'; +import { SharedModule } from '../../../shared/shared.module'; +import { ValidatorService } from 'src/app/modules/core/services/validator.service'; + +describe('EditFeeRecipientComponent', () => { + let component: EditFeeRecipientComponent; + let fixture: ComponentFixture; + let validatorService: ValidatorService + let snackBar: MatSnackBar; + + beforeEach(waitForAsync(() => { + validatorService = MockService(ValidatorService); + snackBar = MockService(MatSnackBar); + const mockDialogDef = { + close: () => {}, + }; + const pubKeys = mockPublicKeys; + TestBed.configureTestingModule({ + declarations: [EditFeeRecipientComponent], + imports: [ + SharedModule.forRoot(), + FormsModule, + ReactiveFormsModule, + BrowserAnimationsModule, + ], + providers: [ + { provide: MAT_DIALOG_DATA, useValue: pubKeys }, + { provide: MatDialogRef, useValue: mockDialogDef }, + { provide: ValidatorService, useValue: validatorService }, + ], + }).compileComponents(); + validatorService = TestBed.inject(ValidatorService); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(EditFeeRecipientComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/modules/wallet/components/fee-recipient/fee-recipient-edit.component.ts b/src/app/modules/wallet/components/fee-recipient/fee-recipient-edit.component.ts new file mode 100644 index 00000000..4599d4f6 --- /dev/null +++ b/src/app/modules/wallet/components/fee-recipient/fee-recipient-edit.component.ts @@ -0,0 +1,84 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import * as FileSaver from 'file-saver'; +import { ToastrService } from 'ngx-toastr'; +import { throwError } from 'rxjs'; +import { tap } from 'rxjs/operators'; +import { ValidatorService } from 'src/app/modules/core/services/validator.service'; +import { base64ToHex } from 'src/app/modules/core/utils/hex-util'; +import { inherits } from 'util'; +import { UtilityValidator } from '../../../onboarding/validators/utility.validator'; +import { TableData } from '../accounts-table/accounts-table.component'; + +@Component({ + selector: 'fee-recipient-edit', + templateUrl: './fee-recipient-edit.component.html', +}) +export class EditFeeRecipientComponent implements OnInit { + publicKey: string; + addPubkeyControl = this.formBuilder.control(null,[ Validators.pattern('^(0x){1}[A-Fa-f0-9]{96}$')]); + confirmGroup: FormGroup = this.formBuilder.group({ + options: ['SET'], + feerecipient: ['',[Validators.required,Validators.pattern('^0x[a-fA-F0-9]{40}$')]], + confirmation: ['', [Validators.required, UtilityValidator.MustBe('agree')]], + }); + + constructor( + private ref: MatDialogRef, + private validatorService: ValidatorService, + private formBuilder: FormBuilder, + private toastr: ToastrService, + @Inject(MAT_DIALOG_DATA) private data: {publickey:string,ethaddress:string} + ) { + this.publicKey = this.data.publickey; + } + + ngOnInit(){ + this.confirmGroup.controls['options'].valueChanges.subscribe(value=>{ + if(value === 'DELETE'){ + this.confirmGroup.controls['feerecipient'].reset(); + this.confirmGroup.controls['feerecipient'].removeValidators([Validators.required,Validators.pattern('/^0x[a-fA-F0-9]{40}$/')]); + this.confirmGroup.controls['feerecipient'].updateValueAndValidity(); + } + if(value === 'SET'){ + this.confirmGroup.controls['feerecipient'].addValidators([Validators.required,Validators.pattern('/^0x[a-fA-F0-9]{40}$/')]); + this.confirmGroup.controls['feerecipient'].updateValueAndValidity(); + } + }); + } + + cancel(): void { + this.ref.close(); + } + + + confirm(): void { + let updateFeeRecipient$; + switch(this.confirmGroup.controls['options'].value) { + case 'DELETE': + updateFeeRecipient$ = this.validatorService.deleteFeeRecipient(base64ToHex(this.publicKey)); + break; + case 'SET': + updateFeeRecipient$ = this.validatorService.setFeeRecipient(base64ToHex(this.publicKey),{ethaddress:this.confirmGroup.controls['feerecipient'].value}); + break; + } + if(updateFeeRecipient$) { + updateFeeRecipient$.subscribe(()=>{ + this.toastr.success( + `${base64ToHex(this.publicKey).substring(0, 10)}... updated fee recipient`, + ); + this.ref.close(); + }, (error) => { + this.toastr.error( + `${base64ToHex(this.publicKey).substring(0, 10)}... failed to update fee recipient`,error,{ + timeOut: 20000, + }); + this.ref.close(); + }); + } + + } + + +} diff --git a/src/app/modules/wallet/components/icon-trigger-select/icon-trigger-select.component.ts b/src/app/modules/wallet/components/icon-trigger-select/icon-trigger-select.component.ts index 28dbdd1a..2066aafe 100644 --- a/src/app/modules/wallet/components/icon-trigger-select/icon-trigger-select.component.ts +++ b/src/app/modules/wallet/components/icon-trigger-select/icon-trigger-select.component.ts @@ -1,11 +1,12 @@ import { Component, Input } from '@angular/core'; +import { TableData } from '../accounts-table/accounts-table.component'; export interface MenuItem { disabled?: boolean; danger?: boolean; name: string; icon: string; - action: (publicKey: string) => void; + action: (row: TableData) => void; } @Component({ @@ -13,7 +14,7 @@ export interface MenuItem { templateUrl: './icon-trigger-select.component.html', }) export class IconTriggerSelectComponent { - @Input() data: string | null = null; + @Input() data: TableData | null = null; @Input() icon: string | null = null; @Input() menuItems: MenuItem[] | null = null; constructor() { } diff --git a/src/app/modules/wallet/wallet.module.ts b/src/app/modules/wallet/wallet.module.ts index 6c8be474..bfec6f8f 100644 --- a/src/app/modules/wallet/wallet.module.ts +++ b/src/app/modules/wallet/wallet.module.ts @@ -21,6 +21,7 @@ import { AccountDeleteComponent } from './components/account-delete/account-dele import { WalletComponent } from './wallet.component'; import { AccountsFormSelectionComponent } from './components/accounts-form-selection/accounts-form-selection.component'; import { AccountBackupComponent } from './pages/account-backup/account-backup.component'; +import { EditFeeRecipientComponent } from './components/fee-recipient/fee-recipient-edit.component'; @NgModule({ declarations: [ @@ -39,6 +40,7 @@ import { AccountBackupComponent } from './pages/account-backup/account-backup.co WalletComponent, AccountBackupComponent, AccountsFormSelectionComponent, + EditFeeRecipientComponent ], imports: [ CommonModule, diff --git a/src/app/proto/validator/accounts/v2/web_api_keymanager-api.ts b/src/app/proto/validator/accounts/v2/web_api_keymanager-api.ts index c64fefa6..b0b5cc71 100644 --- a/src/app/proto/validator/accounts/v2/web_api_keymanager-api.ts +++ b/src/app/proto/validator/accounts/v2/web_api_keymanager-api.ts @@ -25,4 +25,8 @@ export interface ListFeeRecipientResponse { export interface FeeRecipientData { pubkey: string, ethAddress: string +} + +export interface SetFeeRecipientRequest { + ethaddress: string } \ No newline at end of file From 5cc47f3c803106d22c8be17d787086f8af2e2aef Mon Sep 17 00:00:00 2001 From: James He Date: Mon, 27 Jun 2022 11:51:23 -0500 Subject: [PATCH 3/6] adding refresh on fee recipient update --- .../core/services/validator.service.ts | 4 +- .../fee-recipient-edit.component.ts | 6 +- .../pages/accounts/accounts.component.ts | 81 ++++++++++++------- 3 files changed, 54 insertions(+), 37 deletions(-) diff --git a/src/app/modules/core/services/validator.service.ts b/src/app/modules/core/services/validator.service.ts index 0a1236bb..8c2fc8bd 100644 --- a/src/app/modules/core/services/validator.service.ts +++ b/src/app/modules/core/services/validator.service.ts @@ -1,6 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Observable, zip } from 'rxjs'; +import { Observable, Subject, zip } from 'rxjs'; import { map, share, switchMap } from 'rxjs/operators'; import { ValidatorBalances, Validators, ValidatorSummaryResponse @@ -27,6 +27,8 @@ export class ValidatorService { private apiUrl = this.environmenter.env.validatorEndpoint; private keymanagerUrl = this.environmenter.env.keymanagerEndpoint; + refreshTableDataTrigger$ = new Subject(); + version$: Observable = this.http.get(`${this.apiUrl}/health/version`).pipe( share(), ); diff --git a/src/app/modules/wallet/components/fee-recipient/fee-recipient-edit.component.ts b/src/app/modules/wallet/components/fee-recipient/fee-recipient-edit.component.ts index 4599d4f6..fef03a0f 100644 --- a/src/app/modules/wallet/components/fee-recipient/fee-recipient-edit.component.ts +++ b/src/app/modules/wallet/components/fee-recipient/fee-recipient-edit.component.ts @@ -1,15 +1,10 @@ import { Component, Inject, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; -import * as FileSaver from 'file-saver'; import { ToastrService } from 'ngx-toastr'; -import { throwError } from 'rxjs'; -import { tap } from 'rxjs/operators'; import { ValidatorService } from 'src/app/modules/core/services/validator.service'; import { base64ToHex } from 'src/app/modules/core/utils/hex-util'; -import { inherits } from 'util'; import { UtilityValidator } from '../../../onboarding/validators/utility.validator'; -import { TableData } from '../accounts-table/accounts-table.component'; @Component({ selector: 'fee-recipient-edit', @@ -68,6 +63,7 @@ export class EditFeeRecipientComponent implements OnInit { this.toastr.success( `${base64ToHex(this.publicKey).substring(0, 10)}... updated fee recipient`, ); + this.validatorService.refreshTableDataTrigger$.next(true); this.ref.close(); }, (error) => { this.toastr.error( diff --git a/src/app/modules/wallet/pages/accounts/accounts.component.ts b/src/app/modules/wallet/pages/accounts/accounts.component.ts index c0383c65..e7167fe2 100644 --- a/src/app/modules/wallet/pages/accounts/accounts.component.ts +++ b/src/app/modules/wallet/pages/accounts/accounts.component.ts @@ -65,36 +65,43 @@ export class AccountsComponent extends BaseComponent implements OnInit { selection = new SelectionModel(true /* allow multiselect */, []); tableDataSource$: Observable< MatTableDataSource - > = this.pageChanged$.pipe( - // Debounce to prevent spamming the paginator component. - tap(() => (this.loading = true)), - debounceTime(300), - switchMap((ev: PageEvent) => { - return this.walletService.accounts(ev.pageIndex, ev.pageSize).pipe( - // Extract the validating public keys. - zipMap((accs) => - accs.accounts?.map((account) => account.validating_public_key) - ), - switchMap(([accountsResponse, pubKeys]) =>{ - return zip( - this.validatorService.validatorList(pubKeys, 0, pubKeys.length), - this.validatorService.balances(pubKeys, 0, pubKeys.length), - zip(this.feeRecipients$(pubKeys)) - ).pipe( - map(([validators, balances,feeRecipients]) =>{ - // Transform the data into a pretty format for our table. - return this.transformTableData(accountsResponse, validators, balances, feeRecipients.map(f=>f.data)) - }) - ) - }), - ); - }), - share(), // Share the observable across all subscribers. - tap(() => (this.loading = false)), - catchError((err) => { - return throwError(err); - }) - ); + > = this.getTableData$(); + + getTableData$():Observable< + MatTableDataSource +> { + return this.pageChanged$.pipe( + // Debounce to prevent spamming the paginator component. + tap(() => (this.loading = true)), + debounceTime(300), + switchMap((ev: PageEvent) => { + return this.walletService.accounts(ev.pageIndex, ev.pageSize).pipe( + // Extract the validating public keys. + zipMap((accs) => + accs.accounts?.map((account) => account.validating_public_key) + ), + switchMap(([accountsResponse, pubKeys]) =>{ + return zip( + this.validatorService.validatorList(pubKeys, 0, pubKeys.length), + this.validatorService.balances(pubKeys, 0, pubKeys.length), + zip(this.feeRecipients$(pubKeys)) + ).pipe( + map(([validators, balances,feeRecipients]) =>{ + // Transform the data into a pretty format for our table. + return this.transformTableData(accountsResponse, validators, balances, feeRecipients.map(f=>f.data)) + }) + ) + }), + ); + }), + share(), // Share the observable across all subscribers. + tap(() => (this.loading = false)), + catchError((err) => { + return throwError(err); + }) + ); + } + ngOnInit(): void { this.userService.user$ .pipe( @@ -106,6 +113,12 @@ export class AccountsComponent extends BaseComponent implements OnInit { }) ) .subscribe(); + + this.validatorService.refreshTableDataTrigger$.subscribe(res=>{ + if(res){ + this.refreshData(); + } + }); } applySearchFilter( event: Event, @@ -122,6 +135,12 @@ export class AccountsComponent extends BaseComponent implements OnInit { handlePageEvent(event: PageEvent): void { this.userService.changeAccountListPerPage(event.pageSize); this.pageChanged$.next(event); + this.refreshData(); + } + + refreshData(): void{ + console.log("refresh triggered"); + this.tableDataSource$ = this.getTableData$(); } private feeRecipients$(pubkeys:string[]):Observable[]{ @@ -173,7 +192,7 @@ export class AccountsComponent extends BaseComponent implements OnInit { accountName: acc?.account_name, index: val?.index ? val.index : 'n/a', publicKey: acc.validating_public_key, - balance: bal, + balance: bal+Math.floor(Math.random() *10), effectiveBalance: effectiveBalance.toString(), status, activationEpoch: val?.validator?.activation_epoch, From e903212ce9a44df31b0f0ddc5c359acd014f5b9a Mon Sep 17 00:00:00 2001 From: James He Date: Mon, 27 Jun 2022 15:15:13 -0500 Subject: [PATCH 4/6] removing temp balance --- src/app/modules/wallet/pages/accounts/accounts.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/modules/wallet/pages/accounts/accounts.component.ts b/src/app/modules/wallet/pages/accounts/accounts.component.ts index e7167fe2..516f9a0b 100644 --- a/src/app/modules/wallet/pages/accounts/accounts.component.ts +++ b/src/app/modules/wallet/pages/accounts/accounts.component.ts @@ -192,7 +192,7 @@ export class AccountsComponent extends BaseComponent implements OnInit { accountName: acc?.account_name, index: val?.index ? val.index : 'n/a', publicKey: acc.validating_public_key, - balance: bal+Math.floor(Math.random() *10), + balance: bal, effectiveBalance: effectiveBalance.toString(), status, activationEpoch: val?.validator?.activation_epoch, From 42a4205cdbe81a809fae94249123327adc484e33 Mon Sep 17 00:00:00 2001 From: James He Date: Wed, 29 Jun 2022 13:40:07 -0500 Subject: [PATCH 5/6] fixing unit test and adjusting table based on real testing --- src/app/modules/core/mocks/index.ts | 2 +- src/app/modules/core/services/validator.service.ts | 3 ++- .../validator-performance-list.component.ts | 2 +- .../accounts-table/accounts-table.component.html | 6 +++++- .../components/accounts-table/accounts-table.component.ts | 7 +++++++ .../wallet/pages/accounts/accounts.component.spec.ts | 6 ++++-- .../modules/wallet/pages/accounts/accounts.component.ts | 3 +-- .../proto/validator/accounts/v2/web_api_keymanager-api.ts | 2 +- 8 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/app/modules/core/mocks/index.ts b/src/app/modules/core/mocks/index.ts index ba6e386d..d537deac 100644 --- a/src/app/modules/core/mocks/index.ts +++ b/src/app/modules/core/mocks/index.ts @@ -588,7 +588,7 @@ export const KeymanagerAPIMocks = { 'GET':{ data:{ pubkey:"0xaadaf653799229200378369ee7d6d9fdbdcdc2788143ed44f1ad5f2367c735e83a37c5bb80d7fb917de73a61bbcf00c4", - ethAddress: "0xasdfsadfsfsfsdfsadfsdafsadfsadsdafasdf" + ethaddress: "0xasdfsadfsfsfsdfsadfsdafsadfsadsdafasdf" } } as ListFeeRecipientResponse, 'POST':{}, diff --git a/src/app/modules/core/services/validator.service.ts b/src/app/modules/core/services/validator.service.ts index 8c2fc8bd..fd668905 100644 --- a/src/app/modules/core/services/validator.service.ts +++ b/src/app/modules/core/services/validator.service.ts @@ -7,6 +7,7 @@ import { } from 'src/app/proto/eth/v1alpha1/beacon_chain'; import { VersionResponse } from 'src/app/proto/validator/accounts/v2/web_api'; import { ListFeeRecipientResponse, SetFeeRecipientRequest } from 'src/app/proto/validator/accounts/v2/web_api_keymanager-api'; +import { base64ToHex } from '../utils/hex-util'; import { EnvironmenterService } from './environmenter.service'; import { WalletService } from './wallet.service'; @@ -62,7 +63,7 @@ export class ValidatorService { } getFeeRecipient(publicKey:string): Observable< ListFeeRecipientResponse>{ - return this.http.get(`${this.keymanagerUrl}/validator/${publicKey}/feerecipient`) + return this.http.get(`${this.keymanagerUrl}/validator/${base64ToHex(publicKey)}/feerecipient`) } setFeeRecipient(publicKey:string,request: SetFeeRecipientRequest){ diff --git a/src/app/modules/dashboard/components/validator-performance-list/validator-performance-list.component.ts b/src/app/modules/dashboard/components/validator-performance-list/validator-performance-list.component.ts index 0512180b..13559459 100644 --- a/src/app/modules/dashboard/components/validator-performance-list/validator-performance-list.component.ts +++ b/src/app/modules/dashboard/components/validator-performance-list/validator-performance-list.component.ts @@ -93,7 +93,7 @@ export class ValidatorPerformanceListComponent res.forEach((r)=>{ let item = list.find((obj)=>base64ToHex(obj.publicKey) === r.data.pubkey) if(item){ - item.feeRecipient = r.data.ethAddress + item.feeRecipient = r.data.ethaddress } }) return list; diff --git a/src/app/modules/wallet/components/accounts-table/accounts-table.component.html b/src/app/modules/wallet/components/accounts-table/accounts-table.component.html index 34ca83fc..c91f6240 100644 --- a/src/app/modules/wallet/components/accounts-table/accounts-table.component.html +++ b/src/app/modules/wallet/components/accounts-table/accounts-table.component.html @@ -39,7 +39,11 @@ Fee Recipient - {{row.feeRecipient | slice:0:8 }}... + {{row.feeRecipient | slice:0:8 }}... ETH Balance diff --git a/src/app/modules/wallet/components/accounts-table/accounts-table.component.ts b/src/app/modules/wallet/components/accounts-table/accounts-table.component.ts index eb237558..fc59f43c 100644 --- a/src/app/modules/wallet/components/accounts-table/accounts-table.component.ts +++ b/src/app/modules/wallet/components/accounts-table/accounts-table.component.ts @@ -108,6 +108,13 @@ export class AccountsTableComponent implements AfterViewInit,OnChanges { }); } + copyFeeRecipientToClipboard(feeRecipient: string):void { + this.clipboard.copy(feeRecipient); + this.snackBar.open(`Copied ${feeRecipient.slice(0, 16)}... to Clipboard`, 'Close', { + duration: 4000, + }); + } + formatStatusColor(validatorStatus: string): string { switch (validatorStatus.trim().toLowerCase()) { case 'active': diff --git a/src/app/modules/wallet/pages/accounts/accounts.component.spec.ts b/src/app/modules/wallet/pages/accounts/accounts.component.spec.ts index ac1c23a6..033635c4 100644 --- a/src/app/modules/wallet/pages/accounts/accounts.component.spec.ts +++ b/src/app/modules/wallet/pages/accounts/accounts.component.spec.ts @@ -1,7 +1,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { MockComponent, MockService } from 'ng-mocks'; -import { of } from 'rxjs'; +import { of, Subject } from 'rxjs'; import { ListAccountsResponse, Account } from 'src/app/proto/validator/accounts/v2/web_api'; import { ValidatorService } from 'src/app/modules/core/services/validator.service'; @@ -17,7 +17,7 @@ describe('AccountsComponent', () => { let component: AccountsComponent; let fixture: ComponentFixture; let service: WalletService = MockService(WalletService); - const valService: ValidatorService = MockService(ValidatorService); + let valService: ValidatorService = MockService(ValidatorService); service.accounts = () => { return of({ accounts: [{ @@ -28,6 +28,7 @@ describe('AccountsComponent', () => { }] as Account[], } as ListAccountsResponse); }; + valService.refreshTableDataTrigger$ = new Subject(); beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ @@ -47,6 +48,7 @@ describe('AccountsComponent', () => { }) .compileComponents(); service = TestBed.inject(WalletService); + valService = TestBed.inject(ValidatorService); })); beforeEach(() => { diff --git a/src/app/modules/wallet/pages/accounts/accounts.component.ts b/src/app/modules/wallet/pages/accounts/accounts.component.ts index 516f9a0b..c0a5f88f 100644 --- a/src/app/modules/wallet/pages/accounts/accounts.component.ts +++ b/src/app/modules/wallet/pages/accounts/accounts.component.ts @@ -160,7 +160,6 @@ export class AccountsComponent extends BaseComponent implements OnInit { ); let feeRecipient = feeRecipients.find(data=> data.pubkey === base64ToHex(acc.validating_public_key)); - if (!val) { val = { index: 0, @@ -199,7 +198,7 @@ export class AccountsComponent extends BaseComponent implements OnInit { exitEpoch: val?.validator?.exit_epoch, lowBalance: effectiveBalance.toNumber() < 32, options: acc.validating_public_key, - feeRecipient: feeRecipient?.ethAddress , + feeRecipient: feeRecipient?.ethaddress , } as TableData; }); const dataSource = new MatTableDataSource(tableData); diff --git a/src/app/proto/validator/accounts/v2/web_api_keymanager-api.ts b/src/app/proto/validator/accounts/v2/web_api_keymanager-api.ts index b0b5cc71..e54c82c9 100644 --- a/src/app/proto/validator/accounts/v2/web_api_keymanager-api.ts +++ b/src/app/proto/validator/accounts/v2/web_api_keymanager-api.ts @@ -24,7 +24,7 @@ export interface ListFeeRecipientResponse { export interface FeeRecipientData { pubkey: string, - ethAddress: string + ethaddress: string } export interface SetFeeRecipientRequest { From d4ab6d43d13cfc96200a1b07c6a44617c2ba8574 Mon Sep 17 00:00:00 2001 From: James He Date: Wed, 29 Jun 2022 13:56:19 -0500 Subject: [PATCH 6/6] fixing linting issue --- .../components/fee-recipient/fee-recipient-edit.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/modules/wallet/components/fee-recipient/fee-recipient-edit.component.ts b/src/app/modules/wallet/components/fee-recipient/fee-recipient-edit.component.ts index fef03a0f..c7573d4b 100644 --- a/src/app/modules/wallet/components/fee-recipient/fee-recipient-edit.component.ts +++ b/src/app/modules/wallet/components/fee-recipient/fee-recipient-edit.component.ts @@ -7,7 +7,7 @@ import { base64ToHex } from 'src/app/modules/core/utils/hex-util'; import { UtilityValidator } from '../../../onboarding/validators/utility.validator'; @Component({ - selector: 'fee-recipient-edit', + selector: 'app-fee-recipient-edit', templateUrl: './fee-recipient-edit.component.html', }) export class EditFeeRecipientComponent implements OnInit {