From 38eafe98567d975d0673ef201d0f715d816a9adb Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Wed, 25 Oct 2023 14:09:43 +0200 Subject: [PATCH] search for validators via withdrawal credential or address --- android/app/build.gradle | 4 +- src/app/requests/requests.ts | 19 ++++++-- .../tab-validators/tab-validators.page.html | 11 +++-- src/app/tab-validators/tab-validators.page.ts | 7 +-- src/app/utils/ValidatorUtils.ts | 46 +++++++++++++------ 5 files changed, 62 insertions(+), 25 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index d0625d38..23aa535b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -8,8 +8,8 @@ android { namespace "in.beaconcha.mobile" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 103 - versionName "4.4.0" + versionCode 105 + versionName "4.5.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/src/app/requests/requests.ts b/src/app/requests/requests.ts index 6d201380..e06b5b83 100644 --- a/src/app/requests/requests.ts +++ b/src/app/requests/requests.ts @@ -153,7 +153,6 @@ export interface CoinbaseExchangeResponse { export interface ETH1ValidatorResponse { publickey: string - valid_signature: boolean validatorindex: number } @@ -354,18 +353,32 @@ export class ValidatorRequest extends APIRequest { } } -export class ValidatorETH1Request extends APIRequest { +export class ValidatorViaDepositAddress extends APIRequest { resource = 'validator/eth1/' method = Method.GET /** - * @param validator Index or PubKey + * @param ethAddress Address + */ + constructor(ethAddress: string) { + super() + this.resource += ethAddress.replace(/\s/g, '') + } +} + +export class ValidatorViaWithdrawalAddress extends APIRequest { + resource = 'validator/withdrawalCredentials/' + method = Method.GET + + /** + * @param ethAddress Address or Withdrawal Credential */ constructor(ethAddress: string) { super() this.resource += ethAddress.replace(/\s/g, '') } } + export class BlockProducedByRequest extends APIRequest { resource = 'execution/' method = Method.GET diff --git a/src/app/tab-validators/tab-validators.page.html b/src/app/tab-validators/tab-validators.page.html index 0a533e4a..bdf10173 100644 --- a/src/app/tab-validators/tab-validators.page.html +++ b/src/app/tab-validators/tab-validators.page.html @@ -45,7 +45,7 @@ (search)="searchEvent($event)" (ionClear)="cancelSearch()" (ionCancel)="cancelSearch()" - placeholder="Public Key / Index / ETH Address" + placeholder="Public Key / Index / Address" animated> @@ -102,13 +102,18 @@

Nothing found

- We couldn't find the validators you are looking for. Try searching by Index, Public Key or ETH address. + We couldn't find the validators you are looking for. Try searching by index, public key, deposit / withdrawal address or withdrawal + credential.

Add Validators

- You can add your validators by searching for a public key, validator index or your ETH address. + + You can add your validators by searching for a public key, validator index, your deposit / withdrawal address or withdrawal credential. +
diff --git a/src/app/tab-validators/tab-validators.page.ts b/src/app/tab-validators/tab-validators.page.ts index 909f9a16..b14223b2 100644 --- a/src/app/tab-validators/tab-validators.page.ts +++ b/src/app/tab-validators/tab-validators.page.ts @@ -256,8 +256,9 @@ export class Tab2Page { this.searchResultMode = true this.loading = true const isETH1Address = searchString.startsWith('0x') && searchString.length == 42 + const isWithdrawalCredential = searchString.startsWith('0x') && searchString.length == 66 - if (isETH1Address) await this.searchETH1(searchString) + if (isETH1Address || isWithdrawalCredential) await this.searchETH1(searchString) else await this.searchByPubKeyOrIndex(searchString) // this.loading = false would be preferable here but somehow the first time it is called the promises resolve instantly without waiting @@ -284,12 +285,12 @@ export class Tab2Page { private async searchETH1(target) { this.dataSource.setLoadFrom(() => { return this.validatorUtils - .searchValidatorsViaETH1(target) + .searchValidatorsViaETHAddress(target) .catch(async (error) => { if (error && error.message && error.message.indexOf('only a maximum of') > 0) { console.log('SET reachedMaxValidators to true') this.reachedMaxValidators = true - return this.validatorUtils.searchValidatorsViaETH1(target, this.currentPackageMaxValidators - 1) + return this.validatorUtils.searchValidatorsViaETHAddress(target, this.currentPackageMaxValidators - 1) } return [] }) diff --git a/src/app/utils/ValidatorUtils.ts b/src/app/utils/ValidatorUtils.ts index a7b82881..aa20b94d 100644 --- a/src/app/utils/ValidatorUtils.ts +++ b/src/app/utils/ValidatorUtils.ts @@ -27,7 +27,6 @@ import { AttestationPerformanceResponse, ValidatorRequest, ValidatorResponse, - ValidatorETH1Request, GetMyValidatorsRequest, MyValidatorResponse, DashboardRequest, @@ -38,6 +37,8 @@ import { ETH1ValidatorResponse, SyncCommitteesStatisticsResponse, ProposalLuckResponse, + ValidatorViaDepositAddress, + ValidatorViaWithdrawalAddress, } from '../requests/requests' import { MerchantUtils } from './MerchantUtils' import BigNumber from 'bignumber.js' @@ -492,20 +493,37 @@ export class ValidatorUtils { } } - async getRemoteValidatorViaETH1(arg: string, enforceMax = -1): Promise { + async getRemoteValidatorViaETHAddress(arg: string, enforceMax = -1): Promise { if (!arg) return [] - const request = new ValidatorETH1Request(arg) - const response = await this.api.execute(request) - - if (request.wasSuccessful(response)) { - const eth1ValidatorList = request.parse(response) - const queryString = getValidatorQueryString(eth1ValidatorList, 2000, enforceMax) - - return await this.getDashboardDataValidators(MEMORY, queryString) - } else { - return this.apiStatusHandler(response) + const viaDeposit = new ValidatorViaDepositAddress(arg) + const viaDepositPromise = this.api.execute(viaDeposit) + + const viaWithdrawal = new ValidatorViaWithdrawalAddress(arg) + const viaWithdrawalPromise = this.api.execute(viaWithdrawal) + + const response = await Promise.all([viaDepositPromise, viaWithdrawalPromise]) + let result: ETH1ValidatorResponse[] = [] + + for (const resp of response) { + if (viaDeposit.wasSuccessful(resp)) { + // since both results are identical we can use any of the requests for parsing + const temp = viaDeposit.parse(resp) + if (temp) { + if (result.length > 0) { + result = Array.from(new Map([...result, ...temp].map((item) => [item.validatorindex, item])).values()) + } else { + result = temp + } + } + } else { + return this.apiStatusHandler(response) + } } + + const queryString = getValidatorQueryString(result, 2000, enforceMax) + return await this.getDashboardDataValidators(MEMORY, queryString) } + private async apiStatusHandler(response) { if (response && response.data && response.data.status) { return Promise.reject(new Error(response.data.status)) @@ -564,8 +582,8 @@ export class ValidatorUtils { return result } - async searchValidatorsViaETH1(search: string, enforceMax = -1): Promise { - const result = await this.getRemoteValidatorViaETH1(search, enforceMax) + async searchValidatorsViaETHAddress(search: string, enforceMax = -1): Promise { + const result = await this.getRemoteValidatorViaETHAddress(search, enforceMax) if (result == null) return [] return result }