From b6903563bfb282eb6b328b0b702780243be8f358 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Mon, 16 Oct 2023 14:02:38 +0200 Subject: [PATCH 01/53] refactoring for gnosis, fix rpl collateral --- .eslintrc | 8 +- src/app/app.module.ts | 9 ++- src/app/components/block/block.component.ts | 2 +- .../dashboard/dashboard.component.html | 2 +- .../dashboard/dashboard.component.ts | 17 ++--- src/app/components/help/help.component.html | 29 ++++++-- src/app/components/help/help.component.ts | 9 ++- src/app/models/StorageTypes.ts | 8 ++ .../pages/block-detail/block-detail.page.ts | 9 +-- .../notifications/notifications.page.html | 2 +- .../pages/notifications/notifications.page.ts | 2 +- src/app/pages/subscribe/subscribe.page.ts | 8 +- src/app/services/api.service.ts | 73 +++++++++++-------- src/app/services/storage.service.ts | 2 +- src/app/services/sync.service.ts | 10 +-- src/app/services/unitconv.service.ts | 6 ++ src/app/tab-preferences/notification-base.ts | 8 +- .../tab-preferences/tab-preferences.page.html | 9 ++- .../tab-preferences/tab-preferences.page.ts | 2 +- src/app/utils/CacheModule.ts | 8 +- src/app/utils/MerchantUtils.ts | 4 +- src/app/utils/NetworkData.ts | 27 ++++++- src/app/utils/OAuthUtils.ts | 4 +- src/app/utils/ValidatorUtils.ts | 22 ++---- 24 files changed, 181 insertions(+), 99 deletions(-) diff --git a/.eslintrc b/.eslintrc index 11643ef3..829b7b59 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,6 +1,9 @@ { "root": true, "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": "./tsconfig.json" + }, "plugins": [ "@typescript-eslint" ], @@ -8,5 +11,8 @@ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended" - ] + ], + "rules": { + "@typescript-eslint/await-thenable": "error" + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 23479878..59f0ae2a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -18,7 +18,7 @@ * // along with Beaconchain Dashboard. If not, see . */ -import { NgModule } from '@angular/core' +import { APP_INITIALIZER, NgModule } from '@angular/core' import { BrowserModule, HammerModule } from '@angular/platform-browser' import { RouteReuseStrategy } from '@angular/router' @@ -30,6 +30,7 @@ import { PipesModule } from './pipes/pipes.module' import { HammerGestureConfig, HAMMER_GESTURE_CONFIG } from '@angular/platform-browser' import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import 'hammerjs' +import { ApiService, initializeApiService } from './services/api.service' // eslint-disable-next-line @typescript-eslint/no-explicit-any declare let Hammer: any @@ -59,6 +60,12 @@ export class MyHammerConfig extends HammerGestureConfig { provide: HAMMER_GESTURE_CONFIG, useClass: MyHammerConfig, }, + { + provide: APP_INITIALIZER, + useFactory: initializeApiService, + deps: [ApiService], + multi: true, + }, ], bootstrap: [AppComponent], }) diff --git a/src/app/components/block/block.component.ts b/src/app/components/block/block.component.ts index bd811873..365fd352 100644 --- a/src/app/components/block/block.component.ts +++ b/src/app/components/block/block.component.ts @@ -31,7 +31,7 @@ export class BlockComponent implements OnInit { async ngOnChanges() { this.imgData = this.getBlockies() this.timestamp = this.block.timestamp * 1000 - this.producerReward = await await this.blockUtils.getBlockRewardWithShare(this.block) + this.producerReward = await this.blockUtils.getBlockRewardWithShare(this.block) this.resolvedName = null this.feeRecipient = this.block.feeRecipient diff --git a/src/app/components/dashboard/dashboard.component.html b/src/app/components/dashboard/dashboard.component.html index 428281d8..7fd3a232 100644 --- a/src/app/components/dashboard/dashboard.component.html +++ b/src/app/components/dashboard/dashboard.component.html @@ -877,7 +877,7 @@ - View on beaconcha.in + View on {{ api.getHostName() }}
diff --git a/src/app/components/dashboard/dashboard.component.ts b/src/app/components/dashboard/dashboard.component.ts index 7d1df8bc..c33b59e3 100644 --- a/src/app/components/dashboard/dashboard.component.ts +++ b/src/app/components/dashboard/dashboard.component.ts @@ -161,7 +161,7 @@ export class DashboardComponent implements OnInit { this.drawProposalChart() }, 500) - this.beaconChainUrl = await this.getBaseBrowserUrl() + this.beaconChainUrl = this.api.getBaseUrl() await Promise.all([ this.updateRplDisplay(), @@ -438,7 +438,11 @@ export class DashboardComponent implements OnInit { updateRplDisplay() { if (this.rplState == '%') { - this.rplDisplay = this.data.rocketpool.currentRpl.dividedBy(this.data.rocketpool.maxRpl).multipliedBy(new BigNumber(150)).decimalPlaces(1) + const rplPrice = this.unit.getRPLPrice() + const currentETH = this.data.rocketpool.currentRpl.multipliedBy(rplPrice) + const minETH = this.data.rocketpool.minRpl.multipliedBy(rplPrice).multipliedBy(10) // since collateral is 10% of borrowed eth, multiply by 10 to get to the borrowed eth amount + + this.rplDisplay = currentETH.dividedBy(minETH).multipliedBy(100).decimalPlaces(1).toNumber() } else { this.rplDisplay = this.data.rocketpool.currentRpl } @@ -866,16 +870,11 @@ export class DashboardComponent implements OnInit { async getBrowserURL(): Promise { if (this.data.foreignValidator) { - return (await this.getBaseBrowserUrl()) + '/validator/' + this.data.foreignValidatorItem.pubkey + return this.api.getBaseUrl() + '/validator/' + this.data.foreignValidatorItem.pubkey } else { - return (await this.getBaseBrowserUrl()) + '/dashboard?validators=' + this.data.lazyChartValidators + return this.api.getBaseUrl() + '/dashboard?validators=' + this.data.lazyChartValidators } } - - async getBaseBrowserUrl() { - const net = (await this.api.networkConfig).net - return 'https://' + net + 'beaconcha.in' - } } function getRandomInt(max) { diff --git a/src/app/components/help/help.component.html b/src/app/components/help/help.component.html index 32804afe..b683ec35 100644 --- a/src/app/components/help/help.component.html +++ b/src/app/components/help/help.component.html @@ -26,7 +26,7 @@ - Or login with beaconcha.in + Or login with {{ api.getHostName() }} @@ -34,7 +34,7 @@ Guides & Help - + Staking Guide & FAQ @@ -61,11 +61,23 @@ + + + + Node Guide & FAQ + + + + + Enable Withdrawals Guide + + + How to set up a Validator - + Setup Guide for Lighthouse @@ -92,13 +104,20 @@ + + + + Interactive Setup Guide + + + Useful Links - + - beaconcha.in Block Explorer + {{ api.getHostName() }} Block Explorer diff --git a/src/app/components/help/help.component.ts b/src/app/components/help/help.component.ts index 5cda2daf..af50f02c 100644 --- a/src/app/components/help/help.component.ts +++ b/src/app/components/help/help.component.ts @@ -25,6 +25,7 @@ import { OAuthUtils } from 'src/app/utils/OAuthUtils' import { ValidatorUtils } from 'src/app/utils/ValidatorUtils' import { Browser } from '@capacitor/browser' +import { ApiService } from 'src/app/services/api.service' @Component({ selector: 'app-help', @@ -35,7 +36,13 @@ export class HelpComponent implements OnInit { @Input() onlyGuides: boolean isAlreadyLoggedIn = false - constructor(private oauthUtils: OAuthUtils, private validator: ValidatorUtils, private storage: StorageService, private router: Router) {} + constructor( + private oauthUtils: OAuthUtils, + private validator: ValidatorUtils, + private storage: StorageService, + private router: Router, + public api: ApiService + ) {} ngOnInit() { this.storage.isLoggedIn().then((result) => { diff --git a/src/app/models/StorageTypes.ts b/src/app/models/StorageTypes.ts index a5e34487..beb75ecd 100644 --- a/src/app/models/StorageTypes.ts +++ b/src/app/models/StorageTypes.ts @@ -34,6 +34,14 @@ export interface ApiNetwork { onlyDebug: boolean active: boolean genesisTs: number + elCurrency: Currency + clCurrency: Currency +} + +export enum Currency { + ETH = 'ETH', + GNO = 'GNO', + xDAI = 'xDAI', } export interface NetworkPreferences { diff --git a/src/app/pages/block-detail/block-detail.page.ts b/src/app/pages/block-detail/block-detail.page.ts index c77f482a..8a2f52d3 100644 --- a/src/app/pages/block-detail/block-detail.page.ts +++ b/src/app/pages/block-detail/block-detail.page.ts @@ -90,20 +90,15 @@ export class BlockDetailPage implements OnInit { async openBlock() { await Browser.open({ - url: (await this.getBaseBrowserUrl()) + '/block/' + this.block.blockNumber, + url: this.api.getBaseUrl() + '/block/' + this.block.blockNumber, toolbarColor: '#2f2e42', }) } async openFeeRecipient() { await Browser.open({ - url: (await this.getBaseBrowserUrl()) + '/address/' + this.feeRecipient, + url: this.api.getBaseUrl() + '/address/' + this.feeRecipient, toolbarColor: '#2f2e42', }) } - - async getBaseBrowserUrl() { - const net = (await this.api.networkConfig).net - return 'https://' + net + 'beaconcha.in' - } } diff --git a/src/app/pages/notifications/notifications.page.html b/src/app/pages/notifications/notifications.page.html index 5c7a7ea8..1e16da12 100644 --- a/src/app/pages/notifications/notifications.page.html +++ b/src/app/pages/notifications/notifications.page.html @@ -10,7 +10,7 @@
Note: You are using the No-Google version of this app. Push notifications will not work, but you can configure webhook - notifications on beaconcha.in directly or by clicking here. + notifications on {{ api.getHostName() }} directly or by clicking here.
diff --git a/src/app/pages/notifications/notifications.page.ts b/src/app/pages/notifications/notifications.page.ts index 09232113..772a1900 100644 --- a/src/app/pages/notifications/notifications.page.ts +++ b/src/app/pages/notifications/notifications.page.ts @@ -73,7 +73,7 @@ export class NotificationsPage extends NotificationBase implements OnInit { } async configureWebhooks() { - await Browser.open({ url: 'https://beaconcha.in/user/webhooks', toolbarColor: '#2f2e42' }) + await Browser.open({ url: this.api.getBaseUrl() + '/user/webhooks', toolbarColor: '#2f2e42' }) } async ionViewWillEnter() { diff --git a/src/app/pages/subscribe/subscribe.page.ts b/src/app/pages/subscribe/subscribe.page.ts index 1a582937..65b4c347 100644 --- a/src/app/pages/subscribe/subscribe.page.ts +++ b/src/app/pages/subscribe/subscribe.page.ts @@ -9,6 +9,7 @@ import { Toast } from '@capacitor/toast' import FlavorUtils from 'src/app/utils/FlavorUtils' import { Browser } from '@capacitor/browser' +import { ApiService } from 'src/app/services/api.service' @Component({ selector: 'app-subscribe', @@ -32,7 +33,8 @@ export class SubscribePage implements OnInit { private oauth: OAuthUtils, private alertService: AlertService, private platform: Platform, - private flavor: FlavorUtils + private flavor: FlavorUtils, + private api: ApiService ) { this.selectedPackage = this.merchant.PACKAGES[2] } @@ -80,7 +82,7 @@ export class SubscribePage implements OnInit { 'Yes', async () => { if (isNoGoogle) { - await Browser.open({ url: 'https://beaconcha.in/premium', toolbarColor: '#2f2e42' }) + await Browser.open({ url: this.api.getBaseUrl() + '/premium', toolbarColor: '#2f2e42' }) } else { this.merchant.purchase(this.selectedPackage.purchaseKey) } @@ -90,7 +92,7 @@ export class SubscribePage implements OnInit { } if (isNoGoogle) { - await Browser.open({ url: 'https://beaconcha.in/premium', toolbarColor: '#2f2e42' }) + await Browser.open({ url: this.api.getBaseUrl() + '/premium', toolbarColor: '#2f2e42' }) } else { this.merchant.purchase(this.selectedPackage.purchaseKey) } diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index 47df85c3..d546ee20 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -38,7 +38,9 @@ const SERVER_TIMEOUT = 25000 providedIn: 'root', }) export class ApiService extends CacheModule { - networkConfig: Promise + private isInitialized = false + + networkConfig: ApiNetwork public connectionStateOK = true @@ -74,7 +76,7 @@ export class ApiService extends CacheModule { }) this.lastCacheInvalidate = Date.now() //this.registerLogMiddleware() - this.updateNetworkConfig() + this.initialize() //this.isIOS15().then((result) => { this.forceNativeAll = result }) } @@ -91,27 +93,26 @@ export class ApiService extends CacheModule { } } - async updateNetworkConfig() { - this.networkConfig = this.storage.getNetworkPreferences().then((config) => { + async initialize() { + this.networkConfig = await this.storage.getNetworkPreferences().then((config) => { const temp = findConfigForKey(config.key) if (temp) { return temp } return config }) - - await this.networkConfig + this.isInitialized = true } networkName = null async getNetworkName(): Promise { - const temp = (await this.networkConfig).key + const temp = this.networkConfig.key this.networkName = temp return temp } async getNetwork(): Promise { - const temp = await this.networkConfig + const temp = this.networkConfig return temp } @@ -158,7 +159,7 @@ export class ApiService extends CacheModule { const formBody = new FormData() formBody.set('grant_type', 'refresh_token') formBody.set('refresh_token', user.refreshToken) - const url = await this.getResourceUrl(req.resource, req.endPoint) + const url = this.getResourceUrl(req.resource, req.endPoint) // use js here for the request since the native http plugin performs inconsistent across platforms with non json requests const resp = await fetch(url, { @@ -195,16 +196,16 @@ export class ApiService extends CacheModule { this.awaitingResponses[resource].release() } - async isNotMainnet(): Promise { - const test = (await this.networkConfig).net != '' + isNotMainnet(): boolean { + const test = this.networkConfig.net != '' return test } - private async getCacheKey(request: APIRequest): Promise { + private getCacheKey(request: APIRequest): string { if (request.method == Method.GET) { - return request.method + (await this.getResourceUrl(request.resource, request.endPoint)) + return request.method + this.getResourceUrl(request.resource, request.endPoint) } else if (request.cacheablePOST) { - return request.method + (await this.getResourceUrl(request.resource, request.endPoint)) + JSON.stringify(request.postData) + return request.method + this.getResourceUrl(request.resource, request.endPoint) + JSON.stringify(request.postData) } return null } @@ -217,7 +218,7 @@ export class ApiService extends CacheModule { } // If cached and not stale, return cache - const cached = (await this.getCache(await this.getCacheKey(request))) as Response + const cached = (await this.getCache(this.getCacheKey(request))) as Response if (cached) { if (this.lastRefreshed == 0) this.lastRefreshed = Date.now() cached.cached = true @@ -281,7 +282,7 @@ export class ApiService extends CacheModule { } if ((request.method == Method.GET || request.cacheablePOST) && result && result.status == 200 && result.data) { - this.putCache(await this.getCacheKey(request), result, request.maxCacheAge) + this.putCache(this.getCacheKey(request), result, request.maxCacheAge) } if (request.updatesLastRefreshState) this.updateLastRefreshed(result) @@ -295,7 +296,7 @@ export class ApiService extends CacheModule { } async clearSpecificCache(request: APIRequest) { - this.putCache(await this.getCacheKey(request), null, request.maxCacheAge) + this.putCache(this.getCacheKey(request), null, request.maxCacheAge) } private updateLastRefreshed(response: Response) { @@ -306,7 +307,7 @@ export class ApiService extends CacheModule { private async get(resource: string, endpoint = 'default', ignoreFails = false, options: HttpOptions = { url: null, headers: {} }) { const getOptions = { - url: await this.getResourceUrl(resource, endpoint), + url: this.getResourceUrl(resource, endpoint), method: 'get', headers: options.headers, } @@ -324,7 +325,7 @@ export class ApiService extends CacheModule { } const postOptions = { - url: await this.getResourceUrl(resource, endpoint), + url: this.getResourceUrl(resource, endpoint), headers: options.headers, data: this.formatPostData(data), method: 'post', @@ -339,7 +340,7 @@ export class ApiService extends CacheModule { private async legacyGet(resource: string, endpoint = 'default', ignoreFails = false, options: HttpOptions = { url: null, headers: {} }) { return this.httpLegacy - .get(await this.getResourceUrl(resource, endpoint), options) + .get(this.getResourceUrl(resource, endpoint), options) .catch((err) => { this.updateConnectionState(ignoreFails, false) console.warn('Connection err', err) @@ -366,7 +367,7 @@ export class ApiService extends CacheModule { }) .then((response: AxiosResponse) => this.validateResponseLegacy(ignoreFails, response)); */ - const resp = await fetch(await this.getResourceUrl(resource, endpoint), { + const resp = await fetch(this.getResourceUrl(resource, endpoint), { method: 'POST', body: JSON.stringify(this.formatPostData(data)), headers: options.headers, @@ -427,23 +428,23 @@ export class ApiService extends CacheModule { return response } - async getResourceUrl(resource: string, endpoint = 'default'): Promise { - const base = await this.getBaseUrl() + getResourceUrl(resource: string, endpoint = 'default'): string { + const base = this.getBaseUrl() if (endpoint == 'default') { - return (await this.getApiBaseUrl()) + '/' + resource + return this.getApiBaseUrl() + '/' + resource } else { const substitute = endpoint.replace('{$BASE}', base) return substitute + '/' + resource } } - async getApiBaseUrl() { - const cfg = await this.networkConfig - return (await this.getBaseUrl()) + cfg.endpoint + cfg.version + getApiBaseUrl() { + const cfg = this.networkConfig + return this.getBaseUrl() + cfg.endpoint + cfg.version } - async getBaseUrl(): Promise { - const cfg = await this.networkConfig + getBaseUrl(): string { + const cfg = this.networkConfig return cfg.protocol + '://' + cfg.net + cfg.host } @@ -460,6 +461,7 @@ export class ApiService extends CacheModule { for (const entry of MAP) { if (entry.key == 'main') continue + if (entry.key == 'gnosis') continue if (!entry.active) continue if (entry.onlyDebug && !debug) continue re.push([this.capitalize(entry.key) + ' (Testnet)', entry.key]) @@ -470,6 +472,15 @@ export class ApiService extends CacheModule { capitalize(text) { return text.charAt(0).toUpperCase() + text.slice(1) } + + getHostName() { + const network = this.networkConfig + return network.host + } + + isGnosis() { + return this.networkConfig.key == 'gnosis' + } } export interface Response extends HttpResponse { @@ -479,3 +490,7 @@ export interface Response extends HttpResponse { export interface DevModeEnabled { enabled: boolean } + +export function initializeApiService(service: ApiService): () => Promise { + return () => service.initialize() +} diff --git a/src/app/services/storage.service.ts b/src/app/services/storage.service.ts index e21022a3..e123e441 100644 --- a/src/app/services/storage.service.ts +++ b/src/app/services/storage.service.ts @@ -139,7 +139,7 @@ export class StorageService extends CacheModule { } async openLogSession(modalCtr, offset: number) { - let lastLogSession = parseInt(await window.localStorage.getItem('last_log_session')) + let lastLogSession = parseInt(window.localStorage.getItem('last_log_session')) if (isNaN(lastLogSession)) lastLogSession = 0 const modal = await modalCtr.create({ diff --git a/src/app/services/sync.service.ts b/src/app/services/sync.service.ts index a80c7a30..07e02395 100644 --- a/src/app/services/sync.service.ts +++ b/src/app/services/sync.service.ts @@ -293,7 +293,7 @@ export class SyncService { if (success) this.lastNotifySync = Date.now() for (let j = 0; j < splice.length; j++) { - await splice[j].onComplete(success) + splice[j].onComplete(success) } } } @@ -372,7 +372,7 @@ export class SyncService { } async changeNotifyEvent(key: string, event: string, value: boolean, filter: string = null, threshold: number = null) { - const net = (await this.api.networkConfig).net + const net = this.api.networkConfig.net this.storage.setBooleanSetting(net + key, value) this.setLastChanged(net + key, event, filter, threshold, value ? 'subscribe' : 'unsubscribe') } @@ -421,7 +421,7 @@ export class SyncService { lastChanged: current.lastChanged, lastSynced: value, eventName: event, - network: await this.api.getApiBaseUrl(), + network: this.api.getApiBaseUrl(), eventFilter: current.eventFilter, eventThreshold: current.eventThreshold, subscribeAction: subscribeAction, @@ -441,7 +441,7 @@ export class SyncService { lastChanged: value, lastSynced: current.lastSynced, eventName: event, - network: await this.api.getApiBaseUrl(), + network: this.api.getApiBaseUrl(), eventFilter: filter, eventThreshold: threshold, subscribeAction: subscribeAction, @@ -463,7 +463,7 @@ export class SyncService { lastChanged: 0, lastSynced: 0, eventName: '', - network: await this.api.getApiBaseUrl(), + network: this.api.getApiBaseUrl(), eventFilter: null, eventThreshold: null, subscribeAction: 'subscribe', diff --git a/src/app/services/unitconv.service.ts b/src/app/services/unitconv.service.ts index af0e9376..9b6d6dcd 100644 --- a/src/app/services/unitconv.service.ts +++ b/src/app/services/unitconv.service.ts @@ -116,6 +116,12 @@ export class UnitconvService { unitStored.value = convertEthUnits(price, MAPPING.get('WEI'), Unit.ETHER) } + getRPLPrice() { + const unitStored: Unit = MAPPING.get('RPL') + if (!unitStored) return + return unitStored.value + } + setRETHPrice(price: BigNumber) { const unitStored: Unit = MAPPING.get('RETH') if (!unitStored) return diff --git a/src/app/tab-preferences/notification-base.ts b/src/app/tab-preferences/notification-base.ts index 7a4706b6..02042c98 100644 --- a/src/app/tab-preferences/notification-base.ts +++ b/src/app/tab-preferences/notification-base.ts @@ -97,7 +97,7 @@ export class NotificationBase implements OnInit { async loadAllToggles() { if (!(await this.storage.isLoggedIn())) return - const net = (await this.api.networkConfig).net + const net = this.api.networkConfig.net const request = new NotificationGetRequest() const response = await this.api.execute(request) @@ -166,7 +166,7 @@ export class NotificationBase implements OnInit { // locking toggle so we dont execute onChange when setting initial values const preferences = await this.storage.loadPreferencesToggles(net) - if (await this.api.isNotMainnet()) { + if (this.api.isNotMainnet()) { this.notify = preferences this.notifyInitialized = true this.disableToggleLock() @@ -208,10 +208,10 @@ export class NotificationBase implements OnInit { } } - const net = (await this.api.networkConfig).net + const net = this.api.networkConfig.net this.storage.setBooleanSetting(net + SETTING_NOTIFY, this.notify) this.settingsChanged = true - if (!(await this.api.isNotMainnet())) { + if (!this.api.isNotMainnet()) { this.sync.changeGeneralNotify(this.notify) } diff --git a/src/app/tab-preferences/tab-preferences.page.html b/src/app/tab-preferences/tab-preferences.page.html index 95232011..239dab64 100644 --- a/src/app/tab-preferences/tab-preferences.page.html +++ b/src/app/tab-preferences/tab-preferences.page.html @@ -211,6 +211,7 @@ Mainnet + Gnosis {{ item[0] }}
@@ -249,7 +250,7 @@ Login
- + Delete Account @@ -259,18 +260,18 @@ Legal - + Privacy Policy - + Terms of Service Open Source Licences - + Imprint diff --git a/src/app/tab-preferences/tab-preferences.page.ts b/src/app/tab-preferences/tab-preferences.page.ts index 6931ee67..443b2c2c 100644 --- a/src/app/tab-preferences/tab-preferences.page.ts +++ b/src/app/tab-preferences/tab-preferences.page.ts @@ -316,7 +316,7 @@ export class Tab3Page { await this.validatorUtils.clearCache() await this.storage.setNetworkPreferences(newConfig) - await this.api.updateNetworkConfig() + await this.api.initialize() await this.notificationBase.loadAllToggles() this.validatorUtils.notifyListeners() } diff --git a/src/app/utils/CacheModule.ts b/src/app/utils/CacheModule.ts index cdc16266..c2bb19bb 100644 --- a/src/app/utils/CacheModule.ts +++ b/src/app/utils/CacheModule.ts @@ -88,15 +88,15 @@ export class CacheModule { } } - clearCache() { - this.clearHardCache() + async clearCache() { + await this.clearHardCache() this.cache.clear() this.hotOnly.clear() } - clearHardCache() { + async clearHardCache() { if (this.hardStorage) { - this.hardStorage.setObject('cachemodule2_' + this.keyPrefix, null) + await this.hardStorage.setObject('cachemodule2_' + this.keyPrefix, null) } } diff --git a/src/app/utils/MerchantUtils.ts b/src/app/utils/MerchantUtils.ts index bdd85860..5b6f8d11 100644 --- a/src/app/utils/MerchantUtils.ts +++ b/src/app/utils/MerchantUtils.ts @@ -386,7 +386,7 @@ export class MerchantUtils { const currentProduct = this.findProduct(currentPlan) if (currentProduct == null) return 100 - const notMainnet = await this.api.isNotMainnet() + const notMainnet = this.api.isNotMainnet() if (notMainnet) return currentProduct.maxTestnetValidators return currentProduct.maxValidators } @@ -395,7 +395,7 @@ export class MerchantUtils { const currentProduct = this.findProduct(MAX_PRODUCT) if (currentProduct == null) return 100 - const notMainnet = await this.api.isNotMainnet() + const notMainnet = this.api.isNotMainnet() if (notMainnet) return currentProduct.maxTestnetValidators return currentProduct.maxValidators } diff --git a/src/app/utils/NetworkData.ts b/src/app/utils/NetworkData.ts index a671f291..eee4abf0 100644 --- a/src/app/utils/NetworkData.ts +++ b/src/app/utils/NetworkData.ts @@ -18,7 +18,7 @@ * // along with Beaconchain Dashboard. If not, see . */ -import { ApiNetwork } from '../models/StorageTypes' +import { ApiNetwork, Currency } from '../models/StorageTypes' export const MAP: ApiNetwork[] = [ { @@ -31,6 +31,21 @@ export const MAP: ApiNetwork[] = [ onlyDebug: false, active: true, genesisTs: 1606824023, + clCurrency: Currency.ETH, + elCurrency: Currency.ETH, + }, + { + key: 'gnosis', + protocol: 'https', + host: 'gnosischa.in', + net: '', + endpoint: '/api/', + version: 'v1', + onlyDebug: false, + active: true, + genesisTs: 1638993340, + clCurrency: Currency.GNO, + elCurrency: Currency.xDAI, }, { key: 'prater', @@ -42,6 +57,8 @@ export const MAP: ApiNetwork[] = [ onlyDebug: false, active: true, genesisTs: 1616508000, + clCurrency: Currency.ETH, + elCurrency: Currency.ETH, }, { key: 'sepolia', @@ -53,6 +70,8 @@ export const MAP: ApiNetwork[] = [ onlyDebug: false, active: true, genesisTs: 1655733600, + clCurrency: Currency.ETH, + elCurrency: Currency.ETH, }, { key: 'holesky', @@ -64,6 +83,8 @@ export const MAP: ApiNetwork[] = [ onlyDebug: false, active: true, genesisTs: 1695902400, + clCurrency: Currency.ETH, + elCurrency: Currency.ETH, }, { key: 'local dev', @@ -75,6 +96,8 @@ export const MAP: ApiNetwork[] = [ onlyDebug: true, active: true, genesisTs: 1606824023, + clCurrency: Currency.ETH, + elCurrency: Currency.ETH, }, { key: 'invalid (no connection)', @@ -86,6 +109,8 @@ export const MAP: ApiNetwork[] = [ onlyDebug: true, active: true, genesisTs: 1606824023, + clCurrency: Currency.ETH, + elCurrency: Currency.ETH, }, ] diff --git a/src/app/utils/OAuthUtils.ts b/src/app/utils/OAuthUtils.ts index bb07859f..42886080 100644 --- a/src/app/utils/OAuthUtils.ts +++ b/src/app/utils/OAuthUtils.ts @@ -122,7 +122,7 @@ export class OAuthUtils { private async getOAuthOptions() { const api = this.api - const endpointUrl = await api.getResourceUrl('user/token') + const endpointUrl = api.getResourceUrl('user/token') const info = await Device.getId().catch(() => { return { identifier: 'iduno' } @@ -142,7 +142,7 @@ export class OAuthUtils { } return { - authorizationBaseUrl: (await api.getBaseUrl()) + '/user/authorize', + authorizationBaseUrl: api.getBaseUrl() + '/user/authorize', accessTokenEndpoint: endpointUrl, web: { appId: clientID, diff --git a/src/app/utils/ValidatorUtils.ts b/src/app/utils/ValidatorUtils.ts index 0fcaee89..6899fe4c 100644 --- a/src/app/utils/ValidatorUtils.ts +++ b/src/app/utils/ValidatorUtils.ts @@ -306,9 +306,8 @@ export class ValidatorUtils extends CacheModule { } async updateValidatorStates(validators: Validator[]) { - const epoch = await this.getRemoteCurrentEpoch() validators.forEach((item) => { - item.state = this.getValidatorState(item, epoch) + item.state = this.getValidatorState(item) }) } @@ -328,22 +327,15 @@ export class ValidatorUtils extends CacheModule { return result } - getValidatorState(item: Validator, currentEpoch: EpochResponse): ValidatorState { - if ( - item.data.slashed == false && - item.data.lastattestationslot >= (currentEpoch.epoch - 2) * 32 && // online since last two epochs - item.data.exitepoch >= currentEpoch.epoch && - item.data.activationepoch <= currentEpoch.epoch - ) { + getValidatorState(item: Validator): ValidatorState { + if (item.data.slashed) return ValidatorState.SLASHED + if (item.data.status == 'exited') return ValidatorState.EXITED + if (item.data.status == 'deposited') return ValidatorState.ELIGABLE + if (item.data.status == 'pending') return ValidatorState.WAITING + if (item.data.slashed == false && item.data.status.indexOf('online') > 0) { return ValidatorState.ACTIVE } - if (item.data.slashed) return ValidatorState.SLASHED - if (item.data.exitepoch < currentEpoch.epoch) return ValidatorState.EXITED - if (item.data.activationeligibilityepoch > currentEpoch.epoch) return ValidatorState.ELIGABLE - if (item.data.activationepoch > currentEpoch.epoch) return ValidatorState.WAITING - if (item.data.lastattestationslot < (currentEpoch.epoch - 2) * 32) return ValidatorState.OFFLINE - // default case return ValidatorState.OFFLINE } From 0dfeca53a7cfc5ebb834c988e46dacef47d7c4d7 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Mon, 16 Oct 2023 14:13:00 +0200 Subject: [PATCH 02/53] fix tos and privacy links --- src/app/tab-preferences/tab-preferences.page.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/tab-preferences/tab-preferences.page.html b/src/app/tab-preferences/tab-preferences.page.html index 239dab64..e36309e6 100644 --- a/src/app/tab-preferences/tab-preferences.page.html +++ b/src/app/tab-preferences/tab-preferences.page.html @@ -260,11 +260,11 @@ Legal - + Privacy Policy - + Terms of Service From 8a800dfa593f429954c3a6a410b65e1ddbedfb85 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Mon, 16 Oct 2023 15:32:07 +0200 Subject: [PATCH 03/53] code quality and new linter rules --- .eslintrc | 13 +++- src/app/app.component.ts | 2 +- src/app/components/block/block.component.ts | 2 +- .../dashboard/dashboard.component.ts | 38 ++++++------ .../components/message/message.component.ts | 4 +- src/app/controllers/MachineController.ts | 11 ++-- src/app/controllers/OverviewController.ts | 8 +-- src/app/pages/helppage/helppage.page.ts | 2 +- src/app/pages/licences/licences.page.ts | 2 +- .../machine-detail/machine-detail.page.ts | 2 +- .../pages/notifications/notifications.page.ts | 4 +- src/app/pages/subscribe/subscribe.page.ts | 6 +- .../validatordetail/validatordetail.page.ts | 2 +- src/app/services/api.service.ts | 8 +-- src/app/services/sync.service.ts | 10 ++-- src/app/services/unitconv.service.ts | 2 +- src/app/tab-blocks/tab-blocks.page.ts | 2 +- src/app/tab-dashboard/tab-dashboard.page.ts | 4 +- src/app/tab-preferences/notification-base.ts | 6 +- .../tab-preferences/tab-preferences.page.ts | 6 +- src/app/tab-validators/tab-validators.page.ts | 24 ++++---- src/app/utils/CacheModule.ts | 6 +- src/app/utils/ClientUpdateUtils.ts | 4 +- src/app/utils/MachineUtils.ts | 4 +- src/app/utils/MerchantUtils.ts | 14 ++--- src/app/utils/OAuthUtils.ts | 2 +- src/app/utils/ThemeUtils.ts | 2 +- src/app/utils/ValidatorSyncUtils.ts | 6 +- src/app/utils/ValidatorUtils.ts | 60 +++++++++---------- 29 files changed, 132 insertions(+), 124 deletions(-) diff --git a/.eslintrc b/.eslintrc index 829b7b59..91b976e0 100644 --- a/.eslintrc +++ b/.eslintrc @@ -13,6 +13,17 @@ "plugin:@typescript-eslint/recommended" ], "rules": { - "@typescript-eslint/await-thenable": "error" + "@typescript-eslint/await-thenable": "error", + "require-await": "off", + "@typescript-eslint/require-await": "error", + "@typescript-eslint/no-misused-promises": [ + "error", + { + "checksVoidReturn": false + } + ], + "no-shadow": "off", + "@typescript-eslint/no-shadow": "error" + // "@typescript-eslint/no-floating-promises": "error" } } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 33e06820..66fb9084 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -51,7 +51,7 @@ export class AppComponent { this.platform.ready().then(() => { this.storage.migrateToCapacitor3().then(async () => { BigNumber.config({ DECIMAL_PLACES: 25 }) - const networkName = await this.api.getNetworkName() + const networkName = this.api.getNetworkName() // migrate to 3.2+ const result = await this.storage.getBooleanSetting(networkName + 'migrated_to_3.2', false) if (!result) { diff --git a/src/app/components/block/block.component.ts b/src/app/components/block/block.component.ts index 365fd352..6247f1a2 100644 --- a/src/app/components/block/block.component.ts +++ b/src/app/components/block/block.component.ts @@ -47,7 +47,7 @@ export class BlockComponent implements OnInit { } } - async setInput(block: BlockResponse) { + setInput(block: BlockResponse) { this.block = block this.ngOnChanges() } diff --git a/src/app/components/dashboard/dashboard.component.ts b/src/app/components/dashboard/dashboard.component.ts index c33b59e3..6976347e 100644 --- a/src/app/components/dashboard/dashboard.component.ts +++ b/src/app/components/dashboard/dashboard.component.ts @@ -188,13 +188,13 @@ export class DashboardComponent implements OnInit { } } - async updateWithdrawalInfo() { + updateWithdrawalInfo() { this.storage.getBooleanSetting('withdrawal_info_dismissed', false).then((result) => { this.showWithdrawalInfo = !this.data.withdrawalsEnabledForAll && !result }) } - async updateDepositCreditText() { + updateDepositCreditText() { if (this.data.rocketpool.depositCredit && this.data.rocketpool.depositCredit.gt(0)) { this.depositCreditText = `You have ${this.unit.convert( this.data.rocketpool.depositCredit, @@ -205,7 +205,7 @@ export class DashboardComponent implements OnInit { } } - async updateVacantMinipoolText() { + updateVacantMinipoolText() { if (this.data.rocketpool.vacantPools && this.data.rocketpool.vacantPools > 0) { this.vacantMinipoolText = `${this.data.rocketpool.vacantPools} of your ${ this.data.rocketpool.vacantPools == 1 ? 'minipool is' : 'minipools are' @@ -217,15 +217,15 @@ export class DashboardComponent implements OnInit { } } - async epochToTimestamp(epoch: number) { - const network = await this.api.getNetwork() + epochToTimestamp(epoch: number) { + const network = this.api.getNetwork() return (network.genesisTs + epoch * 32 * 12) * 1000 } - async updateActiveSyncCommitteeMessage(committee: SyncCommitteeResponse) { + updateActiveSyncCommitteeMessage(committee: SyncCommitteeResponse) { if (committee) { - const endTs = await this.epochToTimestamp(committee.end_epoch) - const startTs = await this.epochToTimestamp(committee.start_epoch) + const endTs = this.epochToTimestamp(committee.end_epoch) + const startTs = this.epochToTimestamp(committee.start_epoch) this.currentSyncCommitteeMessage = { title: 'Sync Committee', text: `Your validator${committee.validators.length > 1 ? 's' : ''} ${committee.validators.toString()} ${ @@ -241,10 +241,10 @@ export class DashboardComponent implements OnInit { } } - async updateNextSyncCommitteeMessage(committee: SyncCommitteeResponse) { + updateNextSyncCommitteeMessage(committee: SyncCommitteeResponse) { if (committee) { - const endTs = await this.epochToTimestamp(committee.end_epoch) - const startTs = await this.epochToTimestamp(committee.start_epoch) + const endTs = this.epochToTimestamp(committee.end_epoch) + const startTs = this.epochToTimestamp(committee.start_epoch) this.nextSyncCommitteeMessage = { title: 'Sync Committee Soon', text: `Your validator${committee.validators.length > 1 ? 's' : ''} ${committee.validators.toString()} ${ @@ -347,7 +347,7 @@ export class DashboardComponent implements OnInit { }) } - private async checkForGenesisOccurred() { + private checkForGenesisOccurred() { if (!this.data || !this.data.currentEpoch) return const currentEpoch = this.data.currentEpoch as EpochResponse this.awaitGenesis = currentEpoch.epoch == 0 && currentEpoch.proposedblocks <= 1 @@ -364,7 +364,7 @@ export class DashboardComponent implements OnInit { } } - const olderResult = await this.validatorUtils.getOlderEpoch() + const olderResult = this.validatorUtils.getOlderEpoch() if (!this.data || !this.data.currentEpoch || !olderResult) return console.log('checkForFinalization', olderResult) this.finalizationIssue = new BigNumber(olderResult.globalparticipationrate).isLessThan('0.664') && olderResult.epoch > 7 @@ -559,8 +559,8 @@ export class DashboardComponent implements OnInit { } private proposalChart = null - async createProposedChart(proposed, missed, orphaned) { - const network = await this.api.getNetwork() + createProposedChart(proposed, missed, orphaned) { + const network = this.api.getNetwork() this.proposalChart = Highstock.chart( 'highchartsBlocks' + this.randomChartId, @@ -678,11 +678,11 @@ export class DashboardComponent implements OnInit { } private balanceChart = null - async createBalanceChart(consensusIncome, executionIncome) { + createBalanceChart(consensusIncome, executionIncome) { executionIncome = executionIncome || [] const ticksDecimalPlaces = 3 - const network = await this.api.getNetwork() + const network = this.api.getNetwork() const getValueString = (value: BigNumber): string => { let text = `${value.toFixed(5)} ETH` @@ -865,10 +865,10 @@ export class DashboardComponent implements OnInit { } async openBrowser() { - await Browser.open({ url: await this.getBrowserURL(), toolbarColor: '#2f2e42' }) + await Browser.open({ url: this.getBrowserURL(), toolbarColor: '#2f2e42' }) } - async getBrowserURL(): Promise { + getBrowserURL(): string { if (this.data.foreignValidator) { return this.api.getBaseUrl() + '/validator/' + this.data.foreignValidatorItem.pubkey } else { diff --git a/src/app/components/message/message.component.ts b/src/app/components/message/message.component.ts index 51738e43..df6fd5c4 100644 --- a/src/app/components/message/message.component.ts +++ b/src/app/components/message/message.component.ts @@ -99,13 +99,13 @@ export class MessageComponent implements OnInit { buttons: [ { text: 'Do not show again', - handler: async () => { + handler: () => { this.dismiss() }, }, { text: 'Decide Later', - handler: async () => { + handler: () => { this.notDismissed = false }, }, diff --git a/src/app/controllers/MachineController.ts b/src/app/controllers/MachineController.ts index 2d07b37b..bb4bfaa3 100644 --- a/src/app/controllers/MachineController.ts +++ b/src/app/controllers/MachineController.ts @@ -598,18 +598,19 @@ export const bytes = (function () { tempLabel = [] let count - return function (bytes, label, isFirst, precision = 3) { + return function (byteData, label, isFirst, precision = 3) { let e, value - if (bytes == 0) return 0 + if (byteData == 0) return 0 if (isFirst) count = 0 - e = Math.floor(Math.log(bytes) / Math.log(1024)) - value = (bytes / Math.pow(1024, Math.floor(e))).toFixed(precision) + e = Math.floor(Math.log(byteData) / Math.log(1024)) + value = (byteData / Math.pow(1024, Math.floor(e))).toFixed(precision) tempLabel[count] = value - if (count > 0 && Math.abs(tempLabel[count - 1] - tempLabel[count]) < 0.0001) value = (bytes / Math.pow(1024, Math.floor(--e))).toFixed(precision) + if (count > 0 && Math.abs(tempLabel[count - 1] - tempLabel[count]) < 0.0001) + value = (byteData / Math.pow(1024, Math.floor(--e))).toFixed(precision) e = e < 0 ? -e : e if (label) value += ' ' + s[e] diff --git a/src/app/controllers/OverviewController.ts b/src/app/controllers/OverviewController.ts index 66cbceca..1454036f 100644 --- a/src/app/controllers/OverviewController.ts +++ b/src/app/controllers/OverviewController.ts @@ -151,7 +151,7 @@ export default class OverviewController { }) const aprPerformance31dExecution = sumBigInt(validators, (cur) => - this.sumExcludeSmoothingPool(cur, (cur) => cur.execution.performance31d.toString()) + this.sumExcludeSmoothingPool(cur, (fieldCur) => fieldCur.execution.performance31d.toString()) ) const overallBalance = this.sumBigIntBalanceRP(validators, (cur) => new BigNumber(cur.data.balance)) @@ -296,17 +296,17 @@ export default class OverviewController { private getExecutionPerformance(validators: Validator[], validatorDepositActive: BigNumber, aprPerformance31dExecution: BigNumber) { const performance1d = this.sumBigIntPerformanceRP(validators, (cur) => - this.sumExcludeSmoothingPool(cur, (cur) => cur.execution.performance1d.toString()).multipliedBy( + this.sumExcludeSmoothingPool(cur, (fieldCur) => fieldCur.execution.performance1d.toString()).multipliedBy( new BigNumber(cur.execshare == null ? 1 : cur.execshare) ) ) const performance31d = this.sumBigIntPerformanceRP(validators, (cur) => - this.sumExcludeSmoothingPool(cur, (cur) => cur.execution.performance31d.toString()).multipliedBy( + this.sumExcludeSmoothingPool(cur, (fieldCur) => fieldCur.execution.performance31d.toString()).multipliedBy( new BigNumber(cur.execshare == null ? 1 : cur.execshare) ) ) const performance7d = this.sumBigIntPerformanceRP(validators, (cur) => - this.sumExcludeSmoothingPool(cur, (cur) => cur.execution.performance7d.toString()).multipliedBy( + this.sumExcludeSmoothingPool(cur, (fieldCur) => fieldCur.execution.performance7d.toString()).multipliedBy( new BigNumber(cur.execshare == null ? 1 : cur.execshare) ) ) diff --git a/src/app/pages/helppage/helppage.page.ts b/src/app/pages/helppage/helppage.page.ts index 5e534eab..309441b5 100644 --- a/src/app/pages/helppage/helppage.page.ts +++ b/src/app/pages/helppage/helppage.page.ts @@ -34,7 +34,7 @@ export class HelppagePage implements OnInit { ngOnInit() { const event = fromEvent(document, 'backbutton') - this.backbuttonSubscription = event.subscribe(async () => { + this.backbuttonSubscription = event.subscribe(() => { this.modalCtrl.dismiss() }) } diff --git a/src/app/pages/licences/licences.page.ts b/src/app/pages/licences/licences.page.ts index b589e3dc..a535469a 100644 --- a/src/app/pages/licences/licences.page.ts +++ b/src/app/pages/licences/licences.page.ts @@ -34,7 +34,7 @@ export class LicencesPage implements OnInit { ngOnInit() { this.populatePre('./3rdpartylicenses.txt') const event = fromEvent(document, 'backbutton') - this.backbuttonSubscription = event.subscribe(async () => { + this.backbuttonSubscription = event.subscribe(() => { this.modalCtrl.dismiss() }) } diff --git a/src/app/pages/machine-detail/machine-detail.page.ts b/src/app/pages/machine-detail/machine-detail.page.ts index f39c2b0e..124677cb 100644 --- a/src/app/pages/machine-detail/machine-detail.page.ts +++ b/src/app/pages/machine-detail/machine-detail.page.ts @@ -96,7 +96,7 @@ export class MachineDetailPage extends MachineController implements OnInit { async ngOnInit() { this.selectionTimeFrame = this.timeframe const event = fromEvent(document, 'backbutton') - this.backbuttonSubscription = event.subscribe(async () => { + this.backbuttonSubscription = event.subscribe(() => { this.modalCtrl.dismiss() }) diff --git a/src/app/pages/notifications/notifications.page.ts b/src/app/pages/notifications/notifications.page.ts index 772a1900..726d0400 100644 --- a/src/app/pages/notifications/notifications.page.ts +++ b/src/app/pages/notifications/notifications.page.ts @@ -84,9 +84,7 @@ export class NotificationsPage extends NotificationBase implements OnInit { }) this.storage.getAuthUser().then((result) => (this.authUser = result)) - this.api.getNetworkName().then((result) => { - this.network = this.api.capitalize(result) - }) + this.network = this.api.getNetworkName() this.merchantUtils.hasCustomizableNotifications().then((result) => { this.canCustomizeThresholds = result }) diff --git a/src/app/pages/subscribe/subscribe.page.ts b/src/app/pages/subscribe/subscribe.page.ts index 65b4c347..8fc5426b 100644 --- a/src/app/pages/subscribe/subscribe.page.ts +++ b/src/app/pages/subscribe/subscribe.page.ts @@ -41,7 +41,7 @@ export class SubscribePage implements OnInit { ngOnInit() { const event = fromEvent(document, 'backbutton') - this.backbuttonSubscription = event.subscribe(async () => { + this.backbuttonSubscription = event.subscribe(() => { this.modalCtrl.dismiss() }) @@ -99,11 +99,11 @@ export class SubscribePage implements OnInit { } async purchaseIntern() { - const loggedIn = await this.storage.isLoggedIn() + let loggedIn = await this.storage.isLoggedIn() if (!loggedIn) { this.alertService.confirmDialog('Login', 'You need to login to your beaconcha.in account first. Continue?', 'Login', () => { this.oauth.login().then(async () => { - const loggedIn = await this.storage.isLoggedIn() + loggedIn = await this.storage.isLoggedIn() if (loggedIn) this.continuePurchaseIntern() }) }) diff --git a/src/app/pages/validatordetail/validatordetail.page.ts b/src/app/pages/validatordetail/validatordetail.page.ts index c99ab379..2ac101f8 100644 --- a/src/app/pages/validatordetail/validatordetail.page.ts +++ b/src/app/pages/validatordetail/validatordetail.page.ts @@ -52,7 +52,7 @@ export class ValidatordetailPage implements OnInit { ngOnInit() { const event = fromEvent(document, 'backbutton') - this.backbuttonSubscription = event.subscribe(async () => { + this.backbuttonSubscription = event.subscribe(() => { this.modalCtrl.dismiss() }) this.updateDetails(this.item) diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index d546ee20..340f00c4 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -105,13 +105,13 @@ export class ApiService extends CacheModule { } networkName = null - async getNetworkName(): Promise { + getNetworkName(): string { const temp = this.networkConfig.key this.networkName = temp return temp } - async getNetwork(): Promise { + getNetwork(): ApiNetwork { const temp = this.networkConfig return temp } @@ -190,7 +190,7 @@ export class ApiService extends CacheModule { await this.awaitingResponses[resource].acquire() } - private async unlock(resource) { + private unlock(resource) { console.log('Unlocking ', resource) this.awaitingResponses[resource].release() @@ -296,7 +296,7 @@ export class ApiService extends CacheModule { } async clearSpecificCache(request: APIRequest) { - this.putCache(this.getCacheKey(request), null, request.maxCacheAge) + await this.putCache(this.getCacheKey(request), null, request.maxCacheAge) } private updateLastRefreshed(response: Response) { diff --git a/src/app/services/sync.service.ts b/src/app/services/sync.service.ts index 07e02395..e3f31e9e 100644 --- a/src/app/services/sync.service.ts +++ b/src/app/services/sync.service.ts @@ -220,7 +220,7 @@ export class SyncService { event_threshold: syncChange.eventThreshold, onComplete: superOnComplete, }) - return true + return Promise.resolve(true) } } @@ -368,18 +368,18 @@ export class SyncService { async changeGeneralNotify(value: boolean, filter: string = null) { this.storage.setBooleanSetting(SETTING_NOTIFY, value) - this.setLastChanged(SETTING_NOTIFY, SETTING_NOTIFY, filter, null, value ? 'subscribe' : 'unsubscribe') + await this.setLastChanged(SETTING_NOTIFY, SETTING_NOTIFY, filter, null, value ? 'subscribe' : 'unsubscribe') } async changeNotifyEvent(key: string, event: string, value: boolean, filter: string = null, threshold: number = null) { const net = this.api.networkConfig.net this.storage.setBooleanSetting(net + key, value) - this.setLastChanged(net + key, event, filter, threshold, value ? 'subscribe' : 'unsubscribe') + await this.setLastChanged(net + key, event, filter, threshold, value ? 'subscribe' : 'unsubscribe') } async changeNotifyEventUser(key: string, event: string, value: boolean, filter: string = null, threshold: number = null) { this.storage.setBooleanSetting(key, value) - this.setLastChanged(key, event, filter, threshold, value ? 'subscribe' : 'unsubscribe') + await this.setLastChanged(key, event, filter, threshold, value ? 'subscribe' : 'unsubscribe') } async reapplyNotifyEvent(event: string, filter: string = null): Promise { @@ -404,7 +404,7 @@ export class SyncService { return true } - async changeNotifyClientUpdate(key: string, value: boolean) { + changeNotifyClientUpdate(key: string, value: boolean) { this.storage.setBooleanSetting(key, value) Clients.forEach(async (client) => { diff --git a/src/app/services/unitconv.service.ts b/src/app/services/unitconv.service.ts index 9b6d6dcd..7a921645 100644 --- a/src/app/services/unitconv.service.ts +++ b/src/app/services/unitconv.service.ts @@ -167,7 +167,7 @@ export class UnitconvService { }, 450) } - private getPref(unitPref, defaultva = 'ETHER') { + private getPref(unitPref, defaultva = 'ETHER'): string { if (unitPref) { return unitPref.prefered } diff --git a/src/app/tab-blocks/tab-blocks.page.ts b/src/app/tab-blocks/tab-blocks.page.ts index 577687c2..0ae5c55e 100644 --- a/src/app/tab-blocks/tab-blocks.page.ts +++ b/src/app/tab-blocks/tab-blocks.page.ts @@ -92,7 +92,7 @@ export class TabBlocksPage implements OnInit { return await modal.present() } - async doRefresh(event) { + doRefresh(event) { setTimeout(async () => { this.virtualScroll.scrollToIndex(0) await this.dataSource.reset() diff --git a/src/app/tab-dashboard/tab-dashboard.page.ts b/src/app/tab-dashboard/tab-dashboard.page.ts index cbd32cc9..df7850cc 100644 --- a/src/app/tab-dashboard/tab-dashboard.page.ts +++ b/src/app/tab-dashboard/tab-dashboard.page.ts @@ -92,14 +92,14 @@ export class Tab1Page { this.scrolling = false } - private async removeTooltips() { + private removeTooltips() { const inputs = Array.from(document.getElementsByTagName('tooltip') as HTMLCollectionOf) for (let i = 0; i < inputs.length; i++) { inputs[i].style.display = 'none' } } - async ionViewWillEnter() { + ionViewWillEnter() { if (this.lastRefreshTs + 6 * 60 > this.getUnixSeconds()) return this.refresh() diff --git a/src/app/tab-preferences/notification-base.ts b/src/app/tab-preferences/notification-base.ts index 02042c98..13e28753 100644 --- a/src/app/tab-preferences/notification-base.ts +++ b/src/app/tab-preferences/notification-base.ts @@ -105,7 +105,7 @@ export class NotificationBase implements OnInit { const isNotifyClientUpdatesEnabled = await this.storage.isNotifyClientUpdatesEnabled() - let network = await this.api.getNetworkName() + let network = this.api.getNetworkName() if (network == 'main') { network = 'mainnet' } else if (network == 'local dev') { @@ -289,7 +289,7 @@ export class NotificationBase implements OnInit { return count ? count : 0 } - async notifyEventToggle(eventName, filter = null, threshold = null) { + notifyEventToggle(eventName, filter = null, threshold = null) { this.settingsChanged = true this.sync.changeNotifyEvent(eventName, eventName, this.getNotifyToggleFromEvent(eventName), filter, threshold) this.api.clearSpecificCache(new NotificationGetRequest()) @@ -305,7 +305,7 @@ export class NotificationBase implements OnInit { } // include filter in key (fe used by machine toggles) - async notifyEventFilterToggle(eventName, filter = null, threshold = null) { + notifyEventFilterToggle(eventName, filter = null, threshold = null) { const key = eventName + filter const value = this.getNotifyToggleFromEvent(eventName) this.settingsChanged = true diff --git a/src/app/tab-preferences/tab-preferences.page.ts b/src/app/tab-preferences/tab-preferences.page.ts index 443b2c2c..3d414568 100644 --- a/src/app/tab-preferences/tab-preferences.page.ts +++ b/src/app/tab-preferences/tab-preferences.page.ts @@ -211,9 +211,7 @@ export class Tab3Page { ionViewWillEnter() { this.storage.getAuthUser().then((result) => (this.authUser = result)) this.debug = this.api.debug - this.api.getNetworkName().then((result) => { - this.network = result - }) + this.network = this.api.getNetworkName() } private getAllCurrencies() { @@ -276,7 +274,7 @@ export class Tab3Page { } } - async logout() { + logout() { this.alerts.confirmDialog('Confirm logout', 'Notifications will stop working if you sign out. Continue?', 'Logout', () => { this.confirmLogout() }) diff --git a/src/app/tab-validators/tab-validators.page.ts b/src/app/tab-validators/tab-validators.page.ts index 38b9dd20..a0f4a3e0 100644 --- a/src/app/tab-validators/tab-validators.page.ts +++ b/src/app/tab-validators/tab-validators.page.ts @@ -101,7 +101,7 @@ export class Tab2Page { if (!this.searchResultMode) { this.dataSource.setLoadFrom(this.getDefaultDataRetriever()) } - this.dataSource.reset() + await this.dataSource.reset() } async syncRemote() { @@ -189,13 +189,13 @@ export class Tab2Page { } } - async removeAllDialog() { + removeAllDialog() { this.showDialog('Remove all', 'Do you want to remove {AMOUNT} validators from your dashboard?', () => { this.confirmRemoveAll() }) } - async addAllDialog() { + addAllDialog() { this.showDialog('Add all', 'Do you want to add {AMOUNT} validators to your dashboard?', () => { this.confirmAddAll() }) @@ -223,18 +223,18 @@ export class Tab2Page { } async confirmRemoveAll() { - this.validatorUtils.deleteAll() - this.dataSource.reset() + await this.validatorUtils.deleteAll() + await this.dataSource.reset() } async confirmAddAll() { const responses: ValidatorResponse[] = [] - this.dataSource.getItems().forEach(async (item) => { + this.dataSource.getItems().forEach((item) => { responses.push(item.data) }) - this.validatorUtils.convertToValidatorModelsAndSaveLocal(false, responses) - this.dataSource.reset() + await this.validatorUtils.convertToValidatorModelsAndSaveLocal(false, responses) + await this.dataSource.reset() } searchEvent(event) { @@ -478,7 +478,7 @@ export class Tab2Page { buttons: [ { text: 'Remove', - handler: async () => { + handler: () => { this.validatorUtils.saveRocketpoolCollateralShare(onlyOneNodeAddress.rocketpool.node_address, null) this.cancelSelect() this.validatorUtils.notifyListeners() @@ -486,7 +486,7 @@ export class Tab2Page { }, { text: 'Save', - handler: async (alertData) => { + handler: (alertData) => { const shares = alertData.share if (shares < minShareStake) { Toast.show({ @@ -564,7 +564,7 @@ export class Tab2Page { buttons: [ { text: 'Remove', - handler: async () => { + handler: () => { for (let i = 0; i < validatorSubArray.length; i++) { validatorSubArray[i].share = null validatorSubArray[i].execshare = null @@ -576,7 +576,7 @@ export class Tab2Page { }, { text: 'Save', - handler: async (alertData) => { + handler: (alertData) => { const shares = alertData.share const sharesEL = alertData.execshare if ((shares && shares < minShareStake) || (sharesEL && sharesEL < minShareStake)) { diff --git a/src/app/utils/CacheModule.ts b/src/app/utils/CacheModule.ts index c2bb19bb..091c0fc5 100644 --- a/src/app/utils/CacheModule.ts +++ b/src/app/utils/CacheModule.ts @@ -67,7 +67,7 @@ export class CacheModule { return storeHard ? this.cache : this.hotOnly } - protected putCache(key: string, data: unknown, staleTime = this.staleTime) { + protected async putCache(key: string, data: unknown, staleTime = this.staleTime) { const cacheKey = this.getKey(key) const store = this.getStoreForCacheKey(cacheKey) @@ -79,11 +79,11 @@ export class CacheModule { try { if (this.hardStorage) { - this.hardStorage.setObject('cachemodule2_' + this.keyPrefix, this.cache) + await this.hardStorage.setObject('cachemodule2_' + this.keyPrefix, this.cache) } } catch (e) { if (isQuotaExceededError(e)) { - this.clearHardCache() + await this.clearHardCache() } } } diff --git a/src/app/utils/ClientUpdateUtils.ts b/src/app/utils/ClientUpdateUtils.ts index 5edc25b8..650fac2b 100644 --- a/src/app/utils/ClientUpdateUtils.ts +++ b/src/app/utils/ClientUpdateUtils.ts @@ -127,7 +127,7 @@ export default class ClientUpdateUtils { return null } - const client = Clients.find((client) => client.key == clientKey) + const client = Clients.find((c) => c.key == clientKey) if (client == undefined) { console.log('ClientInfo for', clientKey, 'not found') return null @@ -255,7 +255,7 @@ export default class ClientUpdateUtils { } async dismissRelease(clientKey: string, id: string) { - this.storage.setObject(LOCAL_UPDATED_KEY + clientKey, { clientKey: clientKey, version: id }) + await this.storage.setObject(LOCAL_UPDATED_KEY + clientKey, { clientKey: clientKey, version: id }) } private async getLastClosedVersion(clientKey: string): Promise { diff --git a/src/app/utils/MachineUtils.ts b/src/app/utils/MachineUtils.ts index e875d30f..54e5900b 100644 --- a/src/app/utils/MachineUtils.ts +++ b/src/app/utils/MachineUtils.ts @@ -104,8 +104,8 @@ export default class MachineUtils extends CacheModule { const machineNames = this.getAllMachineNamesFrom(result) console.log(LOGTAG + ' machine names', machineNames) - this.registerNewRemotesForSync(machineNames).then((result) => { - console.log(LOGTAG + ' registerNewRemotesForSync', result) + this.registerNewRemotesForSync(machineNames).then((remoteResult) => { + console.log(LOGTAG + ' registerNewRemotesForSync', remoteResult) }) // Storing all machine names if diff --git a/src/app/utils/MerchantUtils.ts b/src/app/utils/MerchantUtils.ts index 5b6f8d11..8fb5fa70 100644 --- a/src/app/utils/MerchantUtils.ts +++ b/src/app/utils/MerchantUtils.ts @@ -144,13 +144,13 @@ export class MerchantUtils { } async refreshToken() { - const refreshSuccess = (await this.api.refreshToken()) != null + let refreshSuccess = (await this.api.refreshToken()) != null if (!refreshSuccess) { console.log('refreshing token after purchase failed, scheduling retry') const loading = await this.alertService.presentLoading('This can take a minute') loading.present() await this.sleep(35000) - const refreshSuccess = (await this.api.refreshToken()) != null + refreshSuccess = (await this.api.refreshToken()) != null if (!refreshSuccess) { this.alertService.showError( 'Purchase Error', @@ -230,7 +230,7 @@ export class MerchantUtils { async restore() { this.restorePurchase = true - CdvPurchase.store.restorePurchases() + await CdvPurchase.store.restorePurchases() } async purchase(product: string) { @@ -238,7 +238,7 @@ export class MerchantUtils { const loading = await this.alertService.presentLoading('') loading.present() CdvPurchase.store.order(offer).then( - async () => { + () => { this.restorePurchase = true setTimeout(() => { @@ -276,12 +276,12 @@ export class MerchantUtils { const loading = await this.alertService.presentLoading('Confirming, this might take a couple seconds') loading.present() - const result = await this.registerPurchaseOnRemote(purchaseData) + let result = await this.registerPurchaseOnRemote(purchaseData) if (!result) { console.log('registering receipt at remote failed, scheduling retry') await this.sleep(35000) - const result = await this.registerPurchaseOnRemote(purchaseData) + result = await this.registerPurchaseOnRemote(purchaseData) if (!result) { this.alertService.showError( 'Purchase Error', @@ -391,7 +391,7 @@ export class MerchantUtils { return currentProduct.maxValidators } - async getHighestPackageValidator(): Promise { + getHighestPackageValidator(): number { const currentProduct = this.findProduct(MAX_PRODUCT) if (currentProduct == null) return 100 diff --git a/src/app/utils/OAuthUtils.ts b/src/app/utils/OAuthUtils.ts index 42886080..70d07201 100644 --- a/src/app/utils/OAuthUtils.ts +++ b/src/app/utils/OAuthUtils.ts @@ -75,7 +75,7 @@ export class OAuthUtils { expiresIn: expiresIn, }) - await this.validatorUtils.clearDeletedSet() + this.validatorUtils.clearDeletedSet() await this.firebaseUtils.pushLastTokenUpstream(true) await this.sync.fullSync() diff --git a/src/app/utils/ThemeUtils.ts b/src/app/utils/ThemeUtils.ts index 160d5e2f..4e354ae2 100644 --- a/src/app/utils/ThemeUtils.ts +++ b/src/app/utils/ThemeUtils.ts @@ -54,7 +54,7 @@ export default class ThemeUtils { constructor(private storage: StorageService, private platform: Platform) {} - async init(splashScreenCallback: () => void) { + init(splashScreenCallback: () => void) { this.lock = this.storage.getObject(STORAGE_KEY).then((preferenceDarkMode) => { this.internalInit(preferenceDarkMode as StoredTheme) setTimeout(() => { diff --git a/src/app/utils/ValidatorSyncUtils.ts b/src/app/utils/ValidatorSyncUtils.ts index b05cb195..a3c5870f 100644 --- a/src/app/utils/ValidatorSyncUtils.ts +++ b/src/app/utils/ValidatorSyncUtils.ts @@ -133,7 +133,7 @@ export class ValidatorSyncUtils { } private async deleteUp(): Promise { - const storageKey = await this.validator.getStorageKey() + const storageKey = this.validator.getStorageKey() const deletedSet = await this.validator.getDeletedSet(storageKey) console.log('delete queue', deletedSet) @@ -171,7 +171,7 @@ export class ValidatorSyncUtils { private async syncUp(syncNotificationsForNewValidators: () => void) { console.log('[SYNC] syncing up') - const storageKey = await this.validator.getStorageKey() + const storageKey = this.validator.getStorageKey() const current = await this.validator.getMap(storageKey) const syncUpList: string[] = [] @@ -195,7 +195,7 @@ export class ValidatorSyncUtils { } private async syncRemoteRemovals(myRemotes: MyValidatorResponse[]) { - const storageKey = await this.validator.getStorageKey() + const storageKey = this.validator.getStorageKey() const current = await this.validator.getMap(storageKey) const existsSynced: Set = new Set() diff --git a/src/app/utils/ValidatorUtils.ts b/src/app/utils/ValidatorUtils.ts index 6899fe4c..870d3c4d 100644 --- a/src/app/utils/ValidatorUtils.ts +++ b/src/app/utils/ValidatorUtils.ts @@ -116,15 +116,15 @@ export class ValidatorUtils extends CacheModule { } async hasLocalValdiators() { - return (await this.getMap(await this.getStorageKey())).size > 0 + return (await this.getMap(this.getStorageKey())).size > 0 } async localValidatorCount() { - return (await this.getMap(await this.getStorageKey())).size + return (await this.getMap(this.getStorageKey())).size } - public async getStorageKey(): Promise { - return KEYPREFIX + (await this.api.getNetworkName()) + public getStorageKey(): string { + return KEYPREFIX + this.api.getNetworkName() } async migrateTo3Dot2() { @@ -164,13 +164,13 @@ export class ValidatorUtils extends CacheModule { this.storage.setObject(storageKey + '_deleted', [...list]) } - async clearDeletedSet() { - const storageKey = await this.getStorageKey() + clearDeletedSet() { + const storageKey = this.getStorageKey() this.setDeleteSet(storageKey, new Set()) } async deleteAll() { - const storageKey = await this.getStorageKey() + const storageKey = this.getStorageKey() const current = await this.getMap(storageKey) this.storage.setObject(storageKey, new Map()) @@ -188,7 +188,7 @@ export class ValidatorUtils extends CacheModule { private deletedWithoutNotifying: boolean = false async deleteValidatorLocal(validator: ValidatorResponse, notifyListeners: boolean = true) { - const storageKey = await this.getStorageKey() + const storageKey = this.getStorageKey() const current = await this.getMap(storageKey) current.delete(validator.pubkey) this.storage.setObject(storageKey, current) @@ -211,7 +211,7 @@ export class ValidatorUtils extends CacheModule { } async saveValidatorsLocal(validators: Validator[]) { - const storageKey = await this.getStorageKey() + const storageKey = this.getStorageKey() const current = await this.getMap(storageKey) const newMap = new Map() @@ -229,7 +229,7 @@ export class ValidatorUtils extends CacheModule { } async saveRocketpoolCollateralShare(nodeAddress: string, sharePercent: number) { - this.storage.setObject('rpl_share_' + nodeAddress, { share: sharePercent } as StoredShare) + await this.storage.setObject('rpl_share_' + nodeAddress, { share: sharePercent } as StoredShare) } async getRocketpoolCollateralShare(nodeAddress: string): Promise { @@ -239,12 +239,12 @@ export class ValidatorUtils extends CacheModule { } async getValidatorLocal(pubkey: string): Promise { - const current = await this.getMapWithoutDeleted(await this.getStorageKey()) + const current = await this.getMapWithoutDeleted(this.getStorageKey()) return current.get(pubkey) } async getAllValidatorsLocal(): Promise { - const current = await this.getMap(await this.getStorageKey()) + const current = await this.getMap(this.getStorageKey()) const erg: Validator[] = [...current.values()] return erg } @@ -260,7 +260,7 @@ export class ValidatorUtils extends CacheModule { } async getAllMyValidators(): Promise { - const storageKey = await this.getStorageKey() + const storageKey = this.getStorageKey() const local = await this.getMapWithoutDeleted(storageKey) const validatorString = getValidatorQueryString([...local.values()], 2000, (await this.merchantUtils.getCurrentPlanMaxValidator()) - 1) @@ -305,7 +305,7 @@ export class ValidatorUtils extends CacheModule { return false } - async updateValidatorStates(validators: Validator[]) { + updateValidatorStates(validators: Validator[]) { validators.forEach((item) => { item.state = this.getValidatorState(item) }) @@ -314,7 +314,7 @@ export class ValidatorUtils extends CacheModule { // checks if remote validators are already known locally. // If not, return all indizes of non locally known validators public async getAllNewIndicesOnly(myRemotes: MyValidatorResponse[]): Promise { - const storageKey = await this.getStorageKey() + const storageKey = this.getStorageKey() const current = await this.getMap(storageKey) const result: number[] = [] @@ -389,11 +389,11 @@ export class ValidatorUtils extends CacheModule { let local = null if (storage == SAVED) { - local = await this.getMapWithoutDeleted(await this.getStorageKey()) + local = await this.getMapWithoutDeleted(this.getStorageKey()) } const temp = this.convertToValidatorModel({ synced: false, storage: storage, validatorResponse: validatorsResponse }) - await this.updateValidatorStates(temp) + this.updateValidatorStates(temp) for (const vali of temp) { vali.attrEffectiveness = this.findAttributionEffectiveness(validatorEffectivenessResponse, vali.index) vali.rocketpool = this.findRocketpoolResponse(result.rocketpool_validators, vali.index) @@ -463,7 +463,7 @@ export class ValidatorUtils extends CacheModule { return -1 } - async getOlderEpoch(): Promise { + getOlderEpoch(): EpochResponse { return this.olderEpoch } @@ -509,33 +509,33 @@ export class ValidatorUtils extends CacheModule { return Promise.reject(new Error('Response is invalid')) } - async getCachedAttestationKey() { - return cacheAttestationKeyBare + (await this.api.getNetworkName()) + getCachedAttestationKey() { + return cacheAttestationKeyBare + this.api.getNetworkName() } - async getCachedPerformanceKey() { - return cachePerformanceKeyBare + (await this.api.getNetworkName()) + getCachedPerformanceKey() { + return cachePerformanceKeyBare + this.api.getNetworkName() } - async getCachedValidatorKey() { - return cacheValidatorsKeyBare + (await this.api.getNetworkName()) + getCachedValidatorKey() { + return cacheValidatorsKeyBare + this.api.getNetworkName() } - async getCachedEpochKey() { - return epochCachedKeyBare + (await this.api.getNetworkName()) + getCachedEpochKey() { + return epochCachedKeyBare + this.api.getNetworkName() } // single async convertToValidatorModelAndSaveValidatorLocal(synced: boolean, validator: ValidatorResponse) { - this.convertToValidatorModelsAndSaveLocal(synced, [validator]) + await this.convertToValidatorModelsAndSaveLocal(synced, [validator]) } // multiple async convertToValidatorModelsAndSaveLocal(synced: boolean, validator: ValidatorResponse[]) { - this.saveValidatorsLocal(this.convertToValidatorModel({ synced, storage: SAVED, validatorResponse: validator })) + await this.saveValidatorsLocal(this.convertToValidatorModel({ synced, storage: SAVED, validatorResponse: validator })) if (!synced) { - this.storage.setObject(LAST_TIME_ADDED_KEY, { timestamp: Date.now() } as StoredTimestamp) - this.clearCache() + await this.storage.setObject(LAST_TIME_ADDED_KEY, { timestamp: Date.now() } as StoredTimestamp) + await this.clearCache() } } From 2c04ccded17b298c078ac0cb63ba96a04cb8bb71 Mon Sep 17 00:00:00 2001 From: Manuel Date: Wed, 18 Oct 2023 09:27:10 +0200 Subject: [PATCH 04/53] fix token refresh on ios, update podfile.lock --- ios/App/Podfile.lock | 199 ++++++++++++++++++++++++++++++++ src/app/requests/requests.ts | 7 ++ src/app/services/api.service.ts | 16 +-- 3 files changed, 209 insertions(+), 13 deletions(-) create mode 100644 ios/App/Podfile.lock diff --git a/ios/App/Podfile.lock b/ios/App/Podfile.lock new file mode 100644 index 00000000..0b74c70b --- /dev/null +++ b/ios/App/Podfile.lock @@ -0,0 +1,199 @@ +PODS: + - ByteowlsCapacitorOauth2 (5.0.0): + - Capacitor + - OAuthSwift (= 2.2.0) + - Capacitor (5.4.2): + - CapacitorCordova + - CapacitorApp (5.0.6): + - Capacitor + - CapacitorBrowser (5.1.0): + - Capacitor + - CapacitorClipboard (5.0.6): + - Capacitor + - CapacitorCordova (5.4.2) + - CapacitorDevice (5.0.6): + - Capacitor + - CapacitorHaptics (5.0.6): + - Capacitor + - CapacitorKeyboard (5.0.6): + - Capacitor + - CapacitorLocalNotifications (5.0.6): + - Capacitor + - CapacitorNavigationbarnx (0.0.2): + - Capacitor + - CapacitorPreferences (5.0.6): + - Capacitor + - CapacitorPushNotifications (5.1.0): + - Capacitor + - CapacitorSplashScreen (5.0.6): + - Capacitor + - CapacitorStatusBar (5.0.6): + - Capacitor + - CapacitorToast (5.0.6): + - Capacitor + - CordovaPlugins (5.4.2): + - CapacitorCordova + - Firebase/CoreOnly (9.2.0): + - FirebaseCore (= 9.2.0) + - Firebase/Messaging (9.2.0): + - Firebase/CoreOnly + - FirebaseMessaging (~> 9.2.0) + - FirebaseCore (9.2.0): + - FirebaseCoreDiagnostics (~> 9.0) + - FirebaseCoreInternal (~> 9.0) + - GoogleUtilities/Environment (~> 7.7) + - GoogleUtilities/Logger (~> 7.7) + - FirebaseCoreDiagnostics (9.2.0): + - GoogleDataTransport (< 10.0.0, >= 9.1.4) + - GoogleUtilities/Environment (~> 7.7) + - GoogleUtilities/Logger (~> 7.7) + - nanopb (< 2.30910.0, >= 2.30908.0) + - FirebaseCoreInternal (9.2.0): + - "GoogleUtilities/NSData+zlib (~> 7.7)" + - FirebaseInstallations (9.2.0): + - FirebaseCore (~> 9.0) + - GoogleUtilities/Environment (~> 7.7) + - GoogleUtilities/UserDefaults (~> 7.7) + - PromisesObjC (~> 2.1) + - FirebaseMessaging (9.2.0): + - FirebaseCore (~> 9.0) + - FirebaseInstallations (~> 9.0) + - GoogleDataTransport (< 10.0.0, >= 9.1.4) + - GoogleUtilities/AppDelegateSwizzler (~> 7.7) + - GoogleUtilities/Environment (~> 7.7) + - GoogleUtilities/Reachability (~> 7.7) + - GoogleUtilities/UserDefaults (~> 7.7) + - nanopb (< 2.30910.0, >= 2.30908.0) + - GoogleDataTransport (9.1.4): + - GoogleUtilities/Environment (~> 7.7) + - nanopb (< 2.30910.0, >= 2.30908.0) + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/AppDelegateSwizzler (7.7.0): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Environment (7.7.0): + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/Logger (7.7.0): + - GoogleUtilities/Environment + - GoogleUtilities/Network (7.7.0): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (7.7.0)" + - GoogleUtilities/Reachability (7.7.0): + - GoogleUtilities/Logger + - GoogleUtilities/UserDefaults (7.7.0): + - GoogleUtilities/Logger + - nanopb (2.30909.0): + - nanopb/decode (= 2.30909.0) + - nanopb/encode (= 2.30909.0) + - nanopb/decode (2.30909.0) + - nanopb/encode (2.30909.0) + - OAuthSwift (2.2.0) + - PromisesObjC (2.1.1) + +DEPENDENCIES: + - "ByteowlsCapacitorOauth2 (from `../../node_modules/@byteowls/capacitor-oauth2`)" + - "Capacitor (from `../../node_modules/@capacitor/ios`)" + - "CapacitorApp (from `../../node_modules/@capacitor/app`)" + - "CapacitorBrowser (from `../../node_modules/@capacitor/browser`)" + - "CapacitorClipboard (from `../../node_modules/@capacitor/clipboard`)" + - "CapacitorCordova (from `../../node_modules/@capacitor/ios`)" + - "CapacitorDevice (from `../../node_modules/@capacitor/device`)" + - "CapacitorHaptics (from `../../node_modules/@capacitor/haptics`)" + - "CapacitorKeyboard (from `../../node_modules/@capacitor/keyboard`)" + - "CapacitorLocalNotifications (from `../../node_modules/@capacitor/local-notifications`)" + - CapacitorNavigationbarnx (from `../../node_modules/capacitor-navigationbarnx`) + - "CapacitorPreferences (from `../../node_modules/@capacitor/preferences`)" + - "CapacitorPushNotifications (from `../../node_modules/@capacitor/push-notifications`)" + - "CapacitorSplashScreen (from `../../node_modules/@capacitor/splash-screen`)" + - "CapacitorStatusBar (from `../../node_modules/@capacitor/status-bar`)" + - "CapacitorToast (from `../../node_modules/@capacitor/toast`)" + - CordovaPlugins (from `../capacitor-cordova-ios-plugins`) + - Firebase/Messaging + - FirebaseCore + +SPEC REPOS: + trunk: + - Firebase + - FirebaseCore + - FirebaseCoreDiagnostics + - FirebaseCoreInternal + - FirebaseInstallations + - FirebaseMessaging + - GoogleDataTransport + - GoogleUtilities + - nanopb + - OAuthSwift + - PromisesObjC + +EXTERNAL SOURCES: + ByteowlsCapacitorOauth2: + :path: "../../node_modules/@byteowls/capacitor-oauth2" + Capacitor: + :path: "../../node_modules/@capacitor/ios" + CapacitorApp: + :path: "../../node_modules/@capacitor/app" + CapacitorBrowser: + :path: "../../node_modules/@capacitor/browser" + CapacitorClipboard: + :path: "../../node_modules/@capacitor/clipboard" + CapacitorCordova: + :path: "../../node_modules/@capacitor/ios" + CapacitorDevice: + :path: "../../node_modules/@capacitor/device" + CapacitorHaptics: + :path: "../../node_modules/@capacitor/haptics" + CapacitorKeyboard: + :path: "../../node_modules/@capacitor/keyboard" + CapacitorLocalNotifications: + :path: "../../node_modules/@capacitor/local-notifications" + CapacitorNavigationbarnx: + :path: "../../node_modules/capacitor-navigationbarnx" + CapacitorPreferences: + :path: "../../node_modules/@capacitor/preferences" + CapacitorPushNotifications: + :path: "../../node_modules/@capacitor/push-notifications" + CapacitorSplashScreen: + :path: "../../node_modules/@capacitor/splash-screen" + CapacitorStatusBar: + :path: "../../node_modules/@capacitor/status-bar" + CapacitorToast: + :path: "../../node_modules/@capacitor/toast" + CordovaPlugins: + :path: "../capacitor-cordova-ios-plugins" + +SPEC CHECKSUMS: + ByteowlsCapacitorOauth2: dde6b8fdf2995bc7872434c407ee2492ce562638 + Capacitor: 8a9db42d105f55843cd8ed2a3cb54e2b78e7f102 + CapacitorApp: 963d90cb449803d58efc33bb515c81151e69159d + CapacitorBrowser: 31371ef981e378cb592be8cc203a9a4cbf43c486 + CapacitorClipboard: ae6864d6b4ff2243fc7ce19124df307d8d1bb78d + CapacitorCordova: cfcc06b698481da655415985eeb2b8da363f8451 + CapacitorDevice: 9233ba5f0c8f601bf4992006f5d2b2a86b7abc2e + CapacitorHaptics: 186982679c5f4005d1e6d2c719b8251c0d3a6df8 + CapacitorKeyboard: 2f3296af2bc929a5069f02ed264fdce58ed62028 + CapacitorLocalNotifications: 7ed607db672dc2834f22a25422b86e74129a032a + CapacitorNavigationbarnx: a22badd7d90d9715ecc08f3ae3824a57a37724b0 + CapacitorPreferences: 8053abfef3bd11e3e83834792a97e5a6387bf591 + CapacitorPushNotifications: 14425a66891d4134e73b6abe1932eeff37e4389d + CapacitorSplashScreen: f3c7de5468a52893e90f49d033772984270241b4 + CapacitorStatusBar: a73a9ae0dd2ca30ee429782d5d3ae82dcfa80207 + CapacitorToast: 61a9bec819ad3aeccc1ebe3fcbff565c22969c71 + CordovaPlugins: 13d1f5b0b81c2eba67b8f70432c3edb07483877c + Firebase: 4ba896cb8e5105d4b9e247e1c1b6222b548df55a + FirebaseCore: 0e27f2a15d8f7b7ef11e7d93e23b1cbab55d748c + FirebaseCoreDiagnostics: ad3f6c68b7c5b63b7cf15b0785d7137f05f32268 + FirebaseCoreInternal: cb966328b6985dbd6f535e1461291063e1c4a00f + FirebaseInstallations: 21186f0ca7849f90f4a3219fa31a5eca2e30f113 + FirebaseMessaging: 4eaf1b8a7464b2c5e619ad66e9b20ee3e3206b24 + GoogleDataTransport: 5fffe35792f8b96ec8d6775f5eccd83c998d5a3b + GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1 + nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 + OAuthSwift: 75efbb5bd9a4b2b71a37bd7e986bf3f55ddd54c6 + PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb + +PODFILE CHECKSUM: 0abccb83d0d503da1628c7d1ca5579739161bb33 + +COCOAPODS: 1.10.2 diff --git a/src/app/requests/requests.ts b/src/app/requests/requests.ts index f51de761..ba4c724e 100644 --- a/src/app/requests/requests.ts +++ b/src/app/requests/requests.ts @@ -606,6 +606,13 @@ export class RefreshTokenRequest extends APIRequest { requiresAuth = true ignoreFails = true maxCacheAge = 1000 + options = { + url: null, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Accept: 'application/json', + }, + } parse(response: Response): ApiTokenResponse[] { if (response && response.data) return [response.data] as ApiTokenResponse[] diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index 47df85c3..01932332 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -154,19 +154,9 @@ export class ApiService extends CacheModule { const now = Date.now() const req = new RefreshTokenRequest(user.refreshToken) - - const formBody = new FormData() - formBody.set('grant_type', 'refresh_token') - formBody.set('refresh_token', user.refreshToken) - const url = await this.getResourceUrl(req.resource, req.endPoint) - - // use js here for the request since the native http plugin performs inconsistent across platforms with non json requests - const resp = await fetch(url, { - method: 'POST', - body: formBody, - headers: await this.getAuthHeader(true), - }) - const result = await resp.json() + const resp = await this.execute(req) + const response = req.parse(resp) + const result = response[0] console.log('Refresh token', result, resp) if (!result || !result.access_token) { From ceff25df1f36bd64837b4a4cb1dd2c0a77cbdbc2 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Wed, 18 Oct 2023 11:48:26 +0200 Subject: [PATCH 05/53] support for different execution and consensus currencies via network config, gnosis network config now uses xdai for exec and gno for cons --- src/app/components/block/block.component.html | 2 +- .../dashboard/dashboard.component.html | 72 +-- .../dashboard/dashboard.component.ts | 48 +- src/app/components/help/help.component.html | 28 +- .../validator/validator.component.html | 2 +- src/app/models/StorageTypes.ts | 24 +- .../pages/block-detail/block-detail.page.html | 10 +- .../pages/block-detail/block-detail.page.ts | 10 - src/app/pages/dev/dev.page.ts | 2 +- src/app/pages/machines/machines.page.html | 2 +- src/app/pages/machines/machines.page.ts | 4 +- src/app/pipes/mcurrency.pipe.ts | 8 +- src/app/services/api.service.ts | 38 +- src/app/services/storage.service.ts | 15 +- src/app/services/unitconv.service.ts | 418 +++++++++++++----- src/app/tab-blocks/tab-blocks.page.html | 2 +- src/app/tab-blocks/tab-blocks.page.ts | 10 - .../tab-preferences/tab-preferences.page.html | 4 +- .../tab-preferences/tab-preferences.page.ts | 1 + .../tab-validators/tab-validators.page.html | 2 +- src/app/tab-validators/tab-validators.page.ts | 11 +- src/app/utils/CacheModule.ts | 20 +- src/app/utils/EthereumUnits.ts | 41 +- src/app/utils/MerchantUtils.ts | 2 +- src/app/utils/NetworkData.ts | 38 +- src/app/utils/ValidatorUtils.ts | 2 +- 26 files changed, 524 insertions(+), 292 deletions(-) diff --git a/src/app/components/block/block.component.html b/src/app/components/block/block.component.html index 1632d697..efbed75a 100644 --- a/src/app/components/block/block.component.html +++ b/src/app/components/block/block.component.html @@ -7,7 +7,7 @@
- Reward: {{ producerReward | mcurrency : 'WEI' : unit.pref }} + Reward: {{ producerReward | mcurrency : 'WEI' : unit.pref.Exec }} Proposer: {{ block.posConsensus.proposerIndex }} diff --git a/src/app/components/dashboard/dashboard.component.html b/src/app/components/dashboard/dashboard.component.html index 7fd3a232..83afd055 100644 --- a/src/app/components/dashboard/dashboard.component.html +++ b/src/app/components/dashboard/dashboard.component.html @@ -166,10 +166,10 @@
-
+
- {{ data.combinedPerformance.total | mcurrency : 'GWEI' : unit.pref }} + {{ data.combinedPerformance.total | mcurrency : 'GWEI' : unit.pref.Cons }} TODO
@@ -177,7 +177,7 @@
-
+
{{ data.combinedPerformance.apr }} %
@@ -195,10 +195,10 @@
-
+
- {{ data.overallBalance | mcurrency : 'GWEI' : unit.pref }} + {{ data.overallBalance | mcurrency : 'GWEI' : unit.pref.Cons }}
@@ -267,10 +267,10 @@
-
+
- {{ data.consensusPerformance.performance1d | mcurrency : 'GWEI' : unit.pref }} + {{ data.consensusPerformance.performance1d | mcurrency : 'GWEI' : unit.pref.Cons }}
@@ -285,10 +285,10 @@
-
+
- {{ data.consensusPerformance.performance7d | mcurrency : 'GWEI' : unit.pref }} + {{ data.consensusPerformance.performance7d | mcurrency : 'GWEI' : unit.pref.Cons }}
@@ -296,10 +296,10 @@
-
+
- {{ data.consensusPerformance.performance31d | mcurrency : 'GWEI' : unit.pref }} + {{ data.consensusPerformance.performance31d | mcurrency : 'GWEI' : unit.pref.Cons }}
@@ -307,10 +307,10 @@
-
+
- {{ data.consensusPerformance.total | mcurrency : 'GWEI' : unit.pref }} + {{ data.consensusPerformance.total | mcurrency : 'GWEI' : unit.pref.Cons }}
@@ -329,10 +329,10 @@
-
+
- {{ data.executionPerformance.performance31d | mcurrency : 'GWEI' : unit.pref }} + {{ data.executionPerformance.performance31d | mcurrency : 'GWEI' : unit.pref.Exec }}
@@ -347,7 +347,7 @@
-
+
{{ data.executionPerformance.apr }} %
@@ -376,8 +376,8 @@
- - {{ smoothingClaimed.plus(smoothingUnclaimed) | mcurrency : 'GWEI' : unit.pref }} + + {{ smoothingClaimed.plus(smoothingUnclaimed) | mcurrency : 'GWEI' : unit.pref.Exec }}
@@ -393,8 +393,8 @@
- - {{ smoothingUnclaimed | mcurrency : 'GWEI' : unit.pref }} + + {{ smoothingUnclaimed | mcurrency : 'GWEI' : unit.pref.Exec }}
@@ -424,7 +424,7 @@ {{ rplProjectedClaim | mcurrency : 'WEI' : 'RPL_NAKED' }} - {{ rplProjectedClaim | mcurrency : 'WEI' : 'RPL' : false | mcurrency : 'ETHER' : unit.pref }} + {{ rplProjectedClaim | mcurrency : 'WEI' : 'RPL' : false | mcurrency : 'ETHER' : unit.pref.RPL }}
@@ -440,7 +440,7 @@ {{ totalRplEarned | mcurrency : 'WEI' : 'RPL_NAKED' }} - {{ totalRplEarned | mcurrency : 'WEI' : 'RPL' : false | mcurrency : 'ETHER' : unit.pref }} + {{ totalRplEarned | mcurrency : 'WEI' : 'RPL' : false | mcurrency : 'ETHER' : unit.pref.RPL }}
@@ -462,7 +462,7 @@ {{ unclaimedRpl | mcurrency : 'WEI' : 'RPL_NAKED' }} - {{ unclaimedRpl | mcurrency : 'WEI' : 'RPL' : false | mcurrency : 'ETHER' : unit.pref }} + {{ unclaimedRpl | mcurrency : 'WEI' : 'RPL' : false | mcurrency : 'ETHER' : unit.pref.RPL }}
@@ -501,10 +501,10 @@
-
+
- {{ 1 | mcurrency : 'ETHER' : 'RPL' : false | mcurrency : 'ETHER' : unit.pref }} + {{ 1 | mcurrency : 'ETHER' : 'RPL' : false | mcurrency : 'ETHER' : unit.pref.RPL }}
@@ -535,7 +535,7 @@ class="value left" *ngIf="rplState == 'conv'" [class]="data.rocketpool.currentRpl | valuestyle : data.rocketpool.minRpl : data.rocketpool.maxRpl : -1"> - {{ data.rocketpool.currentRpl | mcurrency : 'WEI' : 'RPL' : false | mcurrency : 'ETHER' : unit.pref }} + {{ data.rocketpool.currentRpl | mcurrency : 'WEI' : 'RPL' : false | mcurrency : 'ETHER' : unit.pref.RPL }} - {{ data.rocketpool.minRpl | mcurrency : 'WEI' : 'RPL' : false | mcurrency : 'ETHER' : unit.pref }} - - {{ data.rocketpool.maxRpl | mcurrency : 'WEI' : 'RPL' : false | mcurrency : 'ETHER' : unit.pref }} + {{ data.rocketpool.minRpl | mcurrency : 'WEI' : 'RPL' : false | mcurrency : 'ETHER' : unit.pref.RPL }} - + {{ data.rocketpool.maxRpl | mcurrency : 'WEI' : 'RPL' : false | mcurrency : 'ETHER' : unit.pref.RPL }}
@@ -632,8 +632,8 @@ data.foreignValidator ">
- - {{ data.foreignValidatorItem.rocketpool.node_deposit_balance | mcurrency : 'WEI' : unit.pref }} + + {{ data.foreignValidatorItem.rocketpool.node_deposit_balance | mcurrency : 'WEI' : unit.pref.Cons }}
@@ -812,7 +812,7 @@ Effective Balance - + {{ data.effectiveBalance | mcurrency : 'GWEI' : 'ETHER' }} @@ -846,15 +846,15 @@ Effective Balance - + {{ data.effectiveBalance | mcurrency : 'GWEI' : 'ETHER' }} - - Ether Price - - {{ 1 | mcurrency : 'ETHER' : unit.pref }} + + {{ unit.pref.Cons.getCurrencyName() }} Price + + {{ 1 | mcurrency : 'ETHER' : unit.pref.Cons }} diff --git a/src/app/components/dashboard/dashboard.component.ts b/src/app/components/dashboard/dashboard.component.ts index 6976347e..fcd391b5 100644 --- a/src/app/components/dashboard/dashboard.component.ts +++ b/src/app/components/dashboard/dashboard.component.ts @@ -19,7 +19,7 @@ */ import { Component, OnInit, Input, SimpleChange } from '@angular/core' -import { UnitconvService } from '../../services/unitconv.service' +import { RewardType, UnitconvService } from '../../services/unitconv.service' import { ApiService } from '../../services/api.service' import { DashboardDataRequest, EpochResponse, SyncCommitteeResponse } from '../../requests/requests' import * as HighCharts from 'highcharts' @@ -196,7 +196,7 @@ export class DashboardComponent implements OnInit { updateDepositCreditText() { if (this.data.rocketpool.depositCredit && this.data.rocketpool.depositCredit.gt(0)) { - this.depositCreditText = `You have ${this.unit.convert( + this.depositCreditText = `You have ${this.unit.convertNonFiat( this.data.rocketpool.depositCredit, 'WEI', 'ETHER', @@ -395,26 +395,6 @@ export class DashboardComponent implements OnInit { return await modal.present() } - switchCurrencyPipe() { - if (this.unit.pref == 'ETHER') { - if (UnitconvService.currencyPipe == null) return - this.unit.pref = UnitconvService.currencyPipe - } else { - UnitconvService.currencyPipe = this.unit.pref - this.unit.pref = 'ETHER' - } - } - - switchCurrencyPipeRocketpool() { - if (this.unit.prefRpl == 'RPL') { - if (UnitconvService.currencyPipe == null) return - this.unit.prefRpl = UnitconvService.currencyPipe - } else { - UnitconvService.currencyPipe = this.unit.pref - this.unit.prefRpl = 'RPL' - } - } - switchRplStake(canPercent = false) { if (this.rplState == 'rpl' && canPercent) { // next % @@ -684,11 +664,18 @@ export class DashboardComponent implements OnInit { const ticksDecimalPlaces = 3 const network = this.api.getNetwork() - const getValueString = (value: BigNumber): string => { + const getValueString = (value: BigNumber, type: RewardType): string => { let text = `${value.toFixed(5)} ETH` - if (this.unit.pref != 'ETHER' && network.key == 'main') { - text += ` (${this.unit.convertToPref(value, 'ETHER')})` + if (type == 'cons') { + if (this.unit.isDefaultCurrency(this.unit.pref.Cons) && network.key == 'main') { + text += ` (${this.unit.convertToPref(value, 'ETHER', type)})` + } + } else if (type == 'exec') { + if (this.unit.isDefaultCurrency(this.unit.pref.Exec) && network.key == 'main') { + text += ` (${this.unit.convertToPref(value, 'ETHER', type)})` + } } + return text } @@ -737,18 +724,23 @@ export class DashboardComponent implements OnInit { let text = this.getChartToolTipCaption(tooltip.chart.hoverPoints[0].x, network.genesisTs, tooltip.chart.hoverPoints[0].dataGroup.length) // income + let consAndExecSameCurrency = 0 let total = new BigNumber(0) for (let i = 0; i < tooltip.chart.hoverPoints.length; i++) { + const type = tooltip.chart.hoverPoints[i].series.name == 'Execution' ? 'exec' : 'cons' + consAndExecSameCurrency += type == 'exec' ? 1 : -1 + const value = new BigNumber(tooltip.chart.hoverPoints[i].y) text += ` ${ tooltip.chart.hoverPoints[i].series.name - }: ${getValueString(value)}
` + }: ${getValueString(value, type)}
` total = total.plus(value) } // add total if hovered point contains rewards for both EL and CL - if (tooltip.chart.hoverPoints.length > 1) { - text += `Total: ${getValueString(total)}` + // only if both exec and cons currencies are the same + if (tooltip.chart.hoverPoints.length > 1 && Math.abs(consAndExecSameCurrency) == 2) { + text += `Total: ${getValueString(total, 'cons')}` } return text diff --git a/src/app/components/help/help.component.html b/src/app/components/help/help.component.html index b683ec35..7572d551 100644 --- a/src/app/components/help/help.component.html +++ b/src/app/components/help/help.component.html @@ -61,17 +61,19 @@
- - - - Node Guide & FAQ - - - - - Enable Withdrawals Guide - - + + + + + Node Guide & FAQ + + + + + Enable Withdrawals Guide + + + How to set up a Validator @@ -104,12 +106,14 @@ - + + Interactive Setup Guide + Useful Links diff --git a/src/app/components/validator/validator.component.html b/src/app/components/validator/validator.component.html index 160f54ab..3e37e9ab 100644 --- a/src/app/components/validator/validator.component.html +++ b/src/app/components/validator/validator.component.html @@ -28,7 +28,7 @@
{{ name }} - {{ balance | mcurrency : 'GWEI' : unit.pref }} + {{ balance | mcurrency : 'GWEI' : unit.pref.Cons }} {{ validator.attrEffectiveness && validator.attrEffectiveness != -1 ? 'Eff.:' + validator.attrEffectiveness + '%' : '' }} diff --git a/src/app/models/StorageTypes.ts b/src/app/models/StorageTypes.ts index beb75ecd..f4c370dc 100644 --- a/src/app/models/StorageTypes.ts +++ b/src/app/models/StorageTypes.ts @@ -34,14 +34,26 @@ export interface ApiNetwork { onlyDebug: boolean active: boolean genesisTs: number - elCurrency: Currency - clCurrency: Currency + elCurrency: NetworkMainCurrency + clCurrency: NetworkMainCurrency + name: string } -export enum Currency { - ETH = 'ETH', - GNO = 'GNO', - xDAI = 'xDAI', + + +export class NetworkMainCurrency { + static readonly ETH = new NetworkMainCurrency('ETHER', 'Ether', 'ETH') + static readonly GNO = new NetworkMainCurrency('GNO', 'mGNO', 'GNO') + static readonly xDAI = new NetworkMainCurrency('xDAI', 'xDAI', 'DAI') + + public internalName: string + public formattedName: string + public coinbaseSpot: string + private constructor(internName: string, formattedName: string, coinbaseSpot: string) { + this.internalName = internName + this.coinbaseSpot = coinbaseSpot + this.formattedName = formattedName + } } export interface NetworkPreferences { diff --git a/src/app/pages/block-detail/block-detail.page.html b/src/app/pages/block-detail/block-detail.page.html index 4e9b6942..012c47da 100644 --- a/src/app/pages/block-detail/block-detail.page.html +++ b/src/app/pages/block-detail/block-detail.page.html @@ -23,8 +23,8 @@
- - {{ producerReward | mcurrency: "WEI":unit.pref }} + + {{ producerReward | mcurrency: "WEI":unit.pref.Exec }}
@@ -51,14 +51,14 @@ Producer Reward - - {{ block.producerReward | mcurrency: "WEI":unit.pref }} + + {{ block.producerReward | mcurrency: "WEI":unit.pref.Exec }} Burned - {{ burned | mcurrency: "WEI":unit.pref }} + {{ burned | mcurrency: "WEI":unit.pref.Exec }} diff --git a/src/app/pages/block-detail/block-detail.page.ts b/src/app/pages/block-detail/block-detail.page.ts index 8a2f52d3..59511df1 100644 --- a/src/app/pages/block-detail/block-detail.page.ts +++ b/src/app/pages/block-detail/block-detail.page.ts @@ -78,16 +78,6 @@ export class BlockDetailPage implements OnInit { this.showGasUsedPercent = !this.showGasUsedPercent } - switchCurrencyPipe() { - if (this.unit.pref == 'ETHER') { - if (UnitconvService.currencyPipe == null) return - this.unit.pref = UnitconvService.currencyPipe - } else { - UnitconvService.currencyPipe = this.unit.pref - this.unit.pref = 'ETHER' - } - } - async openBlock() { await Browser.open({ url: this.api.getBaseUrl() + '/block/' + this.block.blockNumber, diff --git a/src/app/pages/dev/dev.page.ts b/src/app/pages/dev/dev.page.ts index ac036e37..1e79b741 100644 --- a/src/app/pages/dev/dev.page.ts +++ b/src/app/pages/dev/dev.page.ts @@ -4,7 +4,7 @@ import { CURRENT_TOKENKEY } from 'src/app/utils/FirebaseUtils' import { Tab3Page } from 'src/app/tab-preferences/tab-preferences.page' import { Toast } from '@capacitor/toast' import { Clients } from '../../utils/ClientUpdateUtils' -import { DevModeEnabled } from 'src/app/services/api.service' +import { DevModeEnabled } from 'src/app/services/storage.service' @Component({ selector: 'app-dev', diff --git a/src/app/pages/machines/machines.page.html b/src/app/pages/machines/machines.page.html index 83a9e918..31d64f0c 100644 --- a/src/app/pages/machines/machines.page.html +++ b/src/app/pages/machines/machines.page.html @@ -94,7 +94,7 @@ Machine Monitoring

- Monitor your Ethereum staking machines on the go. + Monitor your {{ api.getNetwork().name }} staking machines on the go.

Login { + this.storage.isDebugMode().then((result) => { this.debug = result window.localStorage.setItem('debug', this.debug ? 'true' : 'false') }) @@ -201,6 +200,10 @@ export class ApiService extends CacheModule { return test } + isMainnet(): boolean { + return !this.isNotMainnet() + } + private getCacheKey(request: APIRequest): string { if (request.method == Method.GET) { return request.method + this.getResourceUrl(request.resource, request.endPoint) @@ -243,7 +246,7 @@ export class ApiService extends CacheModule { await this.lockOrWait(request.resource) - console.log(LOGTAG + ' Send request: ' + request.resource, request) + console.log(LOGTAG + ' Send request: ' + request.resource, request.method, request) const startTs = Date.now() if (this.forceNativeAll) { @@ -448,15 +451,9 @@ export class ApiService extends CacheModule { return cfg.protocol + '://' + cfg.net + cfg.host } - private async isDebugMode() { - const devMode = isDevMode() - if (devMode) return true - const permanentDevMode = (await this.storage.getObject('dev_mode')) as DevModeEnabled - return permanentDevMode && permanentDevMode.enabled - } async getAllTestNetNames() { - const debug = await this.isDebugMode() + const debug = await this.storage.isDebugMode() const re: string[][] = [] for (const entry of MAP) { @@ -478,18 +475,33 @@ export class ApiService extends CacheModule { return network.host } + /** + * Avoid whenever possible. Most of the time you can archive your goal by using the + * api.getNetwork().clCurrency or api.getNetwork().elCurrency for currencies. + * And api.getNetwork().name for the network name and api.getCurrenciesFormatted() + * for a formatted output of one/both currencies. + * @returns true if the current network is the mainnet + */ isGnosis() { return this.networkConfig.key == 'gnosis' } + + /** + * Returns the formatted currencies for the network + */ + public getCurrenciesFormatted(): string { + const network = this.networkConfig + if (network.elCurrency.internalName == network.clCurrency.internalName) { + return network.clCurrency.formattedName + } + return network.clCurrency.formattedName + ' / ' + network.elCurrency.formattedName + } } export interface Response extends HttpResponse { cached: boolean } -export interface DevModeEnabled { - enabled: boolean -} export function initializeApiService(service: ApiService): () => Promise { return () => service.initialize() diff --git a/src/app/services/storage.service.ts b/src/app/services/storage.service.ts index e123e441..70324297 100644 --- a/src/app/services/storage.service.ts +++ b/src/app/services/storage.service.ts @@ -18,7 +18,7 @@ * // along with Beaconchain Dashboard. If not, see . */ -import { Injectable } from '@angular/core' +import { Injectable, isDevMode } from '@angular/core' import { Plugins } from '@capacitor/core' import * as StorageTypes from '../models/StorageTypes' import { findConfigForKey } from '../utils/NetworkData' @@ -76,6 +76,13 @@ export class StorageService extends CacheModule { return this.remove(AUTH_USER) } + public async isDebugMode() { + const devMode = isDevMode() + if (devMode) return true + const permanentDevMode = (await this.getObject('dev_mode')) as DevModeEnabled + return permanentDevMode && permanentDevMode.enabled + } + async getNetworkPreferences(): Promise { const result = await this.getObject(PREFERENCES) if (!result) { @@ -235,7 +242,7 @@ export class StorageService extends CacheModule { } } -function replacer(key, value) { +export function replacer(key, value) { const originalObject = this[key] if (originalObject instanceof Map) { return { @@ -271,3 +278,7 @@ export interface StoredTimestamp { export interface StoredShare { share: number } + +export interface DevModeEnabled { + enabled: boolean +} \ No newline at end of file diff --git a/src/app/services/unitconv.service.ts b/src/app/services/unitconv.service.ts index 7a921645..3586cfce 100644 --- a/src/app/services/unitconv.service.ts +++ b/src/app/services/unitconv.service.ts @@ -25,185 +25,377 @@ import BigNumber from 'bignumber.js' import { ApiService } from './api.service' import { CoinbaseExchangeRequest, CoinbaseExchangeResponse } from '../requests/requests' -const STORAGE_KEY = 'prefered_unit' -const STORAGE_KEY_ROCKETPOOL = 'prefered_unit_rocketpool' +const STORAGE_KEY_CONS = 'prefered_unit' // cons +const STORAGE_KEY_EXEC = 'prefered_unit_exec' +const STORAGE_KEY_RPL = 'prefered_unit_rocketpool' + +export type RewardType = 'cons' | 'exec' | 'rpl' @Injectable({ providedIn: 'root', }) export class UnitconvService { - pref = 'ETHER' - prefRpl = 'RPL' - lastPrice: BigNumber + + public pref: PreferredCurrency = { + Cons: { value: 'ETHER', type: 'cons', unit: Unit.ETHER } as Currency, + Exec: { value: 'ETHER', type: 'exec', unit: Unit.ETHER } as Currency, + RPL: { value: 'ETHER', type: 'rpl', unit: Unit.RPL } as Currency, + } + private lastPrice: LastPrice + private rplETHPrice: BigNumber = new BigNumber(1) - static currencyPipe = null + /* + Users can quickly toggle between native currency and fiat currency by clicking on the value. + This variable holds the previous setting (native or fiat) for each currency type (cons, exec, rpl. + */ + static currencyPipe: CurrencyPipe = { Cons: null, Exec: null, RPL: null } constructor(private storage: StorageService, private api: ApiService) { - this.storage.getObject(STORAGE_KEY).then((unitPref) => this.init(unitPref)) - this.storage.getObject(STORAGE_KEY_ROCKETPOOL).then((unitPref) => (this.prefRpl = this.getPref(unitPref, 'RPL'))) + this.init() } - private async getExchangeRate(unitPair: string): Promise { - if (unitPair == 'ETH-BTC') return this.getExchangeRateBitcoin() + private async init() { + await this.migrateToPostGnosisEra() - const req = new CoinbaseExchangeRequest(unitPair) - const response = await this.api.execute(req).catch((e) => { - console.warn('error in response getExchangeRate', e) - return null - }) - const temp = req.parse(response) - if (temp.length <= 0) return null - return temp[0] - } + this.lastPrice = { + Cons: await this.getLastStoredPrice(this.pref.Cons), + Exec: await this.getLastStoredPrice(this.pref.Exec), + } as LastPrice - // Special bitcoin case since coinbase doesn't have an ETH_BTC spot price api endpoint - private async getExchangeRateBitcoin(): Promise { - const reqEthUsd = new CoinbaseExchangeRequest('ETH-USD') - const reqBtcUsd = new CoinbaseExchangeRequest('BTC-USD') + this.pref.Cons = this.createCurrency(this.getPref(await this.loadStored(STORAGE_KEY_CONS), this.getNetworkDefaultCurrency('cons')), 'cons') + this.pref.Exec = this.createCurrency(this.getPref(await this.loadStored(STORAGE_KEY_EXEC), this.getNetworkDefaultCurrency('exec')), 'exec') + this.pref.RPL = this.createCurrency(this.getPref(await this.loadStored(STORAGE_KEY_RPL), this.getNetworkDefaultCurrency('rpl')), 'rpl') - const responseEthUsdPromise = this.api.execute(reqEthUsd) - const responseBtcUsdPromise = this.api.execute(reqBtcUsd) + if (!(await this.storage.getBooleanSetting('UPDATED_CURRENCY_INTEROP', false))) { + this.storage.setBooleanSetting('UPDATED_CURRENCY_INTEROP', true) + this.save() + } - const responseEthUsd = reqEthUsd.parse(await responseEthUsdPromise) - const responseBtcUsd = reqBtcUsd.parse(await responseBtcUsdPromise) - if (responseEthUsd.length <= 0 || responseBtcUsd.length <= 0) return null + await this.updatePriceData() + } - const rate = new BigNumber(responseEthUsd[0].amount).dividedBy(new BigNumber(responseBtcUsd[0].amount)) + public async networkSwitchReload() { + await this.init() + } - return { - base: 'ETH', - currency: 'BTC', - amount: rate.toString(), - } as CoinbaseExchangeResponse + private async loadStored(key: string): Promise { + const result = await this.storage.getObject(key) + if (!result) return null + return result as StoredPref } - async init(unitPref) { - const temp = this.getPref(unitPref) + private createCurrency(value: string, type: RewardType): Currency { + const result = new Currency(value, type) + result.unit = this.getUnit(result) + return result + } - this.pref = temp + private getPref(unitPref: StoredPref, defaultva: string): string { + if (unitPref && unitPref.prefered) { + return unitPref.prefered + } + return defaultva + } - const unit: Unit = this.getCurrentPrefAsUnit() + private getUnit(currency: Currency): Unit { + const result = Object.assign({}, MAPPING.get(currency.value)) + if (!result) { + return Unit.ETHER + } - const lastUpdatedPrice = (await this.storage.getObject('last_price_' + this.pref)) as LastPrice + // Price + let price = null + if (this.lastPrice) { + if (currency.type == 'cons' && this.lastPrice.Cons) { + price = this.lastPrice.Cons + } else if (currency.type == 'exec' && this.lastPrice.Exec) { + price = this.lastPrice.Exec + } else if (currency.type == 'rpl' && this.rplETHPrice && this.lastPrice.Cons) { + price = this.getUpdatedRPLPrice() + } + } - if (lastUpdatedPrice && lastUpdatedPrice.lastPrice) { - const price = new BigNumber(lastUpdatedPrice.lastPrice) - this.lastPrice = price - } else { - this.lastPrice = unit.value + if (this.isDefaultCurrency(currency)) { + result.value = new BigNumber(1) + } else if (price) { + result.value = price } - if (!(await this.storage.getBooleanSetting('UPDATED_CURRENCY_INTEROP', false))) { - this.storage.setBooleanSetting('UPDATED_CURRENCY_INTEROP', true) - this.save() + // Coinbase spot name (for api calls) + if (result.coinbaseSpot) { + const network = this.api.getNetwork() + if (currency.type == 'cons') { + result.coinbaseSpot = result.coinbaseSpot.replace('XXX', network.clCurrency.coinbaseSpot) + } else if (currency.type == 'exec') { + result.coinbaseSpot = result.coinbaseSpot.replace('XXX', network.elCurrency.coinbaseSpot) + } } + return result + } - this.updatePriceData() + public isDefaultCurrency(currency: Currency) { + return currency.value == this.getNetworkDefaultCurrency(currency.type) } - private getCurrentPrefAsUnit() { - return this.getCurrentyAsUnit(this.pref) + public getNetworkDefaultCurrency(type: RewardType | Currency): string { + if (typeof type == 'object') { + type = type.type + } + + const network = this.api.getNetwork() + if (type == 'cons') { + return network.clCurrency.internalName + } else if (type == 'exec') { + return network.elCurrency.internalName + } else if (type == 'rpl') { + return 'RPL' + } + + return 'ETHER' } - private getCurrentyAsUnit(currency) { - const unitStored: Unit = MAPPING.get(currency) - return unitStored ? unitStored : Unit.ETHER + private async getLastStoredPrice(currency: Currency) { + const lastUpdatedConsPrice = (await this.storage.getObject(this.getLastPriceKey(currency))) as LastPrice + if (lastUpdatedConsPrice && lastUpdatedConsPrice.lastPrice) { + const price = new BigNumber(lastUpdatedConsPrice.lastPrice) + return price + } else { + return currency.unit.value + } + } + + private getLastPriceKey(currency: Currency): string { + return 'last_price_' + currency.type + '_' + currency.value } - setRPLPrice(price: BigNumber) { - const unitStored: Unit = MAPPING.get('RPL') + public setRPLPrice(price: BigNumber) { + const unitStored: Unit = this.pref.RPL.unit if (!unitStored) return unitStored.value = convertEthUnits(price, MAPPING.get('WEI'), Unit.ETHER) + this.rplETHPrice = unitStored.value + this.pref.RPL = this.createCurrency(this.pref.Cons.value, 'rpl') } - getRPLPrice() { - const unitStored: Unit = MAPPING.get('RPL') - if (!unitStored) return - return unitStored.value + private getUpdatedRPLPrice() { + return this.rplETHPrice.multipliedBy(this.isDefaultCurrency(this.pref.Cons) ? new BigNumber(1) : this.lastPrice.Cons) } - setRETHPrice(price: BigNumber) { - const unitStored: Unit = MAPPING.get('RETH') + public getRPLPrice() { + const unitStored: Unit = this.pref.RPL.unit if (!unitStored) return - unitStored.value = convertEthUnits(price, Unit.WEI, Unit.ETHER) + return unitStored.value } - async updatePriceData() { - const unit: Unit = this.getCurrentPrefAsUnit() - if (unit.coinbaseSpot) { - const exchangeRate = await this.getExchangeRate(unit.coinbaseSpot) - const bigNumAmount = exchangeRate ? new BigNumber(exchangeRate.amount) : null - if (bigNumAmount && bigNumAmount.isGreaterThan(0)) { - unit.value = bigNumAmount - this.lastPrice = bigNumAmount - this.triggerPropertyChange() - this.storage.setObject('last_price_' + this.pref, { lastPrice: bigNumAmount } as LastPrice) - } else { - // Handles the case if we get no price data atm - // Currently we fall back to ether being the default unit (since price is 1:1) - // (TODO: we could do this for all eth subunits fe. finney) - this.lastPrice = unit.value - this.pref = 'ETHER' - } + private triggerPropertyChange() { + if (this.isDefaultCurrency(this.pref.Cons)) { + this.pref.Cons = this.createCurrency(this.getNetworkDefaultCurrency(this.pref.Cons), 'cons') + this.pref.Exec = this.createCurrency(this.getNetworkDefaultCurrency(this.pref.Cons), 'exec') + this.pref.RPL = this.createCurrency(this.getNetworkDefaultCurrency(this.pref.RPL), 'rpl') + return } + + this.pref.Cons = this.createCurrency(this.pref.Cons.value, 'cons') + this.pref.Exec = this.createCurrency(this.pref.Exec.value, 'exec') + this.pref.RPL = this.createCurrency(this.pref.RPL.value, 'rpl') } - private triggeredChange = false - private triggerPropertyChange() { - this.triggeredChange = true + public convertToPref(value: BigNumber, from, type: RewardType) { + if (type == 'cons') { + return this.convert(value, from, this.pref.Cons) + } + if (type == 'exec') { + return this.convert(value, from, this.pref.Exec) + } + throw new Error('Unsupported reward type') + } - const temp = this.pref - this.pref = 'ETHER' - this.pref = 'FINNEY' + public switchCurrencyPipe() { + if (this.isDefaultCurrency(this.pref.Cons)) { + if (UnitconvService.currencyPipe.Cons == null) return + this.pref.Cons = this.createCurrency(UnitconvService.currencyPipe.Cons, 'cons') + } else { + UnitconvService.currencyPipe.Cons = this.pref.Cons.value + this.pref.Cons = this.createCurrency(this.getNetworkDefaultCurrency('cons'), 'cons') + } - const temp2 = this.prefRpl - this.prefRpl = 'ETHER' - this.prefRpl = 'RPL' + if (this.isDefaultCurrency(this.pref.Exec)) { + if (UnitconvService.currencyPipe.Exec == null) return + this.pref.Exec = this.createCurrency(UnitconvService.currencyPipe.Exec, 'exec') + } else { + UnitconvService.currencyPipe.Exec = this.pref.Exec.value + this.pref.Exec = this.createCurrency(this.getNetworkDefaultCurrency('exec'), 'exec') + } - setTimeout(() => { - this.pref = temp - this.prefRpl = temp2 - this.triggeredChange = false - }, 450) + this.pref.RPL = this.createCurrency(this.pref.Cons.value, 'rpl') } - private getPref(unitPref, defaultva = 'ETHER'): string { - if (unitPref) { - return unitPref.prefered - } - return defaultva + public convert(value: BigNumber | number | string, from: string, to: Currency, displayable = true) { + return this.convertBase(value, from, to.unit, displayable) } - convertToPref(value: BigNumber, from) { - return this.convert(value, from, this.pref) + /** + * Does not support fiat currencies since exchange rate is not applied to units anymore + * (cons and exec could have different conversions so they cant use the same reference to unit) + */ + public convertNonFiat(value: BigNumber | number | string, from: string, to: string, displayable = true) { + if (MAPPING.get(to).coinbaseSpot != null && to != 'ETH' && to != 'ETHER') { + console.warn('convertNonFiat does not support fiat currencies. Use convert instead', value.toString(), from, to, displayable) + } + return this.convertBase(value, from, MAPPING.get(to), displayable) } - convert(value: BigNumber | number | string, from: string, to: string, displayable = true) { + private convertBase(value: BigNumber | number | string, from: string, to: Unit, displayable = true) { if (!value || !from || !to) return value const tempValue = value instanceof BigNumber ? value : new BigNumber(value) if (displayable) { - return convertDisplayable(tempValue, MAPPING.get(from), MAPPING.get(to)) + return convertDisplayable(tempValue, MAPPING.get(from), to) } else { - return convertEthUnits(tempValue, MAPPING.get(from), MAPPING.get(to), false) + return convertEthUnits(tempValue, MAPPING.get(from), to, false) + } + } + + public save() { + this.storage.setObject(STORAGE_KEY_CONS, { + prefered: this.getNetworkDefaultCurrency('cons') == this.pref.Cons.value ? null : this.pref.Cons.value, + coinbaseSpot: this.pref.Cons.unit.coinbaseSpot, + symbol: this.pref.Cons.unit.display, + rounding: this.pref.Cons.unit.rounding, + } as StoredPref) + + this.storage.setObject(STORAGE_KEY_EXEC, { + prefered: this.getNetworkDefaultCurrency('exec') == this.pref.Exec.value ? null : this.pref.Exec.value, + coinbaseSpot: this.pref.Exec.unit.coinbaseSpot, + symbol: this.pref.Exec.unit.display, + rounding: this.pref.Exec.unit.rounding, + } as StoredPref) + + this.storage.setObject(STORAGE_KEY_RPL, { + prefered: this.getNetworkDefaultCurrency('rpl') == this.pref.RPL.value ? null : this.pref.RPL.value, + coinbaseSpot: this.pref.RPL.unit.coinbaseSpot, + symbol: this.pref.RPL.unit.display, + rounding: this.pref.RPL.unit.rounding, + } as StoredPref) + } + + private async migrateToPostGnosisEra() { + const migratedToGnosis = await this.storage.getBooleanSetting('migrated_gnosis', false) + if (!migratedToGnosis) { + const oldCons = await this.loadStored(STORAGE_KEY_CONS) + try { + if (oldCons && oldCons.prefered == 'Ether') { + oldCons.prefered = null + await this.storage.setObject(STORAGE_KEY_CONS, oldCons) + } else { + await this.storage.setObject(STORAGE_KEY_EXEC, oldCons) + await this.storage.setObject(STORAGE_KEY_RPL, oldCons) + } + this.storage.setBooleanSetting('migrated_gnosis', true) + } catch (e) { + console.warn('could not migrate to gnosis') + } } } - save() { - if (this.triggeredChange) return - const unit = this.getCurrentPrefAsUnit() - this.storage.setObject(STORAGE_KEY, { prefered: this.pref, coinbaseSpot: unit.coinbaseSpot, symbol: unit.display, rounding: unit.rounding }) + public isCurrency(obj: unknown): obj is Currency { + return typeof obj == 'object' && obj != null && 'value' in obj && 'type' in obj + } + + async updatePriceData() { + const consPrice = await this.getPriceData(this.pref.Cons.unit) + if (consPrice) { + this.lastPrice.Cons = consPrice + this.storage.setObject(this.getLastPriceKey(this.pref.Cons), { lastPrice: consPrice } as LastPrice) + } else { + this.lastPrice.Cons = this.pref.Cons.unit.value + this.pref.Cons.value = this.getNetworkDefaultCurrency(this.pref.Cons) + } + + const execPrice = await this.getPriceData(this.pref.Exec.unit) + if (execPrice) { + this.lastPrice.Exec = execPrice + this.storage.setObject(this.getLastPriceKey(this.pref.Exec), { lastPrice: execPrice } as LastPrice) + } else { + this.lastPrice.Exec = this.pref.Exec.unit.value + this.pref.Exec.value = this.getNetworkDefaultCurrency(this.pref.Exec) + } - const rplUnit = this.getCurrentyAsUnit(this.prefRpl) - this.storage.setObject(STORAGE_KEY_ROCKETPOOL, { - prefered: this.prefRpl, - coinbaseSpot: rplUnit.coinbaseSpot, - symbol: rplUnit.display, - rounding: rplUnit.rounding, + this.triggerPropertyChange() + } + + private async getPriceData(unit: Unit) { + if (unit.coinbaseSpot) { + const splitted = unit.coinbaseSpot.split('-') + if (splitted.length == 2 && splitted[0] == splitted[1]) { + return null + } + const exchangeRate = await this.getExchangeRate(unit.coinbaseSpot) + const bigNumAmount = exchangeRate ? new BigNumber(exchangeRate.amount) : null + if (bigNumAmount && bigNumAmount.isGreaterThan(0)) { + unit.value = bigNumAmount + return bigNumAmount + } else { + // Handles the case if we get no price data atm + // Currently we fall back to the current networks default unit (since price is 1:1) + // (TODO: we could do this for all eth subunits fe. finney) + return null + } + } + } + + private async getExchangeRate(unitPair: string): Promise { + const req = new CoinbaseExchangeRequest(unitPair) + const response = await this.api.execute(req).catch((e) => { + console.warn('error in response getExchangeRate', e) + return null }) + const temp = req.parse(response) + if (temp.length <= 0) return null + console.log('requested exchange rate for ', unitPair, 'got', temp[0].amount, 'as response') + return temp[0] } } interface LastPrice { lastPrice: BigNumber } + +interface LastPrice { + Cons: BigNumber + Exec: BigNumber +} + +interface PreferredCurrency { + Cons: Currency + Exec: Currency + RPL: Currency +} + +export class Currency { + value: string + type: RewardType + unit: Unit + constructor(value: string, type: RewardType) { + this.value = value + this.type = type + } + + public toString(): string { + return this.value + } + public getCurrencyName(): string { + return this.value.charAt(0) + this.value.toLocaleLowerCase().slice(1) + } +} +interface CurrencyPipe { + Cons: string + Exec: string + RPL: string +} + +interface StoredPref { + prefered: string | null, + coinbaseSpot: string + symbol: string + rounding: number +} \ No newline at end of file diff --git a/src/app/tab-blocks/tab-blocks.page.html b/src/app/tab-blocks/tab-blocks.page.html index fdce75c4..99f0d54d 100644 --- a/src/app/tab-blocks/tab-blocks.page.html +++ b/src/app/tab-blocks/tab-blocks.page.html @@ -1,7 +1,7 @@ - + diff --git a/src/app/tab-blocks/tab-blocks.page.ts b/src/app/tab-blocks/tab-blocks.page.ts index 0ae5c55e..24b67f8a 100644 --- a/src/app/tab-blocks/tab-blocks.page.ts +++ b/src/app/tab-blocks/tab-blocks.page.ts @@ -100,16 +100,6 @@ export class TabBlocksPage implements OnInit { }, 1500) } - switchCurrencyPipe() { - if (this.unit.pref == 'ETHER') { - if (UnitconvService.currencyPipe == null) return - this.unit.pref = UnitconvService.currencyPipe - } else { - UnitconvService.currencyPipe = this.unit.pref - this.unit.pref = 'ETHER' - } - } - luckHelp() { if (!this.luck) { this.alertService.showInfo( diff --git a/src/app/tab-preferences/tab-preferences.page.html b/src/app/tab-preferences/tab-preferences.page.html index e36309e6..9c0576af 100644 --- a/src/app/tab-preferences/tab-preferences.page.html +++ b/src/app/tab-preferences/tab-preferences.page.html @@ -55,8 +55,8 @@ diff --git a/src/app/tab-preferences/tab-preferences.page.ts b/src/app/tab-preferences/tab-preferences.page.ts index 3d414568..b869210e 100644 --- a/src/app/tab-preferences/tab-preferences.page.ts +++ b/src/app/tab-preferences/tab-preferences.page.ts @@ -316,6 +316,7 @@ export class Tab3Page { await this.storage.setNetworkPreferences(newConfig) await this.api.initialize() await this.notificationBase.loadAllToggles() + await this.unit.networkSwitchReload() this.validatorUtils.notifyListeners() } diff --git a/src/app/tab-validators/tab-validators.page.html b/src/app/tab-validators/tab-validators.page.html index ee80ca2c..eb0fcce6 100644 --- a/src/app/tab-validators/tab-validators.page.html +++ b/src/app/tab-validators/tab-validators.page.html @@ -26,7 +26,7 @@ - + diff --git a/src/app/tab-validators/tab-validators.page.ts b/src/app/tab-validators/tab-validators.page.ts index a0f4a3e0..6c4d7f30 100644 --- a/src/app/tab-validators/tab-validators.page.ts +++ b/src/app/tab-validators/tab-validators.page.ts @@ -546,7 +546,7 @@ export class Tab2Page { cssClass: 'my-custom-class', header: 'Define stake share', message: - 'If you own partial amounts of these validators, specify the amount of ether for a custom dashboard. First value defines your consensus share, second value your execution share.', + 'If you own partial amounts of these validators, specify the amount of ' + this.api.getCurrenciesFormatted()+ ' for a custom dashboard. First value defines your consensus share, second value your execution share.', inputs: [ { name: 'share', @@ -617,13 +617,4 @@ export class Tab2Page { await alert.present() } - switchCurrencyPipe() { - if (this.unit.pref == 'ETHER') { - if (UnitconvService.currencyPipe == null) return - this.unit.pref = UnitconvService.currencyPipe - } else { - UnitconvService.currencyPipe = this.unit.pref - this.unit.pref = 'ETHER' - } - } } diff --git a/src/app/utils/CacheModule.ts b/src/app/utils/CacheModule.ts index 091c0fc5..fc7241e4 100644 --- a/src/app/utils/CacheModule.ts +++ b/src/app/utils/CacheModule.ts @@ -18,7 +18,7 @@ * // along with Beaconchain Dashboard. If not, see . */ -import { StorageService } from '../services/storage.service' +import { StorageService, replacer } from '../services/storage.service' import { Validator } from './ValidatorUtils' interface CachedData { @@ -49,7 +49,21 @@ export class CacheModule { if (result) { this.cache = result } - console.log('[CacheModule] initialized with ', this.cache) + try { + let kiloBytes = null; + if (this.hardStorage) { + const size = new TextEncoder().encode(JSON.stringify(this.cache, replacer)).length + kiloBytes = Math.round(size *100 / 1024) / 100; + } + console.log('[CacheModule] initialized with ', kiloBytes == null ? '(unknown size)' : '(' + kiloBytes + ' KiB)', this.cache) + if (kiloBytes && kiloBytes > 1000) { + console.warn('[CacheModule] storage cap exceeded (1 MB), clearing cache') + await this.clearHardCache() + } + } catch (e) { + console.warn("could not calculate cache size") + } + } else { this.initialized = new Promise>((resolve) => { resolve(new Map()) @@ -63,7 +77,7 @@ export class CacheModule { private getStoreForCacheKey(cacheKey: string): Map { // rationale: don't store big data objects in hardStorage due to severe performance impacts - const storeHard = cacheKey.indexOf('app/dashboard') >= 0 + const storeHard = cacheKey.indexOf('app/dashboard') >= 0 || cacheKey.indexOf("beaconcha.in") < 0 // or store all non beaconchain requests return storeHard ? this.cache : this.hotOnly } diff --git a/src/app/utils/EthereumUnits.ts b/src/app/utils/EthereumUnits.ts index 6c109ea3..4913badf 100644 --- a/src/app/utils/EthereumUnits.ts +++ b/src/app/utils/EthereumUnits.ts @@ -32,27 +32,30 @@ export default class Unit { public static SZABO = new Unit('Szabo', new BigNumber('1000000')) public static FINNEY = new Unit('Finney', new BigNumber('1000'), 2, null, 'Finney') - public static ETHER = new Unit('ETH', new BigNumber('1'), 5, null, 'Ether') + public static ETHER = new Unit('ETH', new BigNumber('1'), 5, 'XXX-ETH', 'Ether') public static KETHER = new Unit('KETH', new BigNumber('0.001')) public static RPL = new Unit('RPL', new BigNumber('1'), 1) // RPL TO ETH public static RPL_NAKED = new Unit('RPL', new BigNumber('1'), 2) public static NO_CURRENCY = new Unit('', new BigNumber('1'), 0) public static RETH = new Unit('RETH', new BigNumber('1'), 2) - public static USDETH = new Unit('$', new BigNumber('388.43'), 2, 'ETH-USD', 'Dollar') - public static EURETH = new Unit('€', new BigNumber('329.22'), 2, 'ETH-EUR', 'Euro') - public static RUBETH = new Unit('₽', new BigNumber('38093'), 2, 'ETH-RUB', 'Rubel') - public static JPYETH = new Unit('¥', new BigNumber('38093'), 0, 'ETH-JPY', 'Yen') - public static GBPETH = new Unit('£', new BigNumber('368.5'), 2, 'ETH-GBP', 'Pound') - public static AUDETH = new Unit('A$', new BigNumber('683.65'), 2, 'ETH-AUD', 'Australian Dollar') - public static CADETH = new Unit('C$', new BigNumber('651.02'), 2, 'ETH-CAD', 'Canadian Dollar') - public static CHFETH = new Unit('CHF', new BigNumber('455.55'), 2, 'ETH-CHF', 'Swiss Franc') - public static MXNETH = new Unit('MXN', new BigNumber('455.55'), 2, 'ETH-MXN', 'Mexican Peso') - public static ZARETH = new Unit('R', new BigNumber('455.55'), 2, 'ETH-ZAR', 'South African Rand') - public static CNYETH = new Unit('元', new BigNumber('455.55'), 2, 'ETH-CNY', 'Renminbi') - public static HKDETH = new Unit('HK$', new BigNumber('455.55'), 2, 'ETH-HKD', 'Hong Kong Dollar') - public static NZDETH = new Unit('NZ$', new BigNumber('455.55'), 2, 'ETH-NZD', 'New Zealand Dollar') - public static BTCETH = new Unit('₿', new BigNumber('455.55'), 6, 'ETH-BTC', 'Bitcoin') // coinbase endpoit: invalid currency :/ workaroung in unit converter + public static XDAI = new Unit('xDAI', new BigNumber('1'), 3, 'XXX-DAI', 'xDAI') + public static GNO = new Unit('GNO', new BigNumber('1'), 5, 'XXX-GNO', 'GNO') + + public static USDETH = new Unit('$', new BigNumber('1'), 2, 'XXX-USD', 'Dollar') + public static EURETH = new Unit('€', new BigNumber('1'), 2, 'XXX-EUR', 'Euro') + public static RUBETH = new Unit('₽', new BigNumber('1'), 2, 'XXX-RUB', 'Rubel') + public static JPYETH = new Unit('¥', new BigNumber('1'), 0, 'XXX-JPY', 'Yen') + public static GBPETH = new Unit('£', new BigNumber('1'), 2, 'XXX-GBP', 'Pound') + public static AUDETH = new Unit('A$', new BigNumber('1'), 2, 'XXX-AUD', 'Australian Dollar') + public static CADETH = new Unit('C$', new BigNumber('1'), 2, 'XXX-CAD', 'Canadian Dollar') + public static CHFETH = new Unit('CHF', new BigNumber('1'), 2, 'XXX-CHF', 'Swiss Franc') + public static MXNETH = new Unit('MXN', new BigNumber('1'), 2, 'XXX-MXN', 'Mexican Peso') + public static ZARETH = new Unit('R', new BigNumber('1'), 2, 'XXX-ZAR', 'South African Rand') + public static CNYETH = new Unit('元', new BigNumber('1'), 2, 'XXX-CNY', 'Renminbi') + public static HKDETH = new Unit('HK$', new BigNumber('1'), 2, 'XXX-HKD', 'Hong Kong Dollar') + public static NZDETH = new Unit('NZ$', new BigNumber('1'), 2, 'XXX-NZD', 'New Zealand Dollar') + public static BTCETH = new Unit('₿', new BigNumber('1'), 6, 'XXX-BTC', 'Bitcoin') private constructor(symbol: string, value: BigNumber, rounding = 2, coinbaseSpot = null, settingsName = null) { this.display = symbol @@ -62,10 +65,14 @@ export default class Unit { this.settingName = settingsName } + public toString(): string { + return this.value.toString() + " " + this.display + " (rounding: " + this.rounding + ", coinbaseSpot: " + this.coinbaseSpot + ", settingName: " + this.settingName + ")" + } + readonly display: string value: BigNumber readonly rounding: number - readonly coinbaseSpot: string + coinbaseSpot: string readonly settingName: string } @@ -84,6 +91,8 @@ export const MAPPING = new Map([ ['RPL_NAKED', Unit.RPL_NAKED], ['NO_CURRENCY', Unit.NO_CURRENCY], ['RETH', Unit.RETH], + ['GNO', Unit.GNO], + ['xDAI', Unit.XDAI], ['RUBLE', Unit.RUBETH], ['YEN', Unit.JPYETH], diff --git a/src/app/utils/MerchantUtils.ts b/src/app/utils/MerchantUtils.ts index 8fb5fa70..05a42ed4 100644 --- a/src/app/utils/MerchantUtils.ts +++ b/src/app/utils/MerchantUtils.ts @@ -98,7 +98,7 @@ export class MerchantUtils { constructor(private alertService: AlertService, private api: ApiService, private platform: Platform, private storage: StorageService) { if (!this.platform.is('ios') && !this.platform.is('android')) { - console.log('merchant is not supported on this platform') + console.info('merchant is not supported on this platform') return } diff --git a/src/app/utils/NetworkData.ts b/src/app/utils/NetworkData.ts index eee4abf0..93c925c7 100644 --- a/src/app/utils/NetworkData.ts +++ b/src/app/utils/NetworkData.ts @@ -18,7 +18,7 @@ * // along with Beaconchain Dashboard. If not, see . */ -import { ApiNetwork, Currency } from '../models/StorageTypes' +import { ApiNetwork, NetworkMainCurrency } from '../models/StorageTypes' export const MAP: ApiNetwork[] = [ { @@ -31,8 +31,9 @@ export const MAP: ApiNetwork[] = [ onlyDebug: false, active: true, genesisTs: 1606824023, - clCurrency: Currency.ETH, - elCurrency: Currency.ETH, + clCurrency: NetworkMainCurrency.ETH, + elCurrency: NetworkMainCurrency.ETH, + name: 'Ethereum', }, { key: 'gnosis', @@ -44,8 +45,9 @@ export const MAP: ApiNetwork[] = [ onlyDebug: false, active: true, genesisTs: 1638993340, - clCurrency: Currency.GNO, - elCurrency: Currency.xDAI, + clCurrency: NetworkMainCurrency.GNO, + elCurrency: NetworkMainCurrency.xDAI, + name: 'Gnosis', }, { key: 'prater', @@ -57,8 +59,9 @@ export const MAP: ApiNetwork[] = [ onlyDebug: false, active: true, genesisTs: 1616508000, - clCurrency: Currency.ETH, - elCurrency: Currency.ETH, + clCurrency: NetworkMainCurrency.ETH, + elCurrency: NetworkMainCurrency.ETH, + name: 'Ethereum', }, { key: 'sepolia', @@ -70,8 +73,9 @@ export const MAP: ApiNetwork[] = [ onlyDebug: false, active: true, genesisTs: 1655733600, - clCurrency: Currency.ETH, - elCurrency: Currency.ETH, + clCurrency: NetworkMainCurrency.ETH, + elCurrency: NetworkMainCurrency.ETH, + name: 'Ethereum', }, { key: 'holesky', @@ -83,8 +87,9 @@ export const MAP: ApiNetwork[] = [ onlyDebug: false, active: true, genesisTs: 1695902400, - clCurrency: Currency.ETH, - elCurrency: Currency.ETH, + clCurrency: NetworkMainCurrency.ETH, + elCurrency: NetworkMainCurrency.ETH, + name: 'Ethereum', }, { key: 'local dev', @@ -96,8 +101,9 @@ export const MAP: ApiNetwork[] = [ onlyDebug: true, active: true, genesisTs: 1606824023, - clCurrency: Currency.ETH, - elCurrency: Currency.ETH, + clCurrency: NetworkMainCurrency.ETH, + elCurrency: NetworkMainCurrency.ETH, + name: 'Ethereum', }, { key: 'invalid (no connection)', @@ -109,15 +115,15 @@ export const MAP: ApiNetwork[] = [ onlyDebug: true, active: true, genesisTs: 1606824023, - clCurrency: Currency.ETH, - elCurrency: Currency.ETH, + clCurrency: NetworkMainCurrency.ETH, + elCurrency: NetworkMainCurrency.ETH, + name: 'Ethereum', }, ] export function findConfigForKey(key: string): ApiNetwork { for (const entry of MAP) { if (entry.key == key) { - console.log('found config', key, entry) return entry } } diff --git a/src/app/utils/ValidatorUtils.ts b/src/app/utils/ValidatorUtils.ts index 870d3c4d..3b50bd43 100644 --- a/src/app/utils/ValidatorUtils.ts +++ b/src/app/utils/ValidatorUtils.ts @@ -432,7 +432,7 @@ export class ValidatorUtils extends CacheModule { private updateRplAndRethPrice() { if (!this.rocketpoolStats) return this.unitConversion.setRPLPrice(new BigNumber(this.rocketpoolStats.rpl_price.toString())) - this.unitConversion.setRETHPrice(new BigNumber(this.rocketpoolStats.reth_exchange_rate.toString())) + //this.unitConversion.setRETHPrice(new BigNumber(this.rocketpoolStats.reth_exchange_rate.toString())) } private findExecutionResponse(list: ExecutionResponse[], index: number): ExecutionResponse { From aed44e234d5cf3c06fe09b309a107fdd5e27f084 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Wed, 18 Oct 2023 11:52:05 +0200 Subject: [PATCH 06/53] formatting --- .../components/dashboard/dashboard.component.ts | 2 +- src/app/components/help/help.component.html | 12 ++++++------ src/app/models/StorageTypes.ts | 2 -- src/app/pipes/mcurrency.pipe.ts | 4 ++-- src/app/services/api.service.ts | 2 -- src/app/services/storage.service.ts | 2 +- src/app/services/unitconv.service.ts | 5 ++--- src/app/tab-blocks/tab-blocks.page.html | 4 +++- src/app/tab-validators/tab-validators.page.html | 4 +++- src/app/tab-validators/tab-validators.page.ts | 5 +++-- src/app/utils/CacheModule.ts | 11 +++++------ src/app/utils/EthereumUnits.ts | 15 +++++++++++++-- 12 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/app/components/dashboard/dashboard.component.ts b/src/app/components/dashboard/dashboard.component.ts index fcd391b5..f9ce10ec 100644 --- a/src/app/components/dashboard/dashboard.component.ts +++ b/src/app/components/dashboard/dashboard.component.ts @@ -675,7 +675,7 @@ export class DashboardComponent implements OnInit { text += ` (${this.unit.convertToPref(value, 'ETHER', type)})` } } - + return text } diff --git a/src/app/components/help/help.component.html b/src/app/components/help/help.component.html index 7572d551..f0577468 100644 --- a/src/app/components/help/help.component.html +++ b/src/app/components/help/help.component.html @@ -107,12 +107,12 @@
- - - - Interactive Setup Guide - - + + + + Interactive Setup Guide + + diff --git a/src/app/models/StorageTypes.ts b/src/app/models/StorageTypes.ts index f4c370dc..b495fab3 100644 --- a/src/app/models/StorageTypes.ts +++ b/src/app/models/StorageTypes.ts @@ -39,8 +39,6 @@ export interface ApiNetwork { name: string } - - export class NetworkMainCurrency { static readonly ETH = new NetworkMainCurrency('ETHER', 'Ether', 'ETH') static readonly GNO = new NetworkMainCurrency('GNO', 'mGNO', 'GNO') diff --git a/src/app/pipes/mcurrency.pipe.ts b/src/app/pipes/mcurrency.pipe.ts index 0ae10ff0..9aeadbf3 100644 --- a/src/app/pipes/mcurrency.pipe.ts +++ b/src/app/pipes/mcurrency.pipe.ts @@ -33,9 +33,9 @@ export class McurrencyPipe implements PipeTransform { if (typeof args[1] == 'string') { return this.unit.convertNonFiat(value, args[0] as string, args[1] as string, displayAble) } else if (typeof args[1] == 'object' && this.unit.isCurrency(args[1])) { - return this.unit.convert(value, args[0] as string, args[1], displayAble) + return this.unit.convert(value, args[0] as string, args[1], displayAble) } else { - console.warn("illegal usage of mcurrency pipe. Usage: value | mcurrency:from:to or value | mcurrency:from:currency") + console.warn('illegal usage of mcurrency pipe. Usage: value | mcurrency:from:to or value | mcurrency:from:currency') } } } diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index ea666f6d..abfb5083 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -451,7 +451,6 @@ export class ApiService extends CacheModule { return cfg.protocol + '://' + cfg.net + cfg.host } - async getAllTestNetNames() { const debug = await this.storage.isDebugMode() const re: string[][] = [] @@ -502,7 +501,6 @@ export interface Response extends HttpResponse { cached: boolean } - export function initializeApiService(service: ApiService): () => Promise { return () => service.initialize() } diff --git a/src/app/services/storage.service.ts b/src/app/services/storage.service.ts index 70324297..71a0b37b 100644 --- a/src/app/services/storage.service.ts +++ b/src/app/services/storage.service.ts @@ -281,4 +281,4 @@ export interface StoredShare { export interface DevModeEnabled { enabled: boolean -} \ No newline at end of file +} diff --git a/src/app/services/unitconv.service.ts b/src/app/services/unitconv.service.ts index 3586cfce..04c9b9c8 100644 --- a/src/app/services/unitconv.service.ts +++ b/src/app/services/unitconv.service.ts @@ -35,7 +35,6 @@ export type RewardType = 'cons' | 'exec' | 'rpl' providedIn: 'root', }) export class UnitconvService { - public pref: PreferredCurrency = { Cons: { value: 'ETHER', type: 'cons', unit: Unit.ETHER } as Currency, Exec: { value: 'ETHER', type: 'exec', unit: Unit.ETHER } as Currency, @@ -394,8 +393,8 @@ interface CurrencyPipe { } interface StoredPref { - prefered: string | null, + prefered: string | null coinbaseSpot: string symbol: string rounding: number -} \ No newline at end of file +} diff --git a/src/app/tab-blocks/tab-blocks.page.html b/src/app/tab-blocks/tab-blocks.page.html index 99f0d54d..d6d55b13 100644 --- a/src/app/tab-blocks/tab-blocks.page.html +++ b/src/app/tab-blocks/tab-blocks.page.html @@ -1,7 +1,9 @@ - + diff --git a/src/app/tab-validators/tab-validators.page.html b/src/app/tab-validators/tab-validators.page.html index eb0fcce6..839336ca 100644 --- a/src/app/tab-validators/tab-validators.page.html +++ b/src/app/tab-validators/tab-validators.page.html @@ -26,7 +26,9 @@ - + diff --git a/src/app/tab-validators/tab-validators.page.ts b/src/app/tab-validators/tab-validators.page.ts index 6c4d7f30..4f797dc1 100644 --- a/src/app/tab-validators/tab-validators.page.ts +++ b/src/app/tab-validators/tab-validators.page.ts @@ -546,7 +546,9 @@ export class Tab2Page { cssClass: 'my-custom-class', header: 'Define stake share', message: - 'If you own partial amounts of these validators, specify the amount of ' + this.api.getCurrenciesFormatted()+ ' for a custom dashboard. First value defines your consensus share, second value your execution share.', + 'If you own partial amounts of these validators, specify the amount of ' + + this.api.getCurrenciesFormatted() + + ' for a custom dashboard. First value defines your consensus share, second value your execution share.', inputs: [ { name: 'share', @@ -616,5 +618,4 @@ export class Tab2Page { await alert.present() } - } diff --git a/src/app/utils/CacheModule.ts b/src/app/utils/CacheModule.ts index fc7241e4..cb8eaaad 100644 --- a/src/app/utils/CacheModule.ts +++ b/src/app/utils/CacheModule.ts @@ -50,20 +50,19 @@ export class CacheModule { this.cache = result } try { - let kiloBytes = null; + let kiloBytes = null if (this.hardStorage) { const size = new TextEncoder().encode(JSON.stringify(this.cache, replacer)).length - kiloBytes = Math.round(size *100 / 1024) / 100; - } + kiloBytes = Math.round((size * 100) / 1024) / 100 + } console.log('[CacheModule] initialized with ', kiloBytes == null ? '(unknown size)' : '(' + kiloBytes + ' KiB)', this.cache) if (kiloBytes && kiloBytes > 1000) { console.warn('[CacheModule] storage cap exceeded (1 MB), clearing cache') await this.clearHardCache() } } catch (e) { - console.warn("could not calculate cache size") + console.warn('could not calculate cache size') } - } else { this.initialized = new Promise>((resolve) => { resolve(new Map()) @@ -77,7 +76,7 @@ export class CacheModule { private getStoreForCacheKey(cacheKey: string): Map { // rationale: don't store big data objects in hardStorage due to severe performance impacts - const storeHard = cacheKey.indexOf('app/dashboard') >= 0 || cacheKey.indexOf("beaconcha.in") < 0 // or store all non beaconchain requests + const storeHard = cacheKey.indexOf('app/dashboard') >= 0 || cacheKey.indexOf('beaconcha.in') < 0 // or store all non beaconchain requests return storeHard ? this.cache : this.hotOnly } diff --git a/src/app/utils/EthereumUnits.ts b/src/app/utils/EthereumUnits.ts index 4913badf..fd505a5c 100644 --- a/src/app/utils/EthereumUnits.ts +++ b/src/app/utils/EthereumUnits.ts @@ -55,7 +55,7 @@ export default class Unit { public static CNYETH = new Unit('元', new BigNumber('1'), 2, 'XXX-CNY', 'Renminbi') public static HKDETH = new Unit('HK$', new BigNumber('1'), 2, 'XXX-HKD', 'Hong Kong Dollar') public static NZDETH = new Unit('NZ$', new BigNumber('1'), 2, 'XXX-NZD', 'New Zealand Dollar') - public static BTCETH = new Unit('₿', new BigNumber('1'), 6, 'XXX-BTC', 'Bitcoin') + public static BTCETH = new Unit('₿', new BigNumber('1'), 6, 'XXX-BTC', 'Bitcoin') private constructor(symbol: string, value: BigNumber, rounding = 2, coinbaseSpot = null, settingsName = null) { this.display = symbol @@ -66,7 +66,18 @@ export default class Unit { } public toString(): string { - return this.value.toString() + " " + this.display + " (rounding: " + this.rounding + ", coinbaseSpot: " + this.coinbaseSpot + ", settingName: " + this.settingName + ")" + return ( + this.value.toString() + + ' ' + + this.display + + ' (rounding: ' + + this.rounding + + ', coinbaseSpot: ' + + this.coinbaseSpot + + ', settingName: ' + + this.settingName + + ')' + ) } readonly display: string From 94346f2639d885a67c71de58ae12e9f986a0e4cf Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Wed, 18 Oct 2023 11:53:19 +0200 Subject: [PATCH 07/53] formatting --- src/app/requests/requests.ts | 2 +- src/app/services/api.service.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/requests/requests.ts b/src/app/requests/requests.ts index ba4c724e..6d6ba664 100644 --- a/src/app/requests/requests.ts +++ b/src/app/requests/requests.ts @@ -607,7 +607,7 @@ export class RefreshTokenRequest extends APIRequest { ignoreFails = true maxCacheAge = 1000 options = { - url: null, + url: null, headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json', diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index 01932332..11042fa0 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -156,7 +156,7 @@ export class ApiService extends CacheModule { const req = new RefreshTokenRequest(user.refreshToken) const resp = await this.execute(req) const response = req.parse(resp) - const result = response[0] + const result = response[0] console.log('Refresh token', result, resp) if (!result || !result.access_token) { From 832f6d8ae42095a65bc7ede4764cb81a324e0fb4 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Wed, 18 Oct 2023 17:15:26 +0200 Subject: [PATCH 08/53] fix android --- src/app/services/api.service.ts | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index 11042fa0..a4561a7a 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -19,13 +19,13 @@ */ import { Injectable } from '@angular/core' -import { APIRequest, FormDataContainer, Method, RefreshTokenRequest } from '../requests/requests' +import { APIRequest, ApiTokenResponse, FormDataContainer, Method, RefreshTokenRequest } from '../requests/requests' import { StorageService } from './storage.service' import { ApiNetwork } from '../models/StorageTypes' import { isDevMode } from '@angular/core' import { Mutex } from 'async-mutex' import { findConfigForKey, MAP } from '../utils/NetworkData' -import { CapacitorHttp, HttpResponse } from '@capacitor/core' +import { Capacitor, CapacitorHttp, HttpResponse } from '@capacitor/core' import { CacheModule } from '../utils/CacheModule' import axios, { AxiosResponse } from 'axios' import { HttpOptions } from '@capacitor/core' @@ -154,9 +154,27 @@ export class ApiService extends CacheModule { const now = Date.now() const req = new RefreshTokenRequest(user.refreshToken) - const resp = await this.execute(req) - const response = req.parse(resp) - const result = response[0] + + let result: ApiTokenResponse + let resp + if (Capacitor.isNativePlatform() && Capacitor.getPlatform() == 'android') { + const formBody = new FormData() + formBody.set('grant_type', 'refresh_token') + formBody.set('refresh_token', user.refreshToken) + const url = await this.getResourceUrl(req.resource, req.endPoint) + + resp = await fetch(url, { + method: 'POST', + body: formBody, + headers: await this.getAuthHeader(true), + }) + result = await resp.json() + } else { + + resp = await this.execute(req) + const response = req.parse(resp) + result = response[0] + } console.log('Refresh token', result, resp) if (!result || !result.access_token) { From 7d072f09af4f017fd860680d28f3ff096e4997a0 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Wed, 18 Oct 2023 17:17:33 +0200 Subject: [PATCH 09/53] formatting --- src/app/services/api.service.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index a4561a7a..7772e40f 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -162,7 +162,7 @@ export class ApiService extends CacheModule { formBody.set('grant_type', 'refresh_token') formBody.set('refresh_token', user.refreshToken) const url = await this.getResourceUrl(req.resource, req.endPoint) - + resp = await fetch(url, { method: 'POST', body: formBody, @@ -170,7 +170,6 @@ export class ApiService extends CacheModule { }) result = await resp.json() } else { - resp = await this.execute(req) const response = req.parse(resp) result = response[0] From 0e99a4cc932ec97a1b6ef63abd246c4f6d40fd6c Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Thu, 19 Oct 2023 09:16:05 +0200 Subject: [PATCH 10/53] force native and notification info instead of warning --- src/app/pages/dev/dev.page.html | 12 ++++++++++++ src/app/pages/dev/dev.page.ts | 10 ++++++++++ src/app/services/alert.service.ts | 1 - src/app/services/api.service.ts | 4 ++++ src/app/tab-preferences/notification-base.ts | 16 +++++++--------- 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/app/pages/dev/dev.page.html b/src/app/pages/dev/dev.page.html index 6cafa30f..08813d2b 100644 --- a/src/app/pages/dev/dev.page.html +++ b/src/app/pages/dev/dev.page.html @@ -34,6 +34,18 @@ Set "Last Closed" to 0 for all clients + + + Force All Native POST + + diff --git a/src/app/pages/dev/dev.page.ts b/src/app/pages/dev/dev.page.ts index ac036e37..b35e31b0 100644 --- a/src/app/pages/dev/dev.page.ts +++ b/src/app/pages/dev/dev.page.ts @@ -15,6 +15,7 @@ export class DevPage extends Tab3Page implements OnInit { packageOverride = 'default' firebaseToken = '' notificationConsent = false + forceNativeRequests = false ngOnInit() { this.notificationBase.disableToggleLock() @@ -27,6 +28,7 @@ export class DevPage extends Tab3Page implements OnInit { }) this.firebaseUtils.hasNotificationConsent().then((result) => (this.notificationConsent = result)) + this.storage.getBooleanSetting('force_native_requests', false).then((result) => (this.forceNativeRequests = result)) } // --- Development methods --- @@ -106,6 +108,14 @@ export class DevPage extends Tab3Page implements OnInit { }) } + changeForceNativeRequests() { + this.storage.setBooleanSetting('force_native_requests', this.forceNativeRequests) + console.log('forceNative changed to ' + this.forceNativeRequests) + this.alerts.confirmDialog('Restart', 'Requires restart to take affect, restart?', 'OK', () => { + this.restartApp() + }) + } + restartApp() { this.merchant.restartApp() } diff --git a/src/app/services/alert.service.ts b/src/app/services/alert.service.ts index ba9066d3..8b05d2ae 100644 --- a/src/app/services/alert.service.ts +++ b/src/app/services/alert.service.ts @@ -21,7 +21,6 @@ import { Injectable } from '@angular/core' import { AlertController, LoadingController } from '@ionic/angular' -export const SETTINGS_PAGE = 100 export const VALIDATORUTILS = 140 export const PURCHASEUTILS = 150 diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index 7772e40f..9345421c 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -75,6 +75,10 @@ export class ApiService extends CacheModule { this.lastCacheInvalidate = Date.now() //this.registerLogMiddleware() this.updateNetworkConfig() + this.storage.getBooleanSetting('force_native_requests', false).then((result) => { + this.forceNativeAll = result + console.log('forcing native requests') + }) //this.isIOS15().then((result) => { this.forceNativeAll = result }) } diff --git a/src/app/tab-preferences/notification-base.ts b/src/app/tab-preferences/notification-base.ts index 7a4706b6..2025b0a6 100644 --- a/src/app/tab-preferences/notification-base.ts +++ b/src/app/tab-preferences/notification-base.ts @@ -3,7 +3,7 @@ import { Platform } from '@ionic/angular' import { ApiService } from 'src/app/services/api.service' import { CPU_THRESHOLD, HDD_THRESHOLD, RAM_THRESHOLD, SETTING_NOTIFY, StorageService } from 'src/app/services/storage.service' import { GetMobileSettingsRequest, MobileSettingsResponse, NotificationGetRequest } from '../requests/requests' -import { AlertService, SETTINGS_PAGE } from '../services/alert.service' +import { AlertService } from '../services/alert.service' import { SyncService } from '../services/sync.service' import ClientUpdateUtils, { Clients } from '../utils/ClientUpdateUtils' import FirebaseUtils from '../utils/FirebaseUtils' @@ -80,13 +80,6 @@ export class NotificationBase implements OnInit { if (this.platform.is('android')) { const hasToken = await this.firebaseUtils.hasNotificationToken() if (!hasToken) { - this.alerts.showError( - 'Play Service', - 'We could not enable notifications for your device which might be due to missing Google Play Services. Please note that notifications do not work without Google Play Services.', - SETTINGS_PAGE + 2 - ) - this.notify = false - return false } } @@ -193,7 +186,12 @@ export class NotificationBase implements OnInit { } async notifyToggle() { - if (!(await this.isSupportedOnAndroid())) return + if (!(await this.isSupportedOnAndroid())) { + this.alerts.showInfo( + 'Play Service', + 'Your device can not receive push notifications. Please note that notifications do not work without Google Play Services. As an alternative you can configure webhook notifications on the beaconcha.in website, otherwise changing these settings will have no effect.' + ) + } if (this.platform.is('ios') && (await this.firebaseUtils.hasSeenConsentScreenAndNotConsented())) { this.notify = false From 7d58036b32543aad6f9874fc8640483904693414 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Thu, 19 Oct 2023 10:55:28 +0200 Subject: [PATCH 11/53] fix fiat switching, fix validatorutils --- src/app/services/unitconv.service.ts | 9 +++++++++ src/app/tab-preferences/tab-preferences.page.html | 2 +- src/app/tab-preferences/tab-preferences.page.ts | 8 ++++++-- src/app/utils/ValidatorUtils.ts | 8 ++++---- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/app/services/unitconv.service.ts b/src/app/services/unitconv.service.ts index 04c9b9c8..0f6827a5 100644 --- a/src/app/services/unitconv.service.ts +++ b/src/app/services/unitconv.service.ts @@ -77,6 +77,15 @@ export class UnitconvService { await this.init() } + public async changeCurrency(value: string) { + this.pref.Cons = this.createCurrency(value, 'cons') + await this.updatePriceData() + } + + public async getCurrentConsFiat() { + return this.createCurrency(this.getPref(await this.loadStored(STORAGE_KEY_CONS), this.getNetworkDefaultCurrency('cons')), 'cons').value + } + private async loadStored(key: string): Promise { const result = await this.storage.getObject(key) if (!result) return null diff --git a/src/app/tab-preferences/tab-preferences.page.html b/src/app/tab-preferences/tab-preferences.page.html index 9c0576af..14b858c5 100644 --- a/src/app/tab-preferences/tab-preferences.page.html +++ b/src/app/tab-preferences/tab-preferences.page.html @@ -56,7 +56,7 @@ diff --git a/src/app/tab-preferences/tab-preferences.page.ts b/src/app/tab-preferences/tab-preferences.page.ts index b869210e..298f91e3 100644 --- a/src/app/tab-preferences/tab-preferences.page.ts +++ b/src/app/tab-preferences/tab-preferences.page.ts @@ -81,6 +81,7 @@ export class Tab3Page { premiumLabel = '' protected package = '' + protected currentFiatCurrency constructor( protected api: ApiService, @@ -103,6 +104,9 @@ export class Tab3Page { ) {} ngOnInit() { + this.unit.getCurrentConsFiat().then((result) => { + this.currentFiatCurrency = result + }) this.theme.isDarkThemed().then((result) => (this.darkMode = result)) this.theme.getThemeColor().then((result) => (this.themeColor = result)) @@ -230,13 +234,13 @@ export class Tab3Page { overrideDisplayCurrency = null private changeCurrencyLocked = false - changeCurrency() { + async changeCurrency() { if (this.changeCurrencyLocked) return this.changeCurrencyLocked = true this.overrideDisplayCurrency = this.unit.pref - this.unit.updatePriceData() + await this.unit.changeCurrency(this.currentFiatCurrency) this.unit.save() setTimeout(() => { diff --git a/src/app/utils/ValidatorUtils.ts b/src/app/utils/ValidatorUtils.ts index 31ffd067..5ad647c1 100644 --- a/src/app/utils/ValidatorUtils.ts +++ b/src/app/utils/ValidatorUtils.ts @@ -371,16 +371,16 @@ export class ValidatorUtils extends CacheModule { const result = request.parse(response)[0] if (!result) { - console.warn('error getDashboardDataValidators', response) + console.warn('error getDashboardDataValidators', response, result) return [] } - if (result.currentEpoch && result.currentEpoch.length > 1) { + if (result.currentEpoch && result.currentEpoch.length > 0) { this.currentEpoch = result.currentEpoch[0] } - if (result.olderEpoch && result.olderEpoch.length > 1) { + if (result.olderEpoch && result.olderEpoch.length > 0) { this.olderEpoch = result.olderEpoch[0] } - if (result.rocketpool_network_stats && result.rocketpool_network_stats.length > 1) { + if (result.rocketpool_network_stats && result.rocketpool_network_stats.length > 0) { this.rocketpoolStats = result.rocketpool_network_stats[0] } const validatorEffectivenessResponse = result.effectiveness From 5adde0a0ce1f39f551c7fd5ae355532c8232029b Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Thu, 19 Oct 2023 10:57:14 +0200 Subject: [PATCH 12/53] update android widget for gnosis --- android/app/src/main/java/in/beaconcha/mobile/widget | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/java/in/beaconcha/mobile/widget b/android/app/src/main/java/in/beaconcha/mobile/widget index 9a947695..63d7292e 160000 --- a/android/app/src/main/java/in/beaconcha/mobile/widget +++ b/android/app/src/main/java/in/beaconcha/mobile/widget @@ -1 +1 @@ -Subproject commit 9a947695b1213cc6ac5cf5cad9efd42a96d83d69 +Subproject commit 63d7292e397db12b1545b9ccc2f071858add6702 From 3179651698ef3dd8b21d3a92b38ef7c1cd40bd4a Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Thu, 19 Oct 2023 13:21:46 +0200 Subject: [PATCH 13/53] fixes for network switch --- README.md | 4 ++-- angular.json | 5 +++++ package-lock.json | 14 +++++++------- package.json | 2 +- src/app/app.module.ts | 3 ++- src/app/services/storage.service.ts | 2 +- src/app/services/unitconv.service.ts | 8 ++++++++ src/app/tab-preferences/tab-preferences.page.html | 1 - src/app/tab-preferences/tab-preferences.page.ts | 1 + 9 files changed, 27 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 16fd2006..bb4e7704 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Build the the app at least once before proceeding: Make sure port 8100 is accessable on your computer and use the following command to run a livereload server -`ionic capacitor run android --livereload --external --host=192.168.0.124 --disableHostCheck` +`ionic cap run android --livereload --external --host=192.168.1.64 --disableHostCheck --configuration=development` Adapt the --host param to match your computers IP. @@ -98,7 +98,7 @@ Build the the app at least once before proceeding: Make sure port 8100 is accessable on your mac and use the following command to run a livereload server -`ionic capacitor run ios --livereload --external --host=192.168.0.124 --disableHostCheck` +`ionic cap run ios --livereload --external --host=192.168.1.64 --disableHostCheck --configuration=development` Adapt the --host param to match your macs IP. diff --git a/angular.json b/angular.json index ca614137..4832ba80 100644 --- a/angular.json +++ b/angular.json @@ -19,6 +19,11 @@ "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", + "allowedCommonJsDependencies": [ + "highcharts", + "ethereum-blockies", + "magic-snowflakes" + ], "assets": [ { "glob": "**/*", diff --git a/package-lock.json b/package-lock.json index b022ec93..0c6866d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,7 @@ "@ionic/angular": "^7.4.3", "async-mutex": "^0.2.6", "axios": "^0.27.2", - "bignumber.js": "^9.0.2", + "bignumber.js": "^9.1.2", "canvas-confetti": "^1.3.3", "capacitor-navigationbarnx": "0.1.6", "cordova-plugin-purchase": "^13.8.6", @@ -5849,9 +5849,9 @@ } }, "node_modules/bignumber.js": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz", - "integrity": "sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", "engines": { "node": "*" } @@ -21102,9 +21102,9 @@ "dev": true }, "bignumber.js": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz", - "integrity": "sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==" + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==" }, "binary-extensions": { "version": "2.1.0", diff --git a/package.json b/package.json index 58e5e9b8..95c7eff2 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@ionic/angular": "^7.4.3", "async-mutex": "^0.2.6", "axios": "^0.27.2", - "bignumber.js": "^9.0.2", + "bignumber.js": "^9.1.2", "canvas-confetti": "^1.3.3", "capacitor-navigationbarnx": "0.1.6", "cordova-plugin-purchase": "^13.8.6", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 59f0ae2a..ac0d8dd7 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -18,7 +18,7 @@ * // along with Beaconchain Dashboard. If not, see . */ -import { APP_INITIALIZER, NgModule } from '@angular/core' +import { APP_INITIALIZER, Injectable, NgModule } from '@angular/core' import { BrowserModule, HammerModule } from '@angular/platform-browser' import { RouteReuseStrategy } from '@angular/router' @@ -34,6 +34,7 @@ import { ApiService, initializeApiService } from './services/api.service' // eslint-disable-next-line @typescript-eslint/no-explicit-any declare let Hammer: any +@Injectable() export class MyHammerConfig extends HammerGestureConfig { buildHammer(element: HTMLElement) { const mc = new Hammer(element, { diff --git a/src/app/services/storage.service.ts b/src/app/services/storage.service.ts index 71a0b37b..9f6db1ef 100644 --- a/src/app/services/storage.service.ts +++ b/src/app/services/storage.service.ts @@ -92,7 +92,7 @@ export class StorageService extends CacheModule { } async setNetworkPreferences(value: StorageTypes.ApiNetwork) { - return this.setObject(PREFERENCES, value) + return await this.setObject(PREFERENCES, value) } async loadPreferencesToggles(network: string): Promise { diff --git a/src/app/services/unitconv.service.ts b/src/app/services/unitconv.service.ts index 0f6827a5..362ded39 100644 --- a/src/app/services/unitconv.service.ts +++ b/src/app/services/unitconv.service.ts @@ -78,7 +78,15 @@ export class UnitconvService { } public async changeCurrency(value: string) { + UnitconvService.currencyPipe = { Cons: null, Exec: null, RPL: null } + this.pref.Cons = this.createCurrency(value, 'cons') + if (this.isDefaultCurrency(this.pref.Cons)) { + this.pref.Exec = this.createCurrency(this.getNetworkDefaultCurrency(this.pref.Exec), 'exec') + } else { + this.pref.Exec = this.createCurrency(value, 'exec') + } + await this.updatePriceData() } diff --git a/src/app/tab-preferences/tab-preferences.page.html b/src/app/tab-preferences/tab-preferences.page.html index 14b858c5..8151fb06 100644 --- a/src/app/tab-preferences/tab-preferences.page.html +++ b/src/app/tab-preferences/tab-preferences.page.html @@ -55,7 +55,6 @@ Date: Thu, 19 Oct 2023 13:26:29 +0200 Subject: [PATCH 14/53] refactored api service to always use capacitor native http --- package-lock.json | 66 +++------- package.json | 1 - src/app/pages/dev/dev.page.html | 12 -- src/app/pages/dev/dev.page.ts | 10 -- src/app/requests/requests.ts | 55 ++------ src/app/services/api.service.ts | 215 ++++++++++---------------------- 6 files changed, 93 insertions(+), 266 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0c6866d8..9a87f113 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,7 +37,6 @@ "@capacitor/toast": "^5.0.6", "@ionic/angular": "^7.4.3", "async-mutex": "^0.2.6", - "axios": "^0.27.2", "bignumber.js": "^9.1.2", "canvas-confetti": "^1.3.3", "capacitor-navigationbarnx": "0.1.6", @@ -5611,7 +5610,8 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true }, "node_modules/at-least-node": { "version": "1.0.0", @@ -5669,28 +5669,6 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, - "node_modules/axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", - "dependencies": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" - } - }, - "node_modules/axios/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/axobject-query": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", @@ -6627,6 +6605,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -7364,6 +7343,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, "engines": { "node": ">=0.4.0" } @@ -8805,6 +8785,7 @@ "version": "1.15.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", + "dev": true, "funding": [ { "type": "individual", @@ -11390,6 +11371,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, "engines": { "node": ">= 0.6" } @@ -11398,6 +11380,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, "dependencies": { "mime-db": "1.52.0" }, @@ -20933,7 +20916,8 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true }, "at-least-node": { "version": "1.0.0", @@ -20966,27 +20950,6 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, - "axios": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", - "requires": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" - }, - "dependencies": { - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } - } - }, "axobject-query": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", @@ -21675,6 +21638,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -22239,7 +22203,8 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true }, "delegates": { "version": "1.0.0", @@ -23344,7 +23309,8 @@ "follow-redirects": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", + "dev": true }, "foreground-child": { "version": "3.1.1", @@ -25307,12 +25273,14 @@ "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true }, "mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, "requires": { "mime-db": "1.52.0" } diff --git a/package.json b/package.json index 95c7eff2..2e80f069 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,6 @@ "@capacitor/toast": "^5.0.6", "@ionic/angular": "^7.4.3", "async-mutex": "^0.2.6", - "axios": "^0.27.2", "bignumber.js": "^9.1.2", "canvas-confetti": "^1.3.3", "capacitor-navigationbarnx": "0.1.6", diff --git a/src/app/pages/dev/dev.page.html b/src/app/pages/dev/dev.page.html index 08813d2b..6cafa30f 100644 --- a/src/app/pages/dev/dev.page.html +++ b/src/app/pages/dev/dev.page.html @@ -34,18 +34,6 @@ Set "Last Closed" to 0 for all clients - - - Force All Native POST - - diff --git a/src/app/pages/dev/dev.page.ts b/src/app/pages/dev/dev.page.ts index c1f66379..1e79b741 100644 --- a/src/app/pages/dev/dev.page.ts +++ b/src/app/pages/dev/dev.page.ts @@ -15,7 +15,6 @@ export class DevPage extends Tab3Page implements OnInit { packageOverride = 'default' firebaseToken = '' notificationConsent = false - forceNativeRequests = false ngOnInit() { this.notificationBase.disableToggleLock() @@ -28,7 +27,6 @@ export class DevPage extends Tab3Page implements OnInit { }) this.firebaseUtils.hasNotificationConsent().then((result) => (this.notificationConsent = result)) - this.storage.getBooleanSetting('force_native_requests', false).then((result) => (this.forceNativeRequests = result)) } // --- Development methods --- @@ -108,14 +106,6 @@ export class DevPage extends Tab3Page implements OnInit { }) } - changeForceNativeRequests() { - this.storage.setBooleanSetting('force_native_requests', this.forceNativeRequests) - console.log('forceNative changed to ' + this.forceNativeRequests) - this.alerts.confirmDialog('Restart', 'Requires restart to take affect, restart?', 'OK', () => { - this.restartApp() - }) - } - restartApp() { this.merchant.restartApp() } diff --git a/src/app/requests/requests.ts b/src/app/requests/requests.ts index 6d6ba664..2552c671 100644 --- a/src/app/requests/requests.ts +++ b/src/app/requests/requests.ts @@ -45,7 +45,6 @@ export abstract class APIRequest { return this.parseBase(response) } - // Since we use native http and axios we have various response types // Usually you can expect either a Response or a boolean // response.status can be a string though depending on the type of http connector used // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -53,12 +52,7 @@ export abstract class APIRequest { if (typeof response === 'boolean') { return response } - if (this.nativeHttp) { - if (!response || !response.status) return false - return response.status == 200 && (response.data.status == 'OK' || !hasDataStatus) - } else { - return response && (response.status == 'OK' || response.status == 200 || !hasDataStatus) - } + return response && (response.status == 'OK' || response.status == 200 || !hasDataStatus) } protected parseBase(response: Response, hasDataStatus = true): T[] { @@ -87,7 +81,6 @@ export abstract class APIRequest { updatesLastRefreshState = false ignoreFails = false maxCacheAge = 6 * 60 * 1000 - nativeHttp = true // TODO: for some reason, native HTTP Post doesnt work on iOS.. } // ------------- Responses ------------- @@ -421,7 +414,6 @@ export class SetMobileSettingsRequest extends APIRequest requiresAuth = true ignoreFails = true - nativeHttp = false parse(response: Response): MobileSettingsResponse[] { if (!response || !response.data) return null @@ -453,7 +445,6 @@ export class PostMobileSubscription extends APIRequest { method = Method.POST requiresAuth = true ignoreFails = true - nativeHttp = false constructor(subscriptionData: SubscriptionData) { super() @@ -492,7 +483,6 @@ export class RemoveMyValidatorsRequest extends APIRequest { requiresAuth = true postData = {} ignoreFails = true - nativeHttp = false options = { url: null, // unused @@ -518,7 +508,6 @@ export class AddMyValidatorsRequest extends APIRequest { method = Method.POST requiresAuth = true ignoreFails = true - nativeHttp = false options = { url: null, // unused @@ -591,7 +580,6 @@ export class NotificationBundleSubsRequest extends APIRequest requiresAuth = true postData = {} ignoreFails = true - nativeHttp = false constructor(enabled: boolean, data: BundleSub[]) { super() @@ -621,10 +609,10 @@ export class RefreshTokenRequest extends APIRequest { constructor(refreshToken: string) { super() - this.postData = new FormDataContainer({ - grant_type: 'refresh_token', - refresh_token: refreshToken, - }) + const formBody = new FormData() + formBody.set('grant_type', 'refresh_token') + formBody.set('refresh_token', refreshToken) + this.postData = formBody } } @@ -633,7 +621,6 @@ export class UpdateTokenRequest extends APIRequest { method = Method.POST requiresAuth = true ignoreFails = true - nativeHttp = false parse(response: Response): APIResponse[] { if (response && response.data) return response.data as APIResponse[] @@ -648,21 +635,6 @@ export class UpdateTokenRequest extends APIRequest { // ------------ Special external api requests ----------------- -export class AdSeenRequest extends APIRequest { - endPoint = 'https://request-global.czilladx.com' - - resource = '' - method = Method.GET - ignoreFails = true - maxCacheAge = 0 - nativeHttp = false - - constructor(url: string) { - super() - this.resource = url.replace('https://request-global.czilladx.com/', '') - } -} - export class BitflyAdRequest extends APIRequest { endPoint = 'https://ads.bitfly.at' @@ -670,7 +642,11 @@ export class BitflyAdRequest extends APIRequest { method = Method.GET ignoreFails = true maxCacheAge = 4 * 60 * 1000 - nativeHttp = false + + options = { + url: null, // unused + headers: undefined, + } parse(response: Response): BitflyAdResponse[] { if (!response || !response.data) { @@ -741,14 +717,3 @@ export class GithubReleaseRequest extends APIRequest { } export default class {} - -export class FormDataContainer { - private data: unknown - constructor(data: unknown) { - this.data = data - } - - getBody() { - return this.data - } -} diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index 7536f3d8..52460e8a 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -19,14 +19,12 @@ */ import { Injectable } from '@angular/core' -import { APIRequest, ApiTokenResponse, FormDataContainer, Method, RefreshTokenRequest } from '../requests/requests' +import { APIRequest, Method, RefreshTokenRequest } from '../requests/requests' import { StorageService } from './storage.service' import { ApiNetwork } from '../models/StorageTypes' import { Mutex } from 'async-mutex' import { findConfigForKey, MAP } from '../utils/NetworkData' -import { Capacitor, CapacitorHttp, HttpResponse } from '@capacitor/core' import { CacheModule } from '../utils/CacheModule' -import axios, { AxiosResponse } from 'axios' import { HttpOptions } from '@capacitor/core' const LOGTAG = '[ApiService]' @@ -37,27 +35,17 @@ const SERVER_TIMEOUT = 25000 providedIn: 'root', }) export class ApiService extends CacheModule { - private isInitialized = false - networkConfig: ApiNetwork public connectionStateOK = true - public isAuthorized = false - - awaitingResponses: Map = new Map() + private awaitingResponses: Map = new Map() debug = false public lastRefreshed = 0 // only updated by calls that have the updatesLastRefreshState flag enabled - lastCacheInvalidate = 0 - - private httpLegacy = axios.create({ - timeout: SERVER_TIMEOUT, - }) - - forceNativeAll = false + private lastCacheInvalidate = 0 constructor(private storage: StorageService) { super('api', 6 * 60 * 1000, storage) @@ -74,13 +62,8 @@ export class ApiService extends CacheModule { window.localStorage.setItem('debug', this.debug ? 'true' : 'false') }) this.lastCacheInvalidate = Date.now() - //this.registerLogMiddleware() + this.initialize() - this.storage.getBooleanSetting('force_native_requests', false).then((result) => { - this.forceNativeAll = result - console.log('forcing native requests') - }) - //this.isIOS15().then((result) => { this.forceNativeAll = result }) } mayInvalidateOnFaultyConnectionState() { @@ -104,7 +87,6 @@ export class ApiService extends CacheModule { } return config }) - this.isInitialized = true } networkName = null @@ -159,25 +141,25 @@ export class ApiService extends CacheModule { const now = Date.now() const req = new RefreshTokenRequest(user.refreshToken) - let result: ApiTokenResponse - let resp - if (Capacitor.isNativePlatform() && Capacitor.getPlatform() == 'android') { - const formBody = new FormData() - formBody.set('grant_type', 'refresh_token') - formBody.set('refresh_token', user.refreshToken) - const url = this.getResourceUrl(req.resource, req.endPoint) - - resp = await fetch(url, { - method: 'POST', - body: formBody, - headers: await this.getAuthHeader(true), - }) - result = await resp.json() - } else { - resp = await this.execute(req) - const response = req.parse(resp) - result = response[0] - } + // let result: ApiTokenResponse + // let resp + // if (Capacitor.isNativePlatform() && Capacitor.getPlatform() == 'ios') { + // resp = await this.execute(req) + // const response = req.parse(resp) + // result = response[0] + // } else { + const formBody = new FormData() + formBody.set('grant_type', 'refresh_token') + formBody.set('refresh_token', user.refreshToken) + const url = this.getResourceUrl(req.resource, req.endPoint) + + const resp = await fetch(url, { + method: 'POST', + body: formBody, + headers: await this.getAuthHeader(true), + }) + const result = await resp.json() + // } console.log('Refresh token', result, resp) if (!result || !result.access_token) { @@ -260,27 +242,13 @@ export class ApiService extends CacheModule { console.log(LOGTAG + ' Send request: ' + request.resource, request.method, request) const startTs = Date.now() - if (this.forceNativeAll) { - // android appears to have issues with native POST right now - console.log('force native all') - request.nativeHttp = false - } - let response: Promise switch (request.method) { case Method.GET: - if (request.nativeHttp) { - response = this.get(request.resource, request.endPoint, request.ignoreFails, options) - } else { - response = this.legacyGet(request.resource, request.endPoint, request.ignoreFails, options) - } + response = this.get(request.resource, request.endPoint, request.ignoreFails, options) break case Method.POST: - if (request.nativeHttp) { - response = this.post(request.resource, request.postData, request.endPoint, request.ignoreFails, options) - } else { - response = this.legacyPost(request.resource, request.postData, request.endPoint, request.ignoreFails, options) - } + response = this.post(request.resource, request.postData, request.endPoint, request.ignoreFails, options) break default: throw 'Unsupported method: ' + request.method @@ -320,87 +288,43 @@ export class ApiService extends CacheModule { } private async get(resource: string, endpoint = 'default', ignoreFails = false, options: HttpOptions = { url: null, headers: {} }) { - const getOptions = { - url: this.getResourceUrl(resource, endpoint), - method: 'get', + const result = await fetch(this.getResourceUrl(resource, endpoint), { + method: 'GET', headers: options.headers, - } - return CapacitorHttp.get(getOptions) - .catch((err) => { - this.updateConnectionState(ignoreFails, false) - console.warn('Connection err', err) - }) - .then((response: Response) => this.validateResponse(ignoreFails, response)) + }).catch((err) => { + this.updateConnectionState(ignoreFails, false) + console.warn('Connection err', err) + }) + if (!result) return null + return await this.validateResponse(ignoreFails, result) } - private async post(resource: string, data: unknown, endpoint = 'default', ignoreFails = false, options: HttpOptions = { url: null, headers: {} }) { + private async post(resource: string, data, endpoint = 'default', ignoreFails = false, options: HttpOptions = { url: null, headers: {} }) { + const contentType = this.getContentType(data) if (!Object.prototype.hasOwnProperty.call(options.headers, 'Content-Type')) { - options.headers = { ...options.headers, ...{ 'Content-Type': this.getContentType(data) } } + options.headers = { ...options.headers, ...{ 'Content-Type': contentType } } } - const postOptions = { - url: this.getResourceUrl(resource, endpoint), - headers: options.headers, - data: this.formatPostData(data), - method: 'post', - } - return CapacitorHttp.post(postOptions) //options) - .catch((err) => { - this.updateConnectionState(ignoreFails, false) - console.warn('Connection err', err) - }) - .then((response: Response) => this.validateResponse(ignoreFails, response)) - } - - private async legacyGet(resource: string, endpoint = 'default', ignoreFails = false, options: HttpOptions = { url: null, headers: {} }) { - return this.httpLegacy - .get(this.getResourceUrl(resource, endpoint), options) - .catch((err) => { - this.updateConnectionState(ignoreFails, false) - console.warn('Connection err', err) - }) - .then((response: AxiosResponse) => this.validateResponseLegacy(ignoreFails, response)) - } - - private async legacyPost( - resource: string, - data: unknown, - endpoint = 'default', - // eslint-disable-next-line @typescript-eslint/no-unused-vars - ignoreFails = false, - options: HttpOptions = { url: null, headers: {} } - ) { - if (!Object.prototype.hasOwnProperty.call(options.headers, 'Content-Type')) { - options.headers = { ...options.headers, ...{ 'Content-Type': this.getContentType(data) } } - } - /* return this.httpLegacy - .post(await this.getResourceUrl(resource, endpoint),JSON.stringify(this.formatPostData(data)), options) - .catch((err) => { - this.updateConnectionState(ignoreFails, false); - console.warn("Connection err", err) - }) - .then((response: AxiosResponse) => this.validateResponseLegacy(ignoreFails, response)); - */ - const resp = await fetch(this.getResourceUrl(resource, endpoint), { + const result = await fetch(this.getResourceUrl(resource, endpoint), { method: 'POST', - body: JSON.stringify(this.formatPostData(data)), headers: options.headers, + body: this.formatPostData(data), + }).catch((err) => { + this.updateConnectionState(ignoreFails, false) + console.warn('Connection err', err) }) - if (resp) { - return resp.json() - } else { - return null - } + if (!result) return null + return await this.validateResponse(ignoreFails, result) } private getContentType(data: unknown): string { - if (data instanceof FormDataContainer) return 'application/x-www-form-urlencoded' + if (data instanceof FormData) return 'application/x-www-form-urlencoded' return 'application/json' } - private formatPostData(data: unknown) { - if (data instanceof FormDataContainer) return data.getBody() - return data + private formatPostData(data): BodyInit { + if (data instanceof FormData) return data + return JSON.stringify(data) } private updateConnectionState(ignoreFails: boolean, working: boolean) { @@ -409,37 +333,26 @@ export class ApiService extends CacheModule { console.log(LOGTAG + ' setting status', working) } - private validateResponseLegacy(ignoreFails, response: AxiosResponse): Response { - if (!response || !response.data) { - // || !response.data.data + private async validateResponse(ignoreFails, response: globalThis.Response): Promise { + if (!response) { + console.warn('can not get response', response) this.updateConnectionState(ignoreFails, false) - - return { - cached: false, - data: null, - status: response.status, - headers: response.headers, - url: null, - } - } - this.updateConnectionState(ignoreFails, true) - return { - cached: false, - data: response.data, - status: response.status, - headers: response.headers, - url: response.config.url, + return } - } - - private validateResponse(ignoreFails, response: Response): Response { - if (!response || !response.data) { - // || !response.data.data + const jsonData = await response.json() + if (!jsonData) { + console.warn('not json response', response, jsonData) this.updateConnectionState(ignoreFails, false) return } this.updateConnectionState(ignoreFails, true) - return response + return { + data: jsonData, + status: response.status, + headers: response.headers, + url: response.url, + cached: false, + } as Response } getResourceUrl(resource: string, endpoint = 'default'): string { @@ -508,8 +421,12 @@ export class ApiService extends CacheModule { } } -export interface Response extends HttpResponse { +export interface Response { cached: boolean + data + headers: Headers + status: number + url: string } export function initializeApiService(service: ApiService): () => Promise { From d70dddc4919abd4a65a74d30e6d70640e8dfe61e Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 19 Oct 2023 14:07:35 +0200 Subject: [PATCH 15/53] ios fix --- src/app/pages/dev/dev.page.ts | 16 +++++++++----- src/app/requests/requests.ts | 18 ++++++++++----- src/app/services/api.service.ts | 39 ++++++++++----------------------- 3 files changed, 35 insertions(+), 38 deletions(-) diff --git a/src/app/pages/dev/dev.page.ts b/src/app/pages/dev/dev.page.ts index 1e79b741..9e03fe82 100644 --- a/src/app/pages/dev/dev.page.ts +++ b/src/app/pages/dev/dev.page.ts @@ -31,11 +31,17 @@ export class DevPage extends Tab3Page implements OnInit { // --- Development methods --- - forceTokenRefresh() { - this.api.refreshToken() - Toast.show({ - text: 'Token refreshed', - }) + async forceTokenRefresh() { + const result = await this.api.refreshToken() + if (result) { + Toast.show({ + text: 'Token refreshed', + }) + } else { + Toast.show({ + text: 'Token refreshed failed :(', + }) + } } clearApiCache() { diff --git a/src/app/requests/requests.ts b/src/app/requests/requests.ts index 2552c671..266fc995 100644 --- a/src/app/requests/requests.ts +++ b/src/app/requests/requests.ts @@ -607,12 +607,20 @@ export class RefreshTokenRequest extends APIRequest { else return [] } - constructor(refreshToken: string) { + constructor(refreshToken: string, isIOS: boolean) { super() - const formBody = new FormData() - formBody.set('grant_type', 'refresh_token') - formBody.set('refresh_token', refreshToken) - this.postData = formBody + // ¯\_(ツ)_/¯ + if (isIOS) { + this.postData = { + grant_type: 'refresh_token', + refresh_token: refreshToken, + } + } else { + const formBody = new FormData() + formBody.set('grant_type', 'refresh_token') + formBody.set('refresh_token', refreshToken) + this.postData = formBody + } } } diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index 52460e8a..c9cad53e 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -25,7 +25,7 @@ import { ApiNetwork } from '../models/StorageTypes' import { Mutex } from 'async-mutex' import { findConfigForKey, MAP } from '../utils/NetworkData' import { CacheModule } from '../utils/CacheModule' -import { HttpOptions } from '@capacitor/core' +import { Capacitor, HttpOptions } from '@capacitor/core' const LOGTAG = '[ApiService]' @@ -139,27 +139,11 @@ export class ApiService extends CacheModule { } const now = Date.now() - const req = new RefreshTokenRequest(user.refreshToken) - - // let result: ApiTokenResponse - // let resp - // if (Capacitor.isNativePlatform() && Capacitor.getPlatform() == 'ios') { - // resp = await this.execute(req) - // const response = req.parse(resp) - // result = response[0] - // } else { - const formBody = new FormData() - formBody.set('grant_type', 'refresh_token') - formBody.set('refresh_token', user.refreshToken) - const url = this.getResourceUrl(req.resource, req.endPoint) - - const resp = await fetch(url, { - method: 'POST', - body: formBody, - headers: await this.getAuthHeader(true), - }) - const result = await resp.json() - // } + const req = new RefreshTokenRequest(user.refreshToken, Capacitor.getPlatform() == 'ios') + + const resp = await this.execute(req) + const response = req.parse(resp) + const result = response[0] console.log('Refresh token', result, resp) if (!result || !result.access_token) { @@ -300,15 +284,14 @@ export class ApiService extends CacheModule { } private async post(resource: string, data, endpoint = 'default', ignoreFails = false, options: HttpOptions = { url: null, headers: {} }) { - const contentType = this.getContentType(data) if (!Object.prototype.hasOwnProperty.call(options.headers, 'Content-Type')) { - options.headers = { ...options.headers, ...{ 'Content-Type': contentType } } + options.headers = { ...options.headers, ...{ 'Content-Type': this.getContentTypeBasedOnData(data) } } } const result = await fetch(this.getResourceUrl(resource, endpoint), { method: 'POST', headers: options.headers, - body: this.formatPostData(data), + body: this.formatPostData(data, resource), }).catch((err) => { this.updateConnectionState(ignoreFails, false) console.warn('Connection err', err) @@ -317,13 +300,13 @@ export class ApiService extends CacheModule { return await this.validateResponse(ignoreFails, result) } - private getContentType(data: unknown): string { + private getContentTypeBasedOnData(data: unknown): string { if (data instanceof FormData) return 'application/x-www-form-urlencoded' return 'application/json' } - private formatPostData(data): BodyInit { - if (data instanceof FormData) return data + private formatPostData(data, resource: string): BodyInit { + if (data instanceof FormData || resource.indexOf('user/token') != -1) return data return JSON.stringify(data) } From 41a07e7ac0bb2e6b3c1b4b2fa7ef69f16bac41c2 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 19 Oct 2023 14:44:53 +0200 Subject: [PATCH 16/53] add holesky, sepolia, gnosis to ios reflect storage --- src/app/services/storage.service.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/app/services/storage.service.ts b/src/app/services/storage.service.ts index 9f6db1ef..85ddbff1 100644 --- a/src/app/services/storage.service.ts +++ b/src/app/services/storage.service.ts @@ -201,9 +201,10 @@ export class StorageService extends CacheModule { 'CapacitorStorage.prefered_unit', 'CapacitorStorage.network_preferences', 'CapacitorStorage.validators_main', - 'CapacitorStorage.validators_pyrmont', + 'CapacitorStorage.validators_holesky', 'CapacitorStorage.validators_prater', - 'CapacitorStorage.validators_staging', + 'CapacitorStorage.validators_sepolia', + 'CapacitorStorage.validators_gnosis', 'CapacitorStorage.auth_user', ], }) From 37cee75fbcba67e2e22bca3b5490bd16e4077ad0 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Thu, 19 Oct 2023 15:09:25 +0200 Subject: [PATCH 17/53] fix android --- src/app/requests/requests.ts | 1 + src/app/services/api.service.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/requests/requests.ts b/src/app/requests/requests.ts index 266fc995..9e1c36a2 100644 --- a/src/app/requests/requests.ts +++ b/src/app/requests/requests.ts @@ -620,6 +620,7 @@ export class RefreshTokenRequest extends APIRequest { formBody.set('grant_type', 'refresh_token') formBody.set('refresh_token', refreshToken) this.postData = formBody + this.options.headers = undefined } } } diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index c9cad53e..11017d3f 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -285,7 +285,9 @@ export class ApiService extends CacheModule { private async post(resource: string, data, endpoint = 'default', ignoreFails = false, options: HttpOptions = { url: null, headers: {} }) { if (!Object.prototype.hasOwnProperty.call(options.headers, 'Content-Type')) { - options.headers = { ...options.headers, ...{ 'Content-Type': this.getContentTypeBasedOnData(data) } } + if (!(data instanceof FormData)) { + options.headers = { ...options.headers, ...{ 'Content-Type': this.getContentTypeBasedOnData(data) } } + } } const result = await fetch(this.getResourceUrl(resource, endpoint), { From ddc9fa9639ab7b0987d80f641e275254f4dedc46 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Thu, 19 Oct 2023 15:42:39 +0200 Subject: [PATCH 18/53] preload blocks, fix client updates popin, fix multiple opens of notification page --- src/app/tab-blocks/tab-blocks.page.ts | 16 +++---- .../tab-preferences/tab-preferences.page.ts | 5 +++ src/app/tabs/tabs.page.ts | 9 +++- src/app/utils/ClientUpdateUtils.ts | 42 +++++++++++++++---- 4 files changed, 56 insertions(+), 16 deletions(-) diff --git a/src/app/tab-blocks/tab-blocks.page.ts b/src/app/tab-blocks/tab-blocks.page.ts index 24b67f8a..54df0f0a 100644 --- a/src/app/tab-blocks/tab-blocks.page.ts +++ b/src/app/tab-blocks/tab-blocks.page.ts @@ -44,14 +44,16 @@ export class TabBlocksPage implements OnInit { }) this.validatorUtils.getAllValidatorsLocal().then((validators) => { this.dataSource = new InfiniteScrollDataSource(this.blockUtils.getLimit(validators.length), async (offset: number) => { - let sleepTime = 1000 - if (offset >= 50) { - sleepTime = 3500 // 20 req per minute => wait at least 3 seconds. Buffer for dashboard and sync stuff - } else if (offset >= 120) { - sleepTime = 4500 + if (offset > 0) { + let sleepTime = 1000 + if (offset >= 50) { + sleepTime = 3500 // 20 req per minute => wait at least 3 seconds. Buffer for dashboard and sync stuff + } else if (offset >= 120) { + sleepTime = 4500 + } + this.loadMore = true + await sleep(sleepTime) // handling rate limit of some sorts } - if (offset > 0) this.loadMore = true - await sleep(sleepTime) // handling rate limit of some sorts const result = await this.blockUtils.getMyBlocks(offset) this.loadMore = false return result diff --git a/src/app/tab-preferences/tab-preferences.page.ts b/src/app/tab-preferences/tab-preferences.page.ts index d84c6c65..89d95906 100644 --- a/src/app/tab-preferences/tab-preferences.page.ts +++ b/src/app/tab-preferences/tab-preferences.page.ts @@ -157,8 +157,13 @@ export class Tab3Page { this.updateUtils.convertOldToNewClientSettings() } + private loadingNotificationPage = false async goToNotificationPage() { + if (this.loadingNotificationPage) return + this.loadingNotificationPage = true + await this.sync.syncAllSettings(true) + this.loadingNotificationPage = false this.router.navigate(['/notifications']) } diff --git a/src/app/tabs/tabs.page.ts b/src/app/tabs/tabs.page.ts index 6364815f..6981df58 100644 --- a/src/app/tabs/tabs.page.ts +++ b/src/app/tabs/tabs.page.ts @@ -25,6 +25,7 @@ import FirebaseUtils from '../utils/FirebaseUtils' import { MerchantUtils } from '../utils/MerchantUtils' import ThemeUtils from '../utils/ThemeUtils' import { Toast } from '@capacitor/toast' +import { BlockUtils } from '../utils/BlockUtils' @Component({ selector: 'app-tabs', templateUrl: 'tabs.page.html', @@ -36,7 +37,8 @@ export class TabsPage { private sync: SyncService, private storage: StorageService, private merchant: MerchantUtils, - private theme: ThemeUtils + private theme: ThemeUtils, + private blockUtils: BlockUtils ) {} ionViewDidEnter() { @@ -46,6 +48,11 @@ export class TabsPage { private preload() { // lazy initiating firebase token exchange this.firebaseUtils.registerPush() // just initialize the firebaseutils service + try { + this.blockUtils.getMyBlocks(0) // preload blocks + } catch (e) { + console.warn('can not preload blocks') + } // lazy sync & notification token update setTimeout(async () => { diff --git a/src/app/utils/ClientUpdateUtils.ts b/src/app/utils/ClientUpdateUtils.ts index 650fac2b..f4a2c5b8 100644 --- a/src/app/utils/ClientUpdateUtils.ts +++ b/src/app/utils/ClientUpdateUtils.ts @@ -137,18 +137,44 @@ export default class ClientUpdateUtils { async checkAllUpdates() { if (this.lastTry + 10 * 60 * 1000 > Date.now()) return - this.updates = null + + const promiseArray: Promise[] = [] for (let i = 0; i < Clients.length; i++) { - this.append(this.checkUpdateFor(await this.storage.getItem(Clients[i].storageKey))) + promiseArray.push(this.checkUpdateFor(await this.storage.getItem(Clients[i].storageKey))) + } + + try { + const results = await Promise.all(promiseArray) + let changeFound = false + for (let i = 0; i < results.length; i++) { + if (results[i] && !this.contains(results[i])) { + changeFound = true + } + } + + if (changeFound) { + this.updates = null + for (let i = 0; i < results.length; i++) { + if (results[i]) { + this.append(results[i]) + } + } + } + } catch (error) { + console.error('An error occurred:', error) } } async checkClientUpdate(clientKey: string) { - this.remove(clientKey) - const client = this.getClientInfo(clientKey) - if (client != null) { - this.append(this.checkUpdateFor(await this.storage.getItem(client.storageKey))) + if (client == null) { + return + } + const update = await this.checkUpdateFor(await this.storage.getItem(client.storageKey)) + + if (update && !this.contains(update)) { + this.remove(clientKey) + this.append(update) } } @@ -156,8 +182,8 @@ export default class ClientUpdateUtils { return (await this.getUpdateChannel()) == 'PRERELEASE' } - private async append(info: Promise) { - const data = await info + private append(info: Release) { + const data = info if (!data) return if (this.updates == null) this.updates = [data] From 06ca7baa9294ecb2ec289973c806495741e730b9 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:25:19 +0200 Subject: [PATCH 19/53] gnosis theme and easy network switcher on fresh install --- src/app/components/help/help.component.html | 12 +++- src/app/components/help/help.component.ts | 33 ++++++++- .../tab-preferences/tab-preferences.page.html | 1 + .../tab-preferences/tab-preferences.page.ts | 70 ++++++++++++++++--- src/app/tabs/tabs.page.ts | 2 +- src/theme/variables.scss | 21 ++++++ 6 files changed, 124 insertions(+), 15 deletions(-) diff --git a/src/app/components/help/help.component.html b/src/app/components/help/help.component.html index f0577468..baba21c9 100644 --- a/src/app/components/help/help.component.html +++ b/src/app/components/help/help.component.html @@ -28,13 +28,21 @@ Or login with {{ api.getHostName() }} + + + + {{ api.getNetwork().name }} Network + - {{ isGnosis ? 'Ethereum' : 'Gnosis' }} Network + Guides & Help - + Staking Guide & FAQ @@ -79,7 +87,7 @@ How to set up a Validator - + Setup Guide for Lighthouse diff --git a/src/app/components/help/help.component.ts b/src/app/components/help/help.component.ts index af50f02c..31a1575a 100644 --- a/src/app/components/help/help.component.ts +++ b/src/app/components/help/help.component.ts @@ -26,6 +26,12 @@ import { ValidatorUtils } from 'src/app/utils/ValidatorUtils' import { Browser } from '@capacitor/browser' import { ApiService } from 'src/app/services/api.service' +import { changeNetwork } from 'src/app/tab-preferences/tab-preferences.page' +import { UnitconvService } from 'src/app/services/unitconv.service' +import { NotificationBase } from 'src/app/tab-preferences/notification-base' +import ThemeUtils from 'src/app/utils/ThemeUtils' +import { AlertService } from 'src/app/services/alert.service' +import { MerchantUtils } from 'src/app/utils/MerchantUtils' @Component({ selector: 'app-help', @@ -36,18 +42,27 @@ export class HelpComponent implements OnInit { @Input() onlyGuides: boolean isAlreadyLoggedIn = false + isGnosis: boolean + constructor( private oauthUtils: OAuthUtils, private validator: ValidatorUtils, private storage: StorageService, private router: Router, - public api: ApiService + public api: ApiService, + private validatorUtils: ValidatorUtils, + private unit: UnitconvService, + private notificationBase: NotificationBase, + private theme: ThemeUtils, + private alert: AlertService, + private merchant: MerchantUtils ) {} ngOnInit() { this.storage.isLoggedIn().then((result) => { this.isAlreadyLoggedIn = result }) + this.isGnosis = this.api.isGnosis() } async openBrowser(link) { @@ -64,4 +79,20 @@ export class HelpComponent implements OnInit { if (!hasValidators) this.router.navigate(['/tabs/validators']) } } + + async switchNetwork() { + await changeNetwork( + this.api.isGnosis() ? 'main' : 'gnosis', + this.storage, + this.api, + this.validatorUtils, + this.unit, + this.notificationBase, + this.theme, + this.alert, + this.merchant, + true + ) + this.isGnosis = this.api.isGnosis() + } } diff --git a/src/app/tab-preferences/tab-preferences.page.html b/src/app/tab-preferences/tab-preferences.page.html index 8151fb06..39c7575f 100644 --- a/src/app/tab-preferences/tab-preferences.page.html +++ b/src/app/tab-preferences/tab-preferences.page.html @@ -84,6 +84,7 @@ Default Ethpool Rocketpool + Gnosis Teal Green Cyan diff --git a/src/app/tab-preferences/tab-preferences.page.ts b/src/app/tab-preferences/tab-preferences.page.ts index 89d95906..5fd1ce56 100644 --- a/src/app/tab-preferences/tab-preferences.page.ts +++ b/src/app/tab-preferences/tab-preferences.page.ts @@ -317,17 +317,18 @@ export class Tab3Page { } async changeNetwork() { - const newConfig = findConfigForKey(this.network) - await this.storage.clearCache() - await this.api.clearCache() - await this.validatorUtils.clearCache() - - await this.storage.setNetworkPreferences(newConfig) - await this.api.initialize() - await this.notificationBase.loadAllToggles() - await this.unit.networkSwitchReload() - //await this.unit.changeCurrency(this.currentFiatCurrency) - this.validatorUtils.notifyListeners() + await changeNetwork( + this.network, + this.storage, + this.api, + this.validatorUtils, + this.unit, + this.notificationBase, + this.theme, + this.alerts, + this.merchant, + false + ) } async openIconCredit() { @@ -442,3 +443,50 @@ export class Tab3Page { await alert.present() } } + +export async function changeNetwork( + network: string, + storage: StorageService, + api: ApiService, + validatorUtils: ValidatorUtils, + unit: UnitconvService, + notificationBase: NotificationBase, + theme: ThemeUtils, + alertService: AlertService, + merchant: MerchantUtils, + forceThemeSwitch: boolean +) { + const darkTheme = await theme.isDarkThemed() + + const newConfig = findConfigForKey(network) + await storage.clearCache() + await api.clearCache() + await validatorUtils.clearCache() + + await storage.setNetworkPreferences(newConfig) + await api.initialize() + await notificationBase.loadAllToggles() + await unit.networkSwitchReload() + //await this.unit.changeCurrency(this.currentFiatCurrency) + validatorUtils.notifyListeners() + + const currentTheme = theme.currentThemeColor + + if (forceThemeSwitch && (currentTheme == '' || currentTheme == 'gnosis')) { + theme.undoColor() + setTimeout(() => { + theme.toggle(darkTheme, true, api.isGnosis() ? 'gnosis' : ''), 50 + }) + } else { + const hasTheming = await merchant.hasPremiumTheming() + if (hasTheming) return + if (currentTheme == '' && !api.isGnosis()) return + if (currentTheme == 'gnosis' && api.isGnosis()) return + alertService.confirmDialog('Switch App Theme', 'Do you want to switch to the free ' + api.getNetwork().name + ' App theme?', 'Sure', () => { + theme.undoColor() + setTimeout(() => { + theme.toggle(darkTheme, true, api.isGnosis() ? 'gnosis' : ''), 50 + }) + }) + } +} diff --git a/src/app/tabs/tabs.page.ts b/src/app/tabs/tabs.page.ts index 6981df58..20e2699f 100644 --- a/src/app/tabs/tabs.page.ts +++ b/src/app/tabs/tabs.page.ts @@ -87,7 +87,7 @@ export class TabsPage { } const hasTheming = await this.merchant.hasPremiumTheming() - if (!hasTheming) { + if (!hasTheming && this.theme.currentThemeColor != 'gnosis') { this.theme.resetTheming() } } diff --git a/src/theme/variables.scss b/src/theme/variables.scss index b462eea3..7408b2c2 100644 --- a/src/theme/variables.scss +++ b/src/theme/variables.scss @@ -714,3 +714,24 @@ body.rocketpool.dark { --x-toolbar-title-color: #fff; } + +body.gnosis { + --ion-color-primary: #3e6957; + --ion-color-primary-rgb: 62, 105, 87; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #e97112; + --ion-color-primary-tint: #0e0b0a; + + --ion-toolbar-background: #3e6957; //2f2e42 + --x-alert-primary: #3e6957; + --ion-color-success: #3e6957; + --chart-default: #3e6957; + --x-update-messages-background: #4c816b; + --x-update-messages-background-selected: #4c816b; + + --status-ok: white; + --status-warn: white; + --status-danger: white; + --status-secondary: white; +} From 2ed602b154afb8961e5096cd59c216d08c2b12e8 Mon Sep 17 00:00:00 2001 From: Manuel Date: Thu, 19 Oct 2023 17:35:30 +0200 Subject: [PATCH 20/53] fix case when switching between gnosis and testnet --- src/app/components/help/help.component.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/app/components/help/help.component.ts b/src/app/components/help/help.component.ts index 31a1575a..c6871237 100644 --- a/src/app/components/help/help.component.ts +++ b/src/app/components/help/help.component.ts @@ -44,6 +44,8 @@ export class HelpComponent implements OnInit { isGnosis: boolean + private ethereumNetworkKey: string + constructor( private oauthUtils: OAuthUtils, private validator: ValidatorUtils, @@ -63,6 +65,10 @@ export class HelpComponent implements OnInit { this.isAlreadyLoggedIn = result }) this.isGnosis = this.api.isGnosis() + this.ethereumNetworkKey = this.api.getNetwork().key + if(this.ethereumNetworkKey == 'gnosis'){ + this.ethereumNetworkKey = 'main' + } } async openBrowser(link) { @@ -82,7 +88,7 @@ export class HelpComponent implements OnInit { async switchNetwork() { await changeNetwork( - this.api.isGnosis() ? 'main' : 'gnosis', + this.api.isGnosis() ? this.ethereumNetworkKey : 'gnosis', this.storage, this.api, this.validatorUtils, From 13f557284cdb6a34c4891e93abd349de5d96f066 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:41:02 +0200 Subject: [PATCH 21/53] formatting --- src/app/components/help/help.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/help/help.component.ts b/src/app/components/help/help.component.ts index c6871237..43786707 100644 --- a/src/app/components/help/help.component.ts +++ b/src/app/components/help/help.component.ts @@ -66,7 +66,7 @@ export class HelpComponent implements OnInit { }) this.isGnosis = this.api.isGnosis() this.ethereumNetworkKey = this.api.getNetwork().key - if(this.ethereumNetworkKey == 'gnosis'){ + if (this.ethereumNetworkKey == 'gnosis') { this.ethereumNetworkKey = 'main' } } From 2f73034bdf8a7cd2d5fe7c7d843435a54f507a5d Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Fri, 20 Oct 2023 11:46:30 +0200 Subject: [PATCH 22/53] preload service, added mgno, further cachemodule hard storage optimizations and dont double cache hardstorage --- browserslist => .browserslistrc | 12 +- src/app/app.module.ts | 17 ++- .../dashboard/dashboard.component.ts | 4 - src/app/models/StorageTypes.ts | 2 +- src/app/services/api.service.ts | 16 +-- src/app/services/boot-preload.service.ts | 21 ++++ src/app/services/storage.service.ts | 24 ++-- src/app/services/unitconv.service.ts | 21 ++-- src/app/tab-preferences/notification-base.ts | 2 +- .../tab-preferences/tab-preferences.page.ts | 3 +- src/app/tabs/tabs.page.ts | 9 +- src/app/utils/CacheModule.ts | 113 +++++++++++++----- src/app/utils/ClientUpdateUtils.ts | 4 + src/app/utils/EthereumUnits.ts | 4 +- src/app/utils/NetworkData.ts | 2 +- src/app/utils/ValidatorUtils.ts | 2 +- 16 files changed, 166 insertions(+), 90 deletions(-) rename browserslist => .browserslistrc (60%) create mode 100644 src/app/services/boot-preload.service.ts diff --git a/browserslist b/.browserslistrc similarity index 60% rename from browserslist rename to .browserslistrc index b15c7fae..2ff4ef04 100644 --- a/browserslist +++ b/.browserslistrc @@ -2,11 +2,13 @@ # For additional information regarding the format and rule options, please see: # https://github.com/browserslist/browserslist#queries +# For the full list of supported browsers by the Angular framework, please see: +# https://angular.io/guide/browser-support + # You can see what browsers were selected by your queries by running: # npx browserslist -> 0.5% -last 2 versions -Firefox ESR -not dead -not IE 9-11 # For IE 9-11 support, remove 'not'. +last 2 Chrome versions +last 2 ChromeAndroid versions +last 2 Safari versions +last 2 iOS versions \ No newline at end of file diff --git a/src/app/app.module.ts b/src/app/app.module.ts index ac0d8dd7..1f1cd473 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -30,7 +30,8 @@ import { PipesModule } from './pipes/pipes.module' import { HammerGestureConfig, HAMMER_GESTURE_CONFIG } from '@angular/platform-browser' import { BrowserAnimationsModule } from '@angular/platform-browser/animations' import 'hammerjs' -import { ApiService, initializeApiService } from './services/api.service' +import { ApiService } from './services/api.service' +import { BootPreloadService } from './services/boot-preload.service' // eslint-disable-next-line @typescript-eslint/no-explicit-any declare let Hammer: any @@ -63,11 +64,21 @@ export class MyHammerConfig extends HammerGestureConfig { }, { provide: APP_INITIALIZER, - useFactory: initializeApiService, - deps: [ApiService], + useFactory: initializeApp, multi: true, + deps: [ApiService, BootPreloadService], }, ], bootstrap: [AppComponent], }) export class AppModule {} + +export function initializeApp(apiService: ApiService, bootPreloadService: BootPreloadService): () => Promise { + return async () => { + // Initialize ApiService first + await apiService.initialize() + + // Now that ApiService is initialized, you can preload using BootPreloadService + bootPreloadService.preload() + } +} diff --git a/src/app/components/dashboard/dashboard.component.ts b/src/app/components/dashboard/dashboard.component.ts index f9ce10ec..30b1557a 100644 --- a/src/app/components/dashboard/dashboard.component.ts +++ b/src/app/components/dashboard/dashboard.component.ts @@ -114,7 +114,6 @@ export class DashboardComponent implements OnInit { private platform: Platform ) { this.randomChartId = getRandomInt(Number.MAX_SAFE_INTEGER) - //this.storage.setBooleanSetting("merge_list_dismissed", false) this.updateMergeListDismissed() } @@ -131,7 +130,6 @@ export class DashboardComponent implements OnInit { isAfterPotentialMergeTarget() { const now = Date.now() const target = 1663624800000 // target sept 20th to dismiss merge checklist - console.log('afterPotentialMerge', now, target, now >= target) return now >= target } @@ -177,8 +175,6 @@ export class DashboardComponent implements OnInit { this.updateWithdrawalInfo(), ]) - console.log('dashboard data', this.data) - if (!this.data.foreignValidator) { await Promise.all([this.checkForFinalization(), this.checkForGenesisOccurred()]) } diff --git a/src/app/models/StorageTypes.ts b/src/app/models/StorageTypes.ts index b495fab3..aee550ea 100644 --- a/src/app/models/StorageTypes.ts +++ b/src/app/models/StorageTypes.ts @@ -41,7 +41,7 @@ export interface ApiNetwork { export class NetworkMainCurrency { static readonly ETH = new NetworkMainCurrency('ETHER', 'Ether', 'ETH') - static readonly GNO = new NetworkMainCurrency('GNO', 'mGNO', 'GNO') + static readonly GNO = new NetworkMainCurrency('GNO', 'GNO', 'GNO') static readonly xDAI = new NetworkMainCurrency('xDAI', 'xDAI', 'DAI') public internalName: string diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index 11017d3f..46909faa 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -48,12 +48,12 @@ export class ApiService extends CacheModule { private lastCacheInvalidate = 0 constructor(private storage: StorageService) { - super('api', 6 * 60 * 1000, storage) - this.storage.getBooleanSetting('migrated_4_3_0', false).then((migrated) => { + super('api', 6 * 60 * 1000, storage, false) + this.storage.getBooleanSetting('migrated_4_4_0', false).then((migrated) => { if (!migrated) { this.clearHardCache() - console.info('Cleared hard cache storage as part of 4.3.0 migration') - this.storage.setBooleanSetting('migrated_4_3_0', true) + console.info('Cleared hard cache storage as part of 4.4.0 migration') + this.storage.setBooleanSetting('migrated_4_4_0', true) } }) @@ -62,8 +62,6 @@ export class ApiService extends CacheModule { window.localStorage.setItem('debug', this.debug ? 'true' : 'false') }) this.lastCacheInvalidate = Date.now() - - this.initialize() } mayInvalidateOnFaultyConnectionState() { @@ -87,6 +85,8 @@ export class ApiService extends CacheModule { } return config }) + await this.init() + console.log('API SERVICE INITIALISED') } networkName = null @@ -413,7 +413,3 @@ export interface Response { status: number url: string } - -export function initializeApiService(service: ApiService): () => Promise { - return () => service.initialize() -} diff --git a/src/app/services/boot-preload.service.ts b/src/app/services/boot-preload.service.ts new file mode 100644 index 00000000..4cfa1268 --- /dev/null +++ b/src/app/services/boot-preload.service.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@angular/core' +import { ValidatorUtils } from '../utils/ValidatorUtils' +import ClientUpdateUtils from '../utils/ClientUpdateUtils' +import { BlockUtils } from '../utils/BlockUtils' + +@Injectable({ + providedIn: 'root', +}) +export class BootPreloadService { + constructor(private validatorUtils: ValidatorUtils, private clientUpdateUtils: ClientUpdateUtils, private blockUtils: BlockUtils) {} + + preload() { + try { + this.validatorUtils.getAllMyValidators() + this.clientUpdateUtils.checkAllUpdates() + this.blockUtils.getMyBlocks(0) // preload blocks + } catch (e) { + console.warn('can not preload', e) + } + } +} diff --git a/src/app/services/storage.service.ts b/src/app/services/storage.service.ts index 85ddbff1..a94c2fe5 100644 --- a/src/app/services/storage.service.ts +++ b/src/app/services/storage.service.ts @@ -21,7 +21,7 @@ import { Injectable, isDevMode } from '@angular/core' import { Plugins } from '@capacitor/core' import * as StorageTypes from '../models/StorageTypes' -import { findConfigForKey } from '../utils/NetworkData' +import { MAP, findConfigForKey } from '../utils/NetworkData' import { CacheModule } from '../utils/CacheModule' import BigNumber from 'bignumber.js' import { Platform } from '@ionic/angular' @@ -165,8 +165,8 @@ export class StorageService extends CacheModule { // --- Low level --- - async setObject(key: string, value: unknown) { - this.putCache(key, value) + async setObject(key: string, value: unknown, cache = true) { + if (cache) this.putCache(key, value) await this.setItem(key, JSON.stringify(value, replacer), false) } @@ -196,17 +196,15 @@ export class StorageService extends CacheModule { private reflectiOSStorage() { try { if (!this.platform.is('ios')) return + const reflectKeys = ['CapacitorStorage.prefered_unit', 'CapacitorStorage.network_preferences', 'CapacitorStorage.auth_user'] + for (let i = 0; i < MAP.length; i++) { + if (MAP[i].key.indexOf('invalid') > -1) continue + if (MAP[i].key.indexOf('local') > -1) continue + reflectKeys.push('CapacitorStorage.validators_' + MAP[i].key) + } + StorageMirror.reflect({ - keys: [ - 'CapacitorStorage.prefered_unit', - 'CapacitorStorage.network_preferences', - 'CapacitorStorage.validators_main', - 'CapacitorStorage.validators_holesky', - 'CapacitorStorage.validators_prater', - 'CapacitorStorage.validators_sepolia', - 'CapacitorStorage.validators_gnosis', - 'CapacitorStorage.auth_user', - ], + keys: reflectKeys, }) } catch (e) { console.warn('StorageMirror exception', e) diff --git a/src/app/services/unitconv.service.ts b/src/app/services/unitconv.service.ts index 362ded39..6e96ba02 100644 --- a/src/app/services/unitconv.service.ts +++ b/src/app/services/unitconv.service.ts @@ -131,9 +131,7 @@ export class UnitconvService { } } - if (this.isDefaultCurrency(currency)) { - result.value = new BigNumber(1) - } else if (price) { + if (price && !this.isDefaultCurrency(currency) && currency.value != 'mGNO') { result.value = price } @@ -203,8 +201,12 @@ export class UnitconvService { } private triggerPropertyChange() { - if (this.isDefaultCurrency(this.pref.Cons)) { - this.pref.Cons = this.createCurrency(this.getNetworkDefaultCurrency(this.pref.Cons), 'cons') + if (this.isDefaultCurrency(this.pref.Cons) || this.pref.Cons.value == 'mGNO') { + if (this.pref.Cons.value == 'mGNO') { + this.pref.Cons = this.createCurrency(this.pref.Cons.value, 'cons') + } else { + this.pref.Cons = this.createCurrency(this.getNetworkDefaultCurrency(this.pref.Cons), 'cons') + } this.pref.Exec = this.createCurrency(this.getNetworkDefaultCurrency(this.pref.Cons), 'exec') this.pref.RPL = this.createCurrency(this.getNetworkDefaultCurrency(this.pref.RPL), 'rpl') return @@ -321,16 +323,18 @@ export class UnitconvService { const consPrice = await this.getPriceData(this.pref.Cons.unit) if (consPrice) { this.lastPrice.Cons = consPrice - this.storage.setObject(this.getLastPriceKey(this.pref.Cons), { lastPrice: consPrice } as LastPrice) + this.storage.setObject(this.getLastPriceKey(this.pref.Cons), { lastPrice: this.lastPrice.Cons } as LastPrice) } else { this.lastPrice.Cons = this.pref.Cons.unit.value - this.pref.Cons.value = this.getNetworkDefaultCurrency(this.pref.Cons) + if (this.pref.Cons.value != 'mGNO') { + this.pref.Cons.value = this.getNetworkDefaultCurrency(this.pref.Cons) + } } const execPrice = await this.getPriceData(this.pref.Exec.unit) if (execPrice) { this.lastPrice.Exec = execPrice - this.storage.setObject(this.getLastPriceKey(this.pref.Exec), { lastPrice: execPrice } as LastPrice) + this.storage.setObject(this.getLastPriceKey(this.pref.Exec), { lastPrice: this.lastPrice.Exec } as LastPrice) } else { this.lastPrice.Exec = this.pref.Exec.unit.value this.pref.Exec.value = this.getNetworkDefaultCurrency(this.pref.Exec) @@ -357,6 +361,7 @@ export class UnitconvService { return null } } + return null } private async getExchangeRate(unitPair: string): Promise { diff --git a/src/app/tab-preferences/notification-base.ts b/src/app/tab-preferences/notification-base.ts index c25183c3..9b5e7e46 100644 --- a/src/app/tab-preferences/notification-base.ts +++ b/src/app/tab-preferences/notification-base.ts @@ -109,7 +109,6 @@ export class NotificationBase implements OnInit { ) //network = 'prater' // use me, dear developer } - console.log('result', results, network) const clientsToActivate = [] @@ -299,6 +298,7 @@ export class NotificationBase implements OnInit { this.sync.changeClient(clientKey, clientKey) } else { this.sync.changeClient(clientKey, 'null') + this.api.deleteAllCacheKeyContains(clientKey) } } diff --git a/src/app/tab-preferences/tab-preferences.page.ts b/src/app/tab-preferences/tab-preferences.page.ts index 5fd1ce56..25790cb9 100644 --- a/src/app/tab-preferences/tab-preferences.page.ts +++ b/src/app/tab-preferences/tab-preferences.page.ts @@ -243,6 +243,7 @@ export class Tab3Page { if (this.changeCurrencyLocked) return this.changeCurrencyLocked = true + await this.api.deleteAllCacheKeyContains('coinbase') this.overrideDisplayCurrency = this.unit.pref await this.unit.changeCurrency(this.currentFiatCurrency) @@ -460,7 +461,7 @@ export async function changeNetwork( const newConfig = findConfigForKey(network) await storage.clearCache() - await api.clearCache() + await api.clearNetworkCache() await validatorUtils.clearCache() await storage.setNetworkPreferences(newConfig) diff --git a/src/app/tabs/tabs.page.ts b/src/app/tabs/tabs.page.ts index 20e2699f..9dd73da5 100644 --- a/src/app/tabs/tabs.page.ts +++ b/src/app/tabs/tabs.page.ts @@ -25,7 +25,6 @@ import FirebaseUtils from '../utils/FirebaseUtils' import { MerchantUtils } from '../utils/MerchantUtils' import ThemeUtils from '../utils/ThemeUtils' import { Toast } from '@capacitor/toast' -import { BlockUtils } from '../utils/BlockUtils' @Component({ selector: 'app-tabs', templateUrl: 'tabs.page.html', @@ -37,8 +36,7 @@ export class TabsPage { private sync: SyncService, private storage: StorageService, private merchant: MerchantUtils, - private theme: ThemeUtils, - private blockUtils: BlockUtils + private theme: ThemeUtils ) {} ionViewDidEnter() { @@ -48,11 +46,6 @@ export class TabsPage { private preload() { // lazy initiating firebase token exchange this.firebaseUtils.registerPush() // just initialize the firebaseutils service - try { - this.blockUtils.getMyBlocks(0) // preload blocks - } catch (e) { - console.warn('can not preload blocks') - } // lazy sync & notification token update setTimeout(async () => { diff --git a/src/app/utils/CacheModule.ts b/src/app/utils/CacheModule.ts index cb8eaaad..4a97e17d 100644 --- a/src/app/utils/CacheModule.ts +++ b/src/app/utils/CacheModule.ts @@ -32,54 +32,83 @@ export class CacheModule { private keyPrefix = '' private hardStorage: StorageService = null - initialized: Promise> + protected initialized: Promise - constructor(keyPrefix = '', staleTime = 6 * 60 * 1000, hardStorage: StorageService = null) { + private cache: Map = new Map() + private hotOnly: Map = new Map() + + constructor(keyPrefix = '', staleTime = 6 * 60 * 1000, hardStorage: StorageService = null, callInit: boolean = true) { this.keyPrefix = keyPrefix this.staleTime = staleTime this.hardStorage = hardStorage - this.init() + if (callInit) this.init() } - private async init() { + async init() { if (this.hardStorage) { - this.hardStorage.setObject('cachemodule_' + this.keyPrefix, null) - this.initialized = this.hardStorage.getObject('cachemodule2_' + this.keyPrefix) as Promise> - const result = await this.initialized + this.hardStorage.setObject('cachemodule_' + this.keyPrefix, null, false) + await this.initHardCache() + this.initialized = Promise.resolve() + } else { + this.initialized = Promise.resolve() + } + } + + private async initHardCache() { + // dont load hardStorage if last time it was written too is more than 6 hours ago + const lastWrite = (await this.hardStorage.getObject('cachemodule2_' + this.keyPrefix + '_lastWrite')) as number + if (lastWrite && lastWrite + 6 * 60 * 60 * 1000 < this.getTimestamp()) { + console.log('[CacheModule] hardStorage too old, ignoring') + } else { + const result = (await this.hardStorage.getObject('cachemodule2_' + this.keyPrefix)) as Map if (result) { this.cache = result } - try { - let kiloBytes = null - if (this.hardStorage) { - const size = new TextEncoder().encode(JSON.stringify(this.cache, replacer)).length - kiloBytes = Math.round((size * 100) / 1024) / 100 - } - console.log('[CacheModule] initialized with ', kiloBytes == null ? '(unknown size)' : '(' + kiloBytes + ' KiB)', this.cache) - if (kiloBytes && kiloBytes > 1000) { - console.warn('[CacheModule] storage cap exceeded (1 MB), clearing cache') - await this.clearHardCache() - } - } catch (e) { - console.warn('could not calculate cache size') - } - } else { - this.initialized = new Promise>((resolve) => { - resolve(new Map()) - }) - await this.initialized + this.checkHardCacheSize() } } - private cache: Map = new Map() - private hotOnly: Map = new Map() + private async checkHardCacheSize() { + try { + let kiloBytes = null + if (this.hardStorage) { + const size = new TextEncoder().encode(JSON.stringify(this.cache, replacer)).length + kiloBytes = Math.round((size * 100) / 1024) / 100 + } + console.log('[CacheModule] initialized with ', kiloBytes == null ? '(unknown size)' : '(' + kiloBytes + ' KiB)', this.cache) + if (kiloBytes && kiloBytes > 1000) { + console.warn('[CacheModule] storage cap exceeded (1 MB), clearing cache') + await this.clearHardCache() + } + } catch (e) { + console.warn('could not calculate cache size') + } + } private getStoreForCacheKey(cacheKey: string): Map { // rationale: don't store big data objects in hardStorage due to severe performance impacts - const storeHard = cacheKey.indexOf('app/dashboard') >= 0 || cacheKey.indexOf('beaconcha.in') < 0 // or store all non beaconchain requests + const storeHard = + cacheKey.indexOf('app/dashboard') >= 0 || + cacheKey.indexOf('produced?offset=0') >= 0 || // first page of blocks page + (cacheKey.indexOf('beaconcha.in') < 0 && cacheKey.indexOf('gnosischa.in') < 0 && cacheKey.indexOf('ads.bitfly') < 0) return storeHard ? this.cache : this.hotOnly } + public async deleteAllCacheKeyContains(search: string) { + if (!this.hardStorage) { + return + } + search = search.toLocaleLowerCase() + const keys = Array.from(this.cache.keys()) + for (let i = 0; i < keys.length; i++) { + if (keys[i].toLocaleLowerCase().indexOf(search) >= 0) { + this.cache.delete(keys[i]) + } + } + + await this.hardStorage.setObject('cachemodule2_' + this.keyPrefix, this.cache, false) + } + protected async putCache(key: string, data: unknown, staleTime = this.staleTime) { const cacheKey = this.getKey(key) const store = this.getStoreForCacheKey(cacheKey) @@ -92,7 +121,8 @@ export class CacheModule { try { if (this.hardStorage) { - await this.hardStorage.setObject('cachemodule2_' + this.keyPrefix, this.cache) + await this.hardStorage.setObject('cachemodule2_' + this.keyPrefix, this.cache, false) + this.setLastHardCacheWrite() } } catch (e) { if (isQuotaExceededError(e)) { @@ -101,15 +131,31 @@ export class CacheModule { } } + private setLastHardCacheWrite() { + if (this.hardStorage) { + this.hardStorage.setObject('cachemodule2_' + this.keyPrefix + '_lastWrite', this.getTimestamp(), false) + } + } + async clearCache() { await this.clearHardCache() - this.cache.clear() + this.hotOnly.clear() + } + + async clearNetworkCache() { + if (this.hardStorage) { + const network = await this.hardStorage.getNetworkPreferences() + this.deleteAllCacheKeyContains(network.key == 'main' ? '//beaconcha.in' : '//' + network.key) + } + this.hotOnly.clear() } async clearHardCache() { if (this.hardStorage) { - await this.hardStorage.setObject('cachemodule2_' + this.keyPrefix, null) + await this.hardStorage.setObject('cachemodule2_' + this.keyPrefix, null, false) + this.setLastHardCacheWrite() + this.cache.clear() } } @@ -164,7 +210,8 @@ export class CacheModule { invalidateAllCache() { this.cache = new Map() if (this.hardStorage) { - this.hardStorage.setObject('cachemodule2_' + this.keyPrefix, null) + this.hardStorage.setObject('cachemodule2_' + this.keyPrefix, null, false) + this.setLastHardCacheWrite() } } diff --git a/src/app/utils/ClientUpdateUtils.ts b/src/app/utils/ClientUpdateUtils.ts index f4a2c5b8..8336c0b0 100644 --- a/src/app/utils/ClientUpdateUtils.ts +++ b/src/app/utils/ClientUpdateUtils.ts @@ -119,6 +119,7 @@ export default class ClientUpdateUtils { private oldClientInfoConverted = false updates: Release[] = null lastTry = 0 + private locked = false constructor(private api: ApiService, private storage: StorageService) {} @@ -137,7 +138,9 @@ export default class ClientUpdateUtils { async checkAllUpdates() { if (this.lastTry + 10 * 60 * 1000 > Date.now()) return + if (this.locked) return + this.locked = true const promiseArray: Promise[] = [] for (let i = 0; i < Clients.length; i++) { promiseArray.push(this.checkUpdateFor(await this.storage.getItem(Clients[i].storageKey))) @@ -163,6 +166,7 @@ export default class ClientUpdateUtils { } catch (error) { console.error('An error occurred:', error) } + this.locked = false } async checkClientUpdate(clientKey: string) { diff --git a/src/app/utils/EthereumUnits.ts b/src/app/utils/EthereumUnits.ts index fd505a5c..acfc6c9c 100644 --- a/src/app/utils/EthereumUnits.ts +++ b/src/app/utils/EthereumUnits.ts @@ -40,7 +40,8 @@ export default class Unit { public static RETH = new Unit('RETH', new BigNumber('1'), 2) public static XDAI = new Unit('xDAI', new BigNumber('1'), 3, 'XXX-DAI', 'xDAI') - public static GNO = new Unit('GNO', new BigNumber('1'), 5, 'XXX-GNO', 'GNO') + public static GNO = new Unit('GNO', new BigNumber('0.03125'), 5, 'XXX-GNO', 'GNO') + public static MGNO = new Unit('mGNO', new BigNumber('1'), 5, 'XXX-GNO', 'mGNO') public static USDETH = new Unit('$', new BigNumber('1'), 2, 'XXX-USD', 'Dollar') public static EURETH = new Unit('€', new BigNumber('1'), 2, 'XXX-EUR', 'Euro') @@ -103,6 +104,7 @@ export const MAPPING = new Map([ ['NO_CURRENCY', Unit.NO_CURRENCY], ['RETH', Unit.RETH], ['GNO', Unit.GNO], + ['mGNO', Unit.MGNO], ['xDAI', Unit.XDAI], ['RUBLE', Unit.RUBETH], diff --git a/src/app/utils/NetworkData.ts b/src/app/utils/NetworkData.ts index 93c925c7..58873f34 100644 --- a/src/app/utils/NetworkData.ts +++ b/src/app/utils/NetworkData.ts @@ -127,6 +127,6 @@ export function findConfigForKey(key: string): ApiNetwork { return entry } } - console.debug('config for ' + key + ' not found, using mainnet instead', key) + console.log('config for ' + key + ' not found, using mainnet instead', key) return MAP[0] } diff --git a/src/app/utils/ValidatorUtils.ts b/src/app/utils/ValidatorUtils.ts index 5ad647c1..0b43a7c9 100644 --- a/src/app/utils/ValidatorUtils.ts +++ b/src/app/utils/ValidatorUtils.ts @@ -358,7 +358,7 @@ export class ValidatorUtils extends CacheModule { return result } - async getDashboardDataValidators(storage: 0 | 1, ...validators): Promise { + private async getDashboardDataValidators(storage: 0 | 1, ...validators): Promise { const request = new DashboardRequest(...validators) const response = await this.api.execute(request) if (!request.wasSuccessful(response)) { From 8368c81d81feb419f65ed14ed60302a4c77623f3 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Mon, 23 Oct 2023 10:16:20 +0200 Subject: [PATCH 23/53] wait for result before we ask for api cache to prevent double request execution --- src/app/services/api.service.ts | 117 ++++++++++--------- src/app/tab-preferences/notification-base.ts | 2 +- 2 files changed, 64 insertions(+), 55 deletions(-) diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index 46909faa..8beb6b3c 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -145,7 +145,17 @@ export class ApiService extends CacheModule { const response = req.parse(resp) const result = response[0] - console.log('Refresh token', result, resp) + // Intention to not log access token in app logs + if (this.debug) { + console.log('Refresh token', result, resp) + } else { + if (result && result.access_token) { + console.log('Refresh token', 'success') + } else { + console.log('Refresh token', result, resp) + } + } + if (!result || !result.access_token) { console.warn('could not refresh token', result) return null @@ -160,15 +170,12 @@ export class ApiService extends CacheModule { private async lockOrWait(resource) { if (!this.awaitingResponses[resource]) { - console.log('Locking ', resource) this.awaitingResponses[resource] = new Mutex() } await this.awaitingResponses[resource].acquire() } private unlock(resource) { - console.log('Unlocking ', resource) - this.awaitingResponses[resource].release() } @@ -197,68 +204,70 @@ export class ApiService extends CacheModule { this.invalidateCache() } - // If cached and not stale, return cache - const cached = (await this.getCache(this.getCacheKey(request))) as Response - if (cached) { - if (this.lastRefreshed == 0) this.lastRefreshed = Date.now() - cached.cached = true - return cached - } + await this.lockOrWait(request.resource) - const options = request.options + try { + // If cached and not stale, return cache + const cached = (await this.getCache(this.getCacheKey(request))) as Response + if (cached) { + if (this.lastRefreshed == 0) this.lastRefreshed = Date.now() + cached.cached = true + return cached + } - // second is special case for notifications - // notifications are rescheduled if response is != 200 - // but user can switch network in the mean time, so we need to reapply the network - // the user was currently on, when they set the notification toggle - // hence the additional request.requiresAuth - if (request.endPoint == 'default' || request.requiresAuth) { - const authHeader = await this.getAuthHeader(request instanceof RefreshTokenRequest) + const options = request.options - if (authHeader) { - const headers = { ...options.headers, ...authHeader } - options.headers = headers - } - } + // second is special case for notifications + // notifications are rescheduled if response is != 200 + // but user can switch network in the mean time, so we need to reapply the network + // the user was currently on, when they set the notification toggle + // hence the additional request.requiresAuth + if (request.endPoint == 'default' || request.requiresAuth) { + const authHeader = await this.getAuthHeader(request instanceof RefreshTokenRequest) - await this.lockOrWait(request.resource) + if (authHeader) { + const headers = { ...options.headers, ...authHeader } + options.headers = headers + } + } - console.log(LOGTAG + ' Send request: ' + request.resource, request.method, request) - const startTs = Date.now() - - let response: Promise - switch (request.method) { - case Method.GET: - response = this.get(request.resource, request.endPoint, request.ignoreFails, options) - break - case Method.POST: - response = this.post(request.resource, request.postData, request.endPoint, request.ignoreFails, options) - break - default: - throw 'Unsupported method: ' + request.method - } + console.log(LOGTAG + ' Send request: ' + request.resource, request.method, request) + const startTs = Date.now() + + let response: Promise + switch (request.method) { + case Method.GET: + response = this.get(request.resource, request.endPoint, request.ignoreFails, options) + break + case Method.POST: + response = this.post(request.resource, request.postData, request.endPoint, request.ignoreFails, options) + break + default: + throw 'Unsupported method: ' + request.method + } - const result = await response - this.updateConnectionState(request.ignoreFails, result && result.data && !!result.url) + const result = await response + this.updateConnectionState(request.ignoreFails, result && result.data && !!result.url) - if (!result) { - this.unlock(request.resource) - console.log(LOGTAG + ' Empty Response: ' + request.resource, Date.now() - startTs) - return result - } + if (!result) { + console.log(LOGTAG + ' Empty Response: ' + request.resource, 'took ' + (Date.now() - startTs) + 'ms') + return result + } - if ((request.method == Method.GET || request.cacheablePOST) && result && result.status == 200 && result.data) { - this.putCache(this.getCacheKey(request), result, request.maxCacheAge) - } + if ((request.method == Method.GET || request.cacheablePOST) && result && result.status == 200 && result.data) { + this.putCache(this.getCacheKey(request), result, request.maxCacheAge) + } - if (request.updatesLastRefreshState) this.updateLastRefreshed(result) + if (request.updatesLastRefreshState) this.updateLastRefreshed(result) - this.unlock(request.resource) - console.log(LOGTAG + ' Response: ' + result.url + '', result, Date.now() - startTs) + console.log(LOGTAG + ' Response: ' + result.url + '', 'took ' + (Date.now() - startTs) + 'ms', result) - result.cached = false + result.cached = false - return result + return result + } finally { + this.unlock(request.resource) + } } async clearSpecificCache(request: APIRequest) { diff --git a/src/app/tab-preferences/notification-base.ts b/src/app/tab-preferences/notification-base.ts index 9b5e7e46..f71da124 100644 --- a/src/app/tab-preferences/notification-base.ts +++ b/src/app/tab-preferences/notification-base.ts @@ -208,7 +208,7 @@ export class NotificationBase implements OnInit { const net = this.api.networkConfig.net this.storage.setBooleanSetting(net + SETTING_NOTIFY, this.notify) this.settingsChanged = true - if (!this.api.isNotMainnet()) { + if (this.api.isMainnet()) { this.sync.changeGeneralNotify(this.notify) } From 65438cf706394ba3d4c5bce2453ebff131ff8c75 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Mon, 23 Oct 2023 11:07:55 +0200 Subject: [PATCH 24/53] remove artificial splashscreen remove delay --- src/app/app.component.ts | 7 +------ src/app/services/boot-preload.service.ts | 19 +++++++++++++++++++ src/app/utils/ThemeUtils.ts | 2 +- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 66fb9084..1404150c 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -48,9 +48,9 @@ export class AppComponent { } initializeApp() { + BigNumber.config({ DECIMAL_PLACES: 25 }) this.platform.ready().then(() => { this.storage.migrateToCapacitor3().then(async () => { - BigNumber.config({ DECIMAL_PLACES: 25 }) const networkName = this.api.getNetworkName() // migrate to 3.2+ const result = await this.storage.getBooleanSetting(networkName + 'migrated_to_3.2', false) @@ -66,11 +66,6 @@ export class AppComponent { }) // just initialize the theme service this.setAndroidBackButtonBehavior() - - /* AdMob.initialize({ - requestTrackingAuthorization: false, - testingDevices: [] - });*/ }) }) } diff --git a/src/app/services/boot-preload.service.ts b/src/app/services/boot-preload.service.ts index 4cfa1268..a972508f 100644 --- a/src/app/services/boot-preload.service.ts +++ b/src/app/services/boot-preload.service.ts @@ -1,3 +1,22 @@ +/* + * // Copyright (C) 2020 - 2021 Bitfly GmbH + * // + * // This file is part of Beaconchain Dashboard. + * // + * // Beaconchain Dashboard is free software: you can redistribute it and/or modify + * // it under the terms of the GNU General Public License as published by + * // the Free Software Foundation, either version 3 of the License, or + * // (at your option) any later version. + * // + * // Beaconchain Dashboard is distributed in the hope that it will be useful, + * // but WITHOUT ANY WARRANTY; without even the implied warranty of + * // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * // GNU General Public License for more details. + * // + * // You should have received a copy of the GNU General Public License + * // along with Beaconchain Dashboard. If not, see . + */ + import { Injectable } from '@angular/core' import { ValidatorUtils } from '../utils/ValidatorUtils' import ClientUpdateUtils from '../utils/ClientUpdateUtils' diff --git a/src/app/utils/ThemeUtils.ts b/src/app/utils/ThemeUtils.ts index 4e354ae2..eb694330 100644 --- a/src/app/utils/ThemeUtils.ts +++ b/src/app/utils/ThemeUtils.ts @@ -60,7 +60,7 @@ export default class ThemeUtils { setTimeout(() => { splashScreenCallback() this.applyColorInitially() - }, 200) + }, 10) return preferenceDarkMode as StoredTheme }) } From ef8733232f98c4bcf3028fc675aa9c0c93e6ddfe Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Mon, 23 Oct 2023 11:22:31 +0200 Subject: [PATCH 25/53] fix gno price convertion --- src/app/services/unitconv.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/services/unitconv.service.ts b/src/app/services/unitconv.service.ts index 6e96ba02..87b6b02c 100644 --- a/src/app/services/unitconv.service.ts +++ b/src/app/services/unitconv.service.ts @@ -322,7 +322,7 @@ export class UnitconvService { async updatePriceData() { const consPrice = await this.getPriceData(this.pref.Cons.unit) if (consPrice) { - this.lastPrice.Cons = consPrice + this.lastPrice.Cons = consPrice.multipliedBy(MAPPING.get(this.getNetworkDefaultCurrency(this.pref.Cons)).value) this.storage.setObject(this.getLastPriceKey(this.pref.Cons), { lastPrice: this.lastPrice.Cons } as LastPrice) } else { this.lastPrice.Cons = this.pref.Cons.unit.value @@ -333,7 +333,7 @@ export class UnitconvService { const execPrice = await this.getPriceData(this.pref.Exec.unit) if (execPrice) { - this.lastPrice.Exec = execPrice + this.lastPrice.Exec = execPrice.multipliedBy(MAPPING.get(this.getNetworkDefaultCurrency(this.pref.Exec)).value) this.storage.setObject(this.getLastPriceKey(this.pref.Exec), { lastPrice: this.lastPrice.Exec } as LastPrice) } else { this.lastPrice.Exec = this.pref.Exec.unit.value From 8770aaf3ff89a1fdea9624f6670d8a1a10e16669 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Mon, 23 Oct 2023 15:28:49 +0200 Subject: [PATCH 26/53] BIDS-2192: clear search when switching networks --- src/app/tab-preferences/tab-preferences.page.ts | 2 +- src/app/tab-validators/tab-validators.page.html | 3 ++- src/app/tab-validators/tab-validators.page.scss | 7 +++++++ src/app/tab-validators/tab-validators.page.ts | 10 ++++++++-- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/app/tab-preferences/tab-preferences.page.ts b/src/app/tab-preferences/tab-preferences.page.ts index 25790cb9..b3355650 100644 --- a/src/app/tab-preferences/tab-preferences.page.ts +++ b/src/app/tab-preferences/tab-preferences.page.ts @@ -461,7 +461,7 @@ export async function changeNetwork( const newConfig = findConfigForKey(network) await storage.clearCache() - await api.clearNetworkCache() + //await api.clearNetworkCache() await validatorUtils.clearCache() await storage.setNetworkPreferences(newConfig) diff --git a/src/app/tab-validators/tab-validators.page.html b/src/app/tab-validators/tab-validators.page.html index 839336ca..0a533e4a 100644 --- a/src/app/tab-validators/tab-validators.page.html +++ b/src/app/tab-validators/tab-validators.page.html @@ -38,7 +38,8 @@ . */ -import { Component } from '@angular/core' +import { Component, ViewChild } from '@angular/core' import { ValidatorUtils, Validator, ValidatorState } from '../utils/ValidatorUtils' -import { ModalController, Platform } from '@ionic/angular' +import { IonSearchbar, ModalController, Platform } from '@ionic/angular' import { ValidatordetailPage } from '../pages/validatordetail/validatordetail.page' import { ApiService } from '../services/api.service' import { AlertController } from '@ionic/angular' @@ -67,6 +67,8 @@ export class Tab2Page { selected = new Map() + @ViewChild('searchbarRef', { static: true }) searchbarRef: IonSearchbar + constructor( private validatorUtils: ValidatorUtils, public modalController: ModalController, @@ -81,6 +83,10 @@ export class Tab2Page { public unit: UnitconvService ) { this.validatorUtils.registerListener(() => { + if (this.searchResultMode && this.searchbarRef) { + this.searchResultMode = false + this.searchbarRef.value = null + } this.refresh() }) this.merchant.getCurrentPlanMaxValidator().then((result) => { From 924e5e2cba48e6c4dd80e7c050170c36c34aecc3 Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 23 Oct 2023 15:58:49 +0200 Subject: [PATCH 27/53] restore old browser list --- .browserslistrc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.browserslistrc b/.browserslistrc index 2ff4ef04..0066735a 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -8,7 +8,8 @@ # You can see what browsers were selected by your queries by running: # npx browserslist -last 2 Chrome versions -last 2 ChromeAndroid versions -last 2 Safari versions -last 2 iOS versions \ No newline at end of file +> 0.5% +last 2 versions +Firefox ESR +not dead +not IE 9-11 # For IE 9-11 support, remove 'not'. \ No newline at end of file From 8836665221d84ceb36a4f0f0570f153e297d3c37 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:14:32 +0200 Subject: [PATCH 28/53] updated readme, android version++ --- README.md | 3 +- android/app/build.gradle | 4 +- android/app/src/main/AndroidManifest.xml | 67 ++++-------------------- 3 files changed, 14 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index bb4e7704..60e42fa9 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # Beaconchain Dashboard App -Beaconchain Dashboard is an open source ethereum validator performance tracker app for Android and iOS. It utilizes the beaconcha.in API. +Beaconchain Dashboard is an open source ethereum and gnosis validator performance tracker app for Android and iOS. It utilizes the beaconcha.in API. [![Get it on Google Play](https://beaconcha.in/img/android.png)](https://play.google.com/store/apps/details?id=in.beaconcha.mobile) @@ -15,6 +15,7 @@ Beaconchain Dashboard is an Angular app written in Typescript, HTML & CSS. It ut ## Features +- Ethereum and Gnosis supported - Keep track on your validators online status, balances, returns and more - Various notification alerts for your validators - Execution block rewards overview diff --git a/android/app/build.gradle b/android/app/build.gradle index 65ea11a4..d0625d38 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 101 - versionName "4.3.2" + versionCode 103 + versionName "4.4.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 447d6c50..5d050c31 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,90 +1,43 @@ - + - - - - - - - - - - + + + - - + - - - + - - + - - + - - - - - + + - - - From 130f83880cb1e09e2bdc994b9e2f40becef51a35 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Tue, 24 Oct 2023 09:20:26 +0200 Subject: [PATCH 29/53] no need for dashboard request on fresh install --- src/app/utils/ValidatorUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/utils/ValidatorUtils.ts b/src/app/utils/ValidatorUtils.ts index 0b43a7c9..1422185d 100644 --- a/src/app/utils/ValidatorUtils.ts +++ b/src/app/utils/ValidatorUtils.ts @@ -262,6 +262,7 @@ export class ValidatorUtils extends CacheModule { async getAllMyValidators(): Promise { const storageKey = this.getStorageKey() const local = await this.getMapWithoutDeleted(storageKey) + if (local.size == 0) return [] const validatorString = getValidatorQueryString([...local.values()], 2000, (await this.merchantUtils.getCurrentPlanMaxValidator()) - 1) From 106e7a9f7b47ec0b68cec293aa9ad90240930edc Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Tue, 24 Oct 2023 11:17:34 +0200 Subject: [PATCH 30/53] important naming fix --- src/app/services/unitconv.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/services/unitconv.service.ts b/src/app/services/unitconv.service.ts index 87b6b02c..753e6e6d 100644 --- a/src/app/services/unitconv.service.ts +++ b/src/app/services/unitconv.service.ts @@ -54,7 +54,7 @@ export class UnitconvService { } private async init() { - await this.migrateToPostGnosisEra() + await this.migrateToGnosisEra() this.lastPrice = { Cons: await this.getLastStoredPrice(this.pref.Cons), @@ -296,7 +296,7 @@ export class UnitconvService { } as StoredPref) } - private async migrateToPostGnosisEra() { + private async migrateToGnosisEra() { const migratedToGnosis = await this.storage.getBooleanSetting('migrated_gnosis', false) if (!migratedToGnosis) { const oldCons = await this.loadStored(STORAGE_KEY_CONS) From 4786a16985150945e0f19d8ea5265b63d82d2720 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Tue, 24 Oct 2023 13:45:58 +0200 Subject: [PATCH 31/53] add el 1y and total --- src/app/controllers/OverviewController.ts | 14 ++++++++++++-- src/app/requests/requests.ts | 2 ++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/app/controllers/OverviewController.ts b/src/app/controllers/OverviewController.ts index 4f194b67..ffa64ac6 100644 --- a/src/app/controllers/OverviewController.ts +++ b/src/app/controllers/OverviewController.ts @@ -325,15 +325,25 @@ export default class OverviewController { new BigNumber(cur.execshare == null ? 1 : cur.execshare) ) ) + const performance365d = this.sumBigIntPerformanceRP(validators, (cur) => + this.sumExcludeSmoothingPool(cur, (fieldCur) => fieldCur.execution.performance365d.toString()).multipliedBy( + new BigNumber(cur.execshare == null ? 1 : cur.execshare) + ) + ) + const total = this.sumBigIntPerformanceRP(validators, (cur) => + this.sumExcludeSmoothingPool(cur, (fieldCur) => fieldCur.execution.performanceTotal.toString()).multipliedBy( + new BigNumber(cur.execshare == null ? 1 : cur.execshare) + ) + ) const aprExecution = this.getAPRFromMonth(validatorDepositActive, aprPerformance31dExecution) // todo return { performance1d: performance1d, performance31d: performance31d, performance7d: performance7d, - performance365d: new BigNumber(0), // not yet implemented + performance365d: performance365d, apr: aprExecution, - total: new BigNumber(0), // not yet implemented + total: total, } } diff --git a/src/app/requests/requests.ts b/src/app/requests/requests.ts index 9e1c36a2..6d201380 100644 --- a/src/app/requests/requests.ts +++ b/src/app/requests/requests.ts @@ -299,6 +299,8 @@ export interface ExecutionResponse { performance1d: number performance7d: number performance31d: number + performance365d: number + performanceTotal: number } export interface NotificationGetResponse { From 128440d8f4ca1457eda8860699ca763728cb4b77 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Tue, 24 Oct 2023 15:15:27 +0200 Subject: [PATCH 32/53] address findings of code review --- .../dashboard/dashboard.component.html | 6 +-- .../dashboard/dashboard.component.ts | 25 ++++++---- .../machinechart/machinechart.component.ts | 5 +- .../pages/notifications/notifications.page.ts | 2 +- src/app/pages/subscribe/subscribe.page.ts | 2 +- src/app/services/api.service.ts | 8 +-- src/app/services/unitconv.service.ts | 24 ++++++++- src/app/tab-preferences/notification-base.ts | 13 +++-- .../tab-preferences/tab-preferences.page.ts | 3 +- src/app/utils/BlockUtils.ts | 7 +-- src/app/utils/CacheModule.ts | 27 ++++++---- src/app/utils/HighchartOptions.ts | 4 +- src/app/utils/MerchantUtils.ts | 4 +- src/app/utils/ValidatorUtils.ts | 49 +++---------------- 14 files changed, 90 insertions(+), 89 deletions(-) diff --git a/src/app/components/dashboard/dashboard.component.html b/src/app/components/dashboard/dashboard.component.html index 0e99af21..6fe7af03 100644 --- a/src/app/components/dashboard/dashboard.component.html +++ b/src/app/components/dashboard/dashboard.component.html @@ -851,10 +851,10 @@
- - {{ unit.pref.Cons.getCurrencyName() }} Price + + {{ unit.getNetworkDefaultUnit('cons').display }} Price - {{ 1 | mcurrency : 'ETHER' : unit.pref.Cons }} + {{ unit.lastPrice.Cons | mcurrency : unit.getFiatCurrency('cons') : unit.getFiatCurrency('cons') }} diff --git a/src/app/components/dashboard/dashboard.component.ts b/src/app/components/dashboard/dashboard.component.ts index 30b1557a..d64fdf3e 100644 --- a/src/app/components/dashboard/dashboard.component.ts +++ b/src/app/components/dashboard/dashboard.component.ts @@ -336,8 +336,8 @@ export class DashboardComponent implements OnInit { //this.doneLoading = false this.storage.getBooleanSetting('rank_percent_mode', false).then((result) => (this.rankPercentMode = result)) this.storage.getItem('rpl_pdisplay_mode').then((result) => (this.rplState = result ? result : 'rpl')) - highChartOptions(HighCharts) - highChartOptions(Highstock) + highChartOptions(HighCharts, this.api.getHostName()) + highChartOptions(Highstock, this.api.getHostName()) this.merchant.getCurrentPlanMaxValidator().then((result) => { this.currentPackageMaxValidators = result }) @@ -661,14 +661,14 @@ export class DashboardComponent implements OnInit { const network = this.api.getNetwork() const getValueString = (value: BigNumber, type: RewardType): string => { - let text = `${value.toFixed(5)} ETH` + let text = `${value.toFixed(5)} ` + this.unit.getNetworkDefaultUnit(type).display if (type == 'cons') { - if (this.unit.isDefaultCurrency(this.unit.pref.Cons) && network.key == 'main') { - text += ` (${this.unit.convertToPref(value, 'ETHER', type)})` + if (!this.unit.isDefaultCurrency(this.unit.pref.Cons)) { + text += ` (${this.unit.convertToPref(value, this.unit.getNetworkDefaultCurrency(type), type)})` } } else if (type == 'exec') { - if (this.unit.isDefaultCurrency(this.unit.pref.Exec) && network.key == 'main') { - text += ` (${this.unit.convertToPref(value, 'ETHER', type)})` + if (!this.unit.isDefaultCurrency(this.unit.pref.Exec)) { + text += ` (${this.unit.convertToPref(value, this.unit.getNetworkDefaultCurrency(type), type)})` } } @@ -720,11 +720,16 @@ export class DashboardComponent implements OnInit { let text = this.getChartToolTipCaption(tooltip.chart.hoverPoints[0].x, network.genesisTs, tooltip.chart.hoverPoints[0].dataGroup.length) // income - let consAndExecSameCurrency = 0 + let lastCurrency = null + let consAndExecSameCurrency = true let total = new BigNumber(0) for (let i = 0; i < tooltip.chart.hoverPoints.length; i++) { const type = tooltip.chart.hoverPoints[i].series.name == 'Execution' ? 'exec' : 'cons' - consAndExecSameCurrency += type == 'exec' ? 1 : -1 + const currency = this.unit.getNetworkDefaultCurrency(type) + if (lastCurrency != null && lastCurrency != currency) { + consAndExecSameCurrency = false + } + lastCurrency = currency const value = new BigNumber(tooltip.chart.hoverPoints[i].y) text += ` ${ @@ -735,7 +740,7 @@ export class DashboardComponent implements OnInit { // add total if hovered point contains rewards for both EL and CL // only if both exec and cons currencies are the same - if (tooltip.chart.hoverPoints.length > 1 && Math.abs(consAndExecSameCurrency) == 2) { + if (tooltip.chart.hoverPoints.length > 1 && consAndExecSameCurrency) { text += `Total: ${getValueString(total, 'cons')}` } diff --git a/src/app/components/machinechart/machinechart.component.ts b/src/app/components/machinechart/machinechart.component.ts index 387f103d..2374c8fa 100644 --- a/src/app/components/machinechart/machinechart.component.ts +++ b/src/app/components/machinechart/machinechart.component.ts @@ -3,6 +3,7 @@ import { highChartOptions } from 'src/app/utils/HighchartOptions' import * as HighCharts from 'highcharts' import * as Highstock from 'highcharts/highstock' import { MachineChartData } from 'src/app/controllers/MachineController' +import { ApiService } from 'src/app/services/api.service' @Component({ selector: 'app-machinechart', @@ -26,6 +27,8 @@ export class MachinechartComponent implements OnInit { chartError = false specificError: string = null + constructor(private api: ApiService) {} + doClick() { this.clickAction() } @@ -58,7 +61,7 @@ export class MachinechartComponent implements OnInit { } ngOnInit() { - highChartOptions(Highstock) + highChartOptions(Highstock, this.api.getHostName()) this.id = makeid(6) } diff --git a/src/app/pages/notifications/notifications.page.ts b/src/app/pages/notifications/notifications.page.ts index 726d0400..b0329c7a 100644 --- a/src/app/pages/notifications/notifications.page.ts +++ b/src/app/pages/notifications/notifications.page.ts @@ -84,7 +84,7 @@ export class NotificationsPage extends NotificationBase implements OnInit { }) this.storage.getAuthUser().then((result) => (this.authUser = result)) - this.network = this.api.getNetworkName() + this.network = this.api.capitalize(this.api.getNetworkName()) this.merchantUtils.hasCustomizableNotifications().then((result) => { this.canCustomizeThresholds = result }) diff --git a/src/app/pages/subscribe/subscribe.page.ts b/src/app/pages/subscribe/subscribe.page.ts index 8fc5426b..9490e426 100644 --- a/src/app/pages/subscribe/subscribe.page.ts +++ b/src/app/pages/subscribe/subscribe.page.ts @@ -101,7 +101,7 @@ export class SubscribePage implements OnInit { async purchaseIntern() { let loggedIn = await this.storage.isLoggedIn() if (!loggedIn) { - this.alertService.confirmDialog('Login', 'You need to login to your beaconcha.in account first. Continue?', 'Login', () => { + this.alertService.confirmDialog('Login', 'You need to login to your ' + this.api.getHostName() + ' account first. Continue?', 'Login', () => { this.oauth.login().then(async () => { loggedIn = await this.storage.isLoggedIn() if (loggedIn) this.continuePurchaseIntern() diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index 8beb6b3c..b09bc3a7 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -48,7 +48,7 @@ export class ApiService extends CacheModule { private lastCacheInvalidate = 0 constructor(private storage: StorageService) { - super('api', 6 * 60 * 1000, storage, false) + super('api', 6 * 60 * 1000, storage, 1000, false) this.storage.getBooleanSetting('migrated_4_4_0', false).then((migrated) => { if (!migrated) { this.clearHardCache() @@ -179,13 +179,13 @@ export class ApiService extends CacheModule { this.awaitingResponses[resource].release() } - isNotMainnet(): boolean { + isNotEthereumMainnet(): boolean { const test = this.networkConfig.net != '' return test } - isMainnet(): boolean { - return !this.isNotMainnet() + isEthereumMainnet(): boolean { + return !this.isNotEthereumMainnet() } private getCacheKey(request: APIRequest): string { diff --git a/src/app/services/unitconv.service.ts b/src/app/services/unitconv.service.ts index 753e6e6d..192fbd9e 100644 --- a/src/app/services/unitconv.service.ts +++ b/src/app/services/unitconv.service.ts @@ -40,7 +40,7 @@ export class UnitconvService { Exec: { value: 'ETHER', type: 'exec', unit: Unit.ETHER } as Currency, RPL: { value: 'ETHER', type: 'rpl', unit: Unit.RPL } as Currency, } - private lastPrice: LastPrice + public lastPrice: LastPrice private rplETHPrice: BigNumber = new BigNumber(1) /* @@ -151,6 +151,10 @@ export class UnitconvService { return currency.value == this.getNetworkDefaultCurrency(currency.type) } + public getNetworkDefaultUnit(type: RewardType): Unit { + return MAPPING.get(this.getNetworkDefaultCurrency(type)) + } + public getNetworkDefaultCurrency(type: RewardType | Currency): string { if (typeof type == 'object') { type = type.type @@ -168,6 +172,22 @@ export class UnitconvService { return 'ETHER' } + public getFiatCurrency(type: RewardType) { + if (type == 'cons') { + if (this.isDefaultCurrency(this.pref.Cons)) { + return UnitconvService.currencyPipe.Cons + } else { + return this.pref.Cons.value + } + } else if (type == 'exec') { + if (this.isDefaultCurrency(this.pref.Exec)) { + return UnitconvService.currencyPipe.Exec + } else { + return this.pref.Exec.value + } + } + } + private async getLastStoredPrice(currency: Currency) { const lastUpdatedConsPrice = (await this.storage.getObject(this.getLastPriceKey(currency))) as LastPrice if (lastUpdatedConsPrice && lastUpdatedConsPrice.lastPrice) { @@ -256,7 +276,7 @@ export class UnitconvService { * (cons and exec could have different conversions so they cant use the same reference to unit) */ public convertNonFiat(value: BigNumber | number | string, from: string, to: string, displayable = true) { - if (MAPPING.get(to).coinbaseSpot != null && to != 'ETH' && to != 'ETHER') { + if (MAPPING.get(to).coinbaseSpot != null && to != 'ETH' && to != 'ETHER' && from != to) { console.warn('convertNonFiat does not support fiat currencies. Use convert instead', value.toString(), from, to, displayable) } return this.convertBase(value, from, MAPPING.get(to), displayable) diff --git a/src/app/tab-preferences/notification-base.ts b/src/app/tab-preferences/notification-base.ts index f71da124..c92c3de8 100644 --- a/src/app/tab-preferences/notification-base.ts +++ b/src/app/tab-preferences/notification-base.ts @@ -158,7 +158,7 @@ export class NotificationBase implements OnInit { // locking toggle so we dont execute onChange when setting initial values const preferences = await this.storage.loadPreferencesToggles(net) - if (this.api.isNotMainnet()) { + if (this.api.isNotEthereumMainnet()) { this.notify = preferences this.notifyInitialized = true this.disableToggleLock() @@ -188,7 +188,9 @@ export class NotificationBase implements OnInit { if (!(await this.isSupportedOnAndroid())) { this.alerts.showInfo( 'Play Service', - 'Your device can not receive push notifications. Please note that notifications do not work without Google Play Services. As an alternative you can configure webhook notifications on the beaconcha.in website, otherwise changing these settings will have no effect.' + 'Your device can not receive push notifications. Please note that notifications do not work without Google Play Services. As an alternative you can configure webhook notifications on the ' + + this.api.getHostName() + + ' website, otherwise changing these settings will have no effect.' ) } @@ -208,7 +210,10 @@ export class NotificationBase implements OnInit { const net = this.api.networkConfig.net this.storage.setBooleanSetting(net + SETTING_NOTIFY, this.notify) this.settingsChanged = true - if (this.api.isMainnet()) { + // TODO: instead of using this.api.isEthereumMainnet(), check each app supported network and if + // one single network has notifications enabled, also sync general als true. If all are disabled, + // set general notify as false + if (this.api.isEthereumMainnet() || this.notify == true) { this.sync.changeGeneralNotify(this.notify) } @@ -298,7 +303,7 @@ export class NotificationBase implements OnInit { this.sync.changeClient(clientKey, clientKey) } else { this.sync.changeClient(clientKey, 'null') - this.api.deleteAllCacheKeyContains(clientKey) + this.api.deleteAllHardStorageCacheKeyContains(clientKey) } } diff --git a/src/app/tab-preferences/tab-preferences.page.ts b/src/app/tab-preferences/tab-preferences.page.ts index b3355650..5feb7dd5 100644 --- a/src/app/tab-preferences/tab-preferences.page.ts +++ b/src/app/tab-preferences/tab-preferences.page.ts @@ -243,7 +243,7 @@ export class Tab3Page { if (this.changeCurrencyLocked) return this.changeCurrencyLocked = true - await this.api.deleteAllCacheKeyContains('coinbase') + await this.api.deleteAllHardStorageCacheKeyContains('coinbase') this.overrideDisplayCurrency = this.unit.pref await this.unit.changeCurrency(this.currentFiatCurrency) @@ -462,7 +462,6 @@ export async function changeNetwork( const newConfig = findConfigForKey(network) await storage.clearCache() //await api.clearNetworkCache() - await validatorUtils.clearCache() await storage.setNetworkPreferences(newConfig) await api.initialize() diff --git a/src/app/utils/BlockUtils.ts b/src/app/utils/BlockUtils.ts index e577e73d..312fbad4 100644 --- a/src/app/utils/BlockUtils.ts +++ b/src/app/utils/BlockUtils.ts @@ -21,7 +21,6 @@ import { ApiService } from '../services/api.service' import { Injectable } from '@angular/core' import { BlockProducedByRequest, BlockResponse, DashboardRequest } from '../requests/requests' -import { CacheModule } from './CacheModule' import BigNumber from 'bignumber.js' import { ValidatorUtils } from './ValidatorUtils' @@ -33,10 +32,8 @@ const MONTH = 60 * 60 * 24 * 30 @Injectable({ providedIn: 'root', }) -export class BlockUtils extends CacheModule { - constructor(public api: ApiService, public validatorUtils: ValidatorUtils) { - super() - } +export class BlockUtils { + constructor(public api: ApiService, public validatorUtils: ValidatorUtils) {} async getBlockRewardWithShare(block: BlockResponse): Promise { const proposer = block.posConsensus.proposerIndex diff --git a/src/app/utils/CacheModule.ts b/src/app/utils/CacheModule.ts index 4a97e17d..f46e6f7d 100644 --- a/src/app/utils/CacheModule.ts +++ b/src/app/utils/CacheModule.ts @@ -37,14 +37,23 @@ export class CacheModule { private cache: Map = new Map() private hotOnly: Map = new Map() - constructor(keyPrefix = '', staleTime = 6 * 60 * 1000, hardStorage: StorageService = null, callInit: boolean = true) { + private hardStorageSizeLimit: number + + constructor( + keyPrefix = '', + staleTime = 6 * 60 * 1000, + hardStorage: StorageService = null, + hardStorageSizeLimit: number = 1000, + callInit: boolean = true + ) { this.keyPrefix = keyPrefix this.staleTime = staleTime this.hardStorage = hardStorage + this.hardStorageSizeLimit = hardStorageSizeLimit if (callInit) this.init() } - async init() { + protected async init() { if (this.hardStorage) { this.hardStorage.setObject('cachemodule_' + this.keyPrefix, null, false) await this.initHardCache() @@ -76,7 +85,7 @@ export class CacheModule { kiloBytes = Math.round((size * 100) / 1024) / 100 } console.log('[CacheModule] initialized with ', kiloBytes == null ? '(unknown size)' : '(' + kiloBytes + ' KiB)', this.cache) - if (kiloBytes && kiloBytes > 1000) { + if (kiloBytes && kiloBytes > this.hardStorageSizeLimit) { console.warn('[CacheModule] storage cap exceeded (1 MB), clearing cache') await this.clearHardCache() } @@ -94,7 +103,7 @@ export class CacheModule { return storeHard ? this.cache : this.hotOnly } - public async deleteAllCacheKeyContains(search: string) { + public async deleteAllHardStorageCacheKeyContains(search: string) { if (!this.hardStorage) { return } @@ -137,21 +146,21 @@ export class CacheModule { } } - async clearCache() { + public async clearCache() { await this.clearHardCache() this.hotOnly.clear() } - async clearNetworkCache() { + public async clearNetworkCache() { if (this.hardStorage) { const network = await this.hardStorage.getNetworkPreferences() - this.deleteAllCacheKeyContains(network.key == 'main' ? '//beaconcha.in' : '//' + network.key) + this.deleteAllHardStorageCacheKeyContains(network.key == 'main' ? '//beaconcha.in' : '//' + network.key) } this.hotOnly.clear() } - async clearHardCache() { + public async clearHardCache() { if (this.hardStorage) { await this.hardStorage.setObject('cachemodule2_' + this.keyPrefix, null, false) this.setLastHardCacheWrite() @@ -207,7 +216,7 @@ export class CacheModule { this.cache[this.getKey(key)] = null } - invalidateAllCache() { + public invalidateAllCache() { this.cache = new Map() if (this.hardStorage) { this.hardStorage.setObject('cachemodule2_' + this.keyPrefix, null, false) diff --git a/src/app/utils/HighchartOptions.ts b/src/app/utils/HighchartOptions.ts index d3a9cd65..7482d61e 100644 --- a/src/app/utils/HighchartOptions.ts +++ b/src/app/utils/HighchartOptions.ts @@ -18,14 +18,14 @@ * // along with Beaconchain Dashboard. If not, see . */ -export function highChartOptions(where) { +export function highChartOptions(where, hostName) { where.setOptions({ time: { useUTC: false, }, credits: { enabled: true, - href: 'https://beaconcha.in', + href: 'https://' + hostName, text: '', style: { color: 'var(--body-color)', diff --git a/src/app/utils/MerchantUtils.ts b/src/app/utils/MerchantUtils.ts index 05a42ed4..fd8f4876 100644 --- a/src/app/utils/MerchantUtils.ts +++ b/src/app/utils/MerchantUtils.ts @@ -386,7 +386,7 @@ export class MerchantUtils { const currentProduct = this.findProduct(currentPlan) if (currentProduct == null) return 100 - const notMainnet = this.api.isNotMainnet() + const notMainnet = this.api.isNotEthereumMainnet() if (notMainnet) return currentProduct.maxTestnetValidators return currentProduct.maxValidators } @@ -395,7 +395,7 @@ export class MerchantUtils { const currentProduct = this.findProduct(MAX_PRODUCT) if (currentProduct == null) return 100 - const notMainnet = this.api.isNotMainnet() + const notMainnet = this.api.isNotEthereumMainnet() if (notMainnet) return currentProduct.maxTestnetValidators return currentProduct.maxValidators } diff --git a/src/app/utils/ValidatorUtils.ts b/src/app/utils/ValidatorUtils.ts index 1422185d..7dcae3e9 100644 --- a/src/app/utils/ValidatorUtils.ts +++ b/src/app/utils/ValidatorUtils.ts @@ -38,7 +38,6 @@ import { ETH1ValidatorResponse, SyncCommitteesStatisticsResponse, } from '../requests/requests' -import { CacheModule } from './CacheModule' import { MerchantUtils } from './MerchantUtils' import BigNumber from 'bignumber.js' import { UnitconvService } from '../services/unitconv.service' @@ -52,12 +51,6 @@ const KEYPREFIX = 'validators_' export const LAST_TIME_ADDED_KEY = 'last_time_added' export const LAST_TIME_REMOVED_KEY = 'last_time_removed' -const cachePerformanceKeyBare = 'performance' -const cacheAttestationKeyBare = 'attestationperformance' -const epochCachedKeyBare = 'epochcached' -const allMyKeyBare = 'allmy' -const cacheValidatorsKeyBare = 'validators_' - export enum ValidatorState { ACTIVE, OFFLINE, @@ -90,7 +83,7 @@ export interface Validator { @Injectable({ providedIn: 'root', }) -export class ValidatorUtils extends CacheModule { +export class ValidatorUtils { private listeners: (() => void)[] = [] private currentEpoch: EpochResponse @@ -103,9 +96,7 @@ export class ValidatorUtils extends CacheModule { private storage: StorageService, private merchantUtils: MerchantUtils, private unitConversion: UnitconvService - ) { - super('vu_') // initialize cache module with vu prefix - } + ) {} notifyListeners() { this.listeners.forEach((callback) => callback()) @@ -266,13 +257,6 @@ export class ValidatorUtils extends CacheModule { const validatorString = getValidatorQueryString([...local.values()], 2000, (await this.merchantUtils.getCurrentPlanMaxValidator()) - 1) - // TODO:: - /* const cached = await this.getMultipleCached(allMyKeyBare, validatorString.split(",")) - if (cached != null) { - console.log("return my validators from cache") - return cached - }*/ - const remoteUpdatesPromise = this.getDashboardDataValidators(SAVED, validatorString).catch((err) => { console.warn('error getAllMyValidators getDashboardDataValidators', err) return [] @@ -290,8 +274,6 @@ export class ValidatorUtils extends CacheModule { const result = [...local.values()] - this.cacheMultiple(allMyKeyBare, result) - return result } @@ -375,12 +357,10 @@ export class ValidatorUtils extends CacheModule { console.warn('error getDashboardDataValidators', response, result) return [] } - if (result.currentEpoch && result.currentEpoch.length > 0) { - this.currentEpoch = result.currentEpoch[0] - } - if (result.olderEpoch && result.olderEpoch.length > 0) { - this.olderEpoch = result.olderEpoch[0] - } + + this.currentEpoch = result.currentEpoch[0] + this.olderEpoch = result.olderEpoch[0] + if (result.rocketpool_network_stats && result.rocketpool_network_stats.length > 0) { this.rocketpoolStats = result.rocketpool_network_stats[0] } @@ -520,22 +500,6 @@ export class ValidatorUtils extends CacheModule { return Promise.reject(new Error('Response is invalid')) } - getCachedAttestationKey() { - return cacheAttestationKeyBare + this.api.getNetworkName() - } - - getCachedPerformanceKey() { - return cachePerformanceKeyBare + this.api.getNetworkName() - } - - getCachedValidatorKey() { - return cacheValidatorsKeyBare + this.api.getNetworkName() - } - - getCachedEpochKey() { - return epochCachedKeyBare + this.api.getNetworkName() - } - // single async convertToValidatorModelAndSaveValidatorLocal(synced: boolean, validator: ValidatorResponse) { await this.convertToValidatorModelsAndSaveLocal(synced, [validator]) @@ -546,7 +510,6 @@ export class ValidatorUtils extends CacheModule { await this.saveValidatorsLocal(this.convertToValidatorModel({ synced, storage: SAVED, validatorResponse: validator })) if (!synced) { await this.storage.setObject(LAST_TIME_ADDED_KEY, { timestamp: Date.now() } as StoredTimestamp) - await this.clearCache() } } From 3a2a93c1dafc9ec0f6dfc42403aa0be75fa176a8 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Tue, 24 Oct 2023 15:52:05 +0200 Subject: [PATCH 33/53] readd safety checks --- src/app/utils/ValidatorUtils.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/app/utils/ValidatorUtils.ts b/src/app/utils/ValidatorUtils.ts index 7dcae3e9..29dc263a 100644 --- a/src/app/utils/ValidatorUtils.ts +++ b/src/app/utils/ValidatorUtils.ts @@ -358,8 +358,16 @@ export class ValidatorUtils { return [] } - this.currentEpoch = result.currentEpoch[0] - this.olderEpoch = result.olderEpoch[0] + if (result.currentEpoch && result.currentEpoch.length > 0) { + this.currentEpoch = result.currentEpoch[0] + } else { + console.warn('no current epoch information!', result) + } + if (result.olderEpoch && result.olderEpoch.length > 0) { + this.olderEpoch = result.olderEpoch[0] + } else { + console.warn('no older epoch information!', result) + } if (result.rocketpool_network_stats && result.rocketpool_network_stats.length > 0) { this.rocketpoolStats = result.rocketpool_network_stats[0] @@ -375,7 +383,9 @@ export class ValidatorUtils { await this.storage.setLastEpochRequestTime(Date.now()) } else { const lastCachedTime = await this.storage.getLastEpochRequestTime() - this.currentEpoch.lastCachedTimestamp = lastCachedTime + if (this.currentEpoch) { + this.currentEpoch.lastCachedTimestamp = lastCachedTime + } } let local = null From 2f214cd22f3673c82d708e24215230cb84a42ce9 Mon Sep 17 00:00:00 2001 From: D13ce Date: Wed, 25 Oct 2023 08:58:12 +0200 Subject: [PATCH 34/53] Implement minor improvements (no behavior change) --- src/app/pages/dev/dev.page.ts | 2 +- src/app/utils/CacheModule.ts | 10 ++++++---- src/app/utils/ClientUpdateUtils.ts | 3 ++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/app/pages/dev/dev.page.ts b/src/app/pages/dev/dev.page.ts index 9e03fe82..68d2f767 100644 --- a/src/app/pages/dev/dev.page.ts +++ b/src/app/pages/dev/dev.page.ts @@ -39,7 +39,7 @@ export class DevPage extends Tab3Page implements OnInit { }) } else { Toast.show({ - text: 'Token refreshed failed :(', + text: 'Token refresh failed :(', }) } } diff --git a/src/app/utils/CacheModule.ts b/src/app/utils/CacheModule.ts index f46e6f7d..bdb96c32 100644 --- a/src/app/utils/CacheModule.ts +++ b/src/app/utils/CacheModule.ts @@ -21,6 +21,8 @@ import { StorageService, replacer } from '../services/storage.service' import { Validator } from './ValidatorUtils' +const LOGTAG = '[CacheModule]' + interface CachedData { maxStaleTime: number time: number @@ -67,7 +69,7 @@ export class CacheModule { // dont load hardStorage if last time it was written too is more than 6 hours ago const lastWrite = (await this.hardStorage.getObject('cachemodule2_' + this.keyPrefix + '_lastWrite')) as number if (lastWrite && lastWrite + 6 * 60 * 60 * 1000 < this.getTimestamp()) { - console.log('[CacheModule] hardStorage too old, ignoring') + console.log(LOGTAG + ' hardStorage too old, ignoring') } else { const result = (await this.hardStorage.getObject('cachemodule2_' + this.keyPrefix)) as Map if (result) { @@ -84,9 +86,9 @@ export class CacheModule { const size = new TextEncoder().encode(JSON.stringify(this.cache, replacer)).length kiloBytes = Math.round((size * 100) / 1024) / 100 } - console.log('[CacheModule] initialized with ', kiloBytes == null ? '(unknown size)' : '(' + kiloBytes + ' KiB)', this.cache) + console.log(LOGTAG + ' initialized with ', kiloBytes == null ? '(unknown size)' : '(' + kiloBytes + ' KiB)', this.cache) if (kiloBytes && kiloBytes > this.hardStorageSizeLimit) { - console.warn('[CacheModule] storage cap exceeded (1 MB), clearing cache') + console.warn(LOGTAG + ' storage cap exceeded (1 MB), clearing cache') await this.clearHardCache() } } catch (e) { @@ -187,7 +189,7 @@ export class CacheModule { protected cacheMultiple(prefix: string, data: Validator[]) { if (!data || data.length <= 0) { - console.log('[CacheModule] ignore cache attempt of empty data set', data) + console.log(LOGTAG + ' ignore cache attempt of empty data set', data) return } diff --git a/src/app/utils/ClientUpdateUtils.ts b/src/app/utils/ClientUpdateUtils.ts index 8336c0b0..7b11b20b 100644 --- a/src/app/utils/ClientUpdateUtils.ts +++ b/src/app/utils/ClientUpdateUtils.ts @@ -152,6 +152,7 @@ export default class ClientUpdateUtils { for (let i = 0; i < results.length; i++) { if (results[i] && !this.contains(results[i])) { changeFound = true + break } } @@ -164,7 +165,7 @@ export default class ClientUpdateUtils { } } } catch (error) { - console.error('An error occurred:', error) + console.error('An error occurred while checking for all updates:', error) } this.locked = false } From a7b38b49c904fc4ab09463eddb7c1c60a618e428 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Wed, 25 Oct 2023 12:54:52 +0200 Subject: [PATCH 35/53] added full el reward support --- .../dashboard/dashboard.component.html | 333 ++++++++++++------ .../dashboard/dashboard.component.scss | 13 +- .../dashboard/dashboard.component.ts | 23 +- src/app/controllers/OverviewController.ts | 72 ++-- .../validatordetail/validatordetail.page.ts | 10 +- src/app/services/api.service.ts | 2 +- src/app/services/unitconv.service.ts | 51 ++- src/app/tab-dashboard/tab-dashboard.page.ts | 23 +- src/app/utils/EthereumUnits.ts | 3 +- src/app/utils/ValidatorUtils.ts | 3 + 10 files changed, 375 insertions(+), 158 deletions(-) diff --git a/src/app/components/dashboard/dashboard.component.html b/src/app/components/dashboard/dashboard.component.html index 6fe7af03..8ce4507f 100644 --- a/src/app/components/dashboard/dashboard.component.html +++ b/src/app/components/dashboard/dashboard.component.html @@ -160,113 +160,149 @@ - + + + - Staking + Rewards + + + Combined + + + Consensus + + + Blocks + + -
+
- - {{ data.overallBalance | mcurrency : 'GWEI' : unit.pref.Cons }} + + {{ data.combinedPerformance.performance1d | mcurrency : 'GWEI' : unit.pref.Cons }}
- Balance + + Today +
-
+
- {{ data.apr }} % + + {{ data.combinedPerformance.performance7d | mcurrency : 'GWEI' : unit.pref.Cons }} +
- - APR + Last Week +
+
+ +
+
+ + {{ data.combinedPerformance.performance31d | mcurrency : 'GWEI' : unit.pref.Cons }} + +
+
+ Last Month +
+
+ +
+
+ + {{ data.combinedPerformance.performance365d | mcurrency : 'GWEI' : unit.pref.Cons }}
+
+ Last Year +
- - - {{ data.attrEffectiveness !== -1 ? data.attrEffectiveness + ' %' : 'NaN' }} - - - - - - + {{ data.combinedPerformance.apr }} %
- Effectiveness - + hideDelayAfterClick="5000" + >APR
-
+
- - {{ proposals.good }} / - {{ proposals.bad }} + + {{ data.combinedPerformance.total | mcurrency : 'GWEI' : unit.pref.Cons }}
- - - Proposals - - + Total
- +
+
@@ -307,6 +343,34 @@
+
+
+ + {{ data.consensusPerformance.performance365d | mcurrency : 'GWEI' : unit.pref.Cons }} + +
+
+ Last Year +
+
+ +
+
+ {{ data.consensusPerformance.apr }} % +
+
+ + APR + +
+
+
@@ -314,57 +378,98 @@
- Total + + Total +
- +
+
+
+ + {{ data.executionPerformance.performance1d | mcurrency : 'GWEI' : unit.pref.Exec }} + +
+
+ + Today + +
+
- +
+
+ + {{ data.executionPerformance.performance7d | mcurrency : 'GWEI' : unit.pref.Exec }} + +
+
+ Last Week +
+
- - Transaction Fees - +
+
+ + {{ data.executionPerformance.performance31d | mcurrency : 'GWEI' : unit.pref.Exec }} + +
+
+ Last Month +
+
- - -
-
-
- - {{ data.executionPerformance.performance31d | mcurrency : 'GWEI' : unit.pref.Exec }} - -
-
- - Last Month - -
-
- -
-
- {{ data.executionPerformance.apr }} % -
-
- - APR - -
+
+
+ + {{ data.executionPerformance.performance365d | mcurrency : 'GWEI' : unit.pref.Exec }} + +
+
+ Last year
- - + +
+
+ {{ data.executionPerformance.apr }} % +
+
+ + APR + +
+
+ +
+
+ + {{ data.executionPerformance.total | mcurrency : 'GWEI' : unit.pref.Exec }} + +
+
+ Total +
+
+
+ @@ -676,6 +781,38 @@
+
+ + Blocks + + + + + + Proposals + + + + {{ proposals.good }} / + {{ proposals.bad }} + + + + Luck + + {{ data.proposalLuckResponse.proposal_luck * 100 | number : '1.0-1' }}% + - + + + +
+
Sync Committees @@ -713,7 +850,7 @@ tooltip="Sync committees expected: {{ data.syncCommitteesStats.committeesExpected }}" trigger="click" hideDelayAfterClick="5000"> - {{ data.syncCommitteesStats.luck | number : '1.0-2' }}% + {{ data.syncCommitteesStats.luck | number : '1.0-1' }}% - @@ -813,7 +950,7 @@ - {{ data.effectiveBalance | mcurrency : 'GWEI' : 'ETHER' }} + {{ data.effectiveBalance | mcurrency : 'GWEI' : unit.getNetworkDefaultCurrency('cons') }} @@ -847,7 +984,7 @@ Effective Balance - {{ data.effectiveBalance | mcurrency : 'GWEI' : 'ETHER' }} + {{ data.effectiveBalance | mcurrency : 'GWEI' : unit.getNetworkDefaultCurrency('cons') }} diff --git a/src/app/components/dashboard/dashboard.component.scss b/src/app/components/dashboard/dashboard.component.scss index 535e41a6..8554954b 100644 --- a/src/app/components/dashboard/dashboard.component.scss +++ b/src/app/components/dashboard/dashboard.component.scss @@ -30,6 +30,10 @@ ion-segment-button { opacity: 1 !important; } +ion-segment { + background: var(--ion-background-color); +} + .segment-button-checked { color: var(--segment-primary) !important; font-weight: 600 !important; @@ -118,7 +122,11 @@ ion-segment-button { padding-bottom: 6px !important; } -ion-list { +ion-list-header { + padding-left: 0px; + text-align: center; + font-size: 1em; + font-weight: bold; } ion-item { @@ -149,9 +157,6 @@ ion-item { padding-bottom: 0px; } -.first-item-group { -} - .status-container { padding-bottom: 6px; } diff --git a/src/app/components/dashboard/dashboard.component.ts b/src/app/components/dashboard/dashboard.component.ts index d64fdf3e..2b31c77c 100644 --- a/src/app/components/dashboard/dashboard.component.ts +++ b/src/app/components/dashboard/dashboard.component.ts @@ -102,6 +102,8 @@ export class DashboardComponent implements OnInit { vacantMinipoolText = null showWithdrawalInfo = false + rewardTab: 'combined' | 'cons' | 'exec' = 'combined' + constructor( public unit: UnitconvService, public api: ApiService, @@ -261,8 +263,8 @@ export class DashboardComponent implements OnInit { if (!this.validatorUtils.rocketpoolStats || !this.validatorUtils.rocketpoolStats.effective_rpl_staked) return this.hasNonSmoothingPoolAsWell = this.data.rocketpool.hasNonSmoothingPoolAsWell this.displaySmoothingPool = this.data.rocketpool.smoothingPool - this.smoothingClaimed = this.data.rocketpool.smoothingPoolClaimed.dividedBy(new BigNumber('1e9')) - this.smoothingUnclaimed = this.data.rocketpool.smoothingPoolUnclaimed.dividedBy(new BigNumber('1e9')) + this.smoothingClaimed = this.data.rocketpool.smoothingPoolClaimed + this.smoothingUnclaimed = this.data.rocketpool.smoothingPoolUnclaimed this.unclaimedRpl = this.data.rocketpool.rplUnclaimed this.totalRplEarned = this.data.rocketpool.totalClaims.plus(this.data.rocketpool.rplUnclaimed) } catch (e) { @@ -720,27 +722,26 @@ export class DashboardComponent implements OnInit { let text = this.getChartToolTipCaption(tooltip.chart.hoverPoints[0].x, network.genesisTs, tooltip.chart.hoverPoints[0].dataGroup.length) // income - let lastCurrency = null - let consAndExecSameCurrency = true + const consAndExecSameCurrency = this.unit.hasSameCLAndELCurrency() let total = new BigNumber(0) for (let i = 0; i < tooltip.chart.hoverPoints.length; i++) { const type = tooltip.chart.hoverPoints[i].series.name == 'Execution' ? 'exec' : 'cons' - const currency = this.unit.getNetworkDefaultCurrency(type) - if (lastCurrency != null && lastCurrency != currency) { - consAndExecSameCurrency = false - } - lastCurrency = currency const value = new BigNumber(tooltip.chart.hoverPoints[i].y) text += ` ${ tooltip.chart.hoverPoints[i].series.name }: ${getValueString(value, type)}
` - total = total.plus(value) + + if (!consAndExecSameCurrency && type == 'exec') { + total = total.plus(this.unit.convertELtoCL(value)) + } else { + total = total.plus(value) + } } // add total if hovered point contains rewards for both EL and CL // only if both exec and cons currencies are the same - if (tooltip.chart.hoverPoints.length > 1 && consAndExecSameCurrency) { + if (tooltip.chart.hoverPoints.length > 1) { text += `Total: ${getValueString(total, 'cons')}` } diff --git a/src/app/controllers/OverviewController.ts b/src/app/controllers/OverviewController.ts index ffa64ac6..a88b77b7 100644 --- a/src/app/controllers/OverviewController.ts +++ b/src/app/controllers/OverviewController.ts @@ -18,12 +18,13 @@ * // along with Beaconchain Dashboard. If not, see . */ -import { EpochResponse, SyncCommitteeResponse, ValidatorResponse } from '../requests/requests' +import { EpochResponse, ProposalLuckResponse, SyncCommitteeResponse, ValidatorResponse } from '../requests/requests' import { sumBigInt, findHighest, findLowest } from '../utils/MathUtils' import BigNumber from 'bignumber.js' import { getValidatorQueryString, ValidatorState, Validator } from '../utils/ValidatorUtils' import { formatDate } from '@angular/common' import { SyncCommitteesStatistics, SyncCommitteesStatisticsResponse } from '../requests/requests' +import { UnitconvService } from '../services/unitconv.service' export type OverviewData = { overallBalance: BigNumber @@ -43,7 +44,7 @@ export type OverviewData = { worstTopPercentage: number displayAttrEffectiveness: boolean attrEffectiveness: number - + proposalLuckResponse: ProposalLuckResponse dashboardState: DashboardStatus lazyLoadChart: boolean lazyChartValidators: string @@ -57,6 +58,7 @@ export type OverviewData = { currentSyncCommittee: SyncCommitteeResponse nextSyncCommittee: SyncCommitteeResponse syncCommitteesStats: SyncCommitteesStatistics + proposalLuck: ProposalLuckResponse withdrawalsEnabledForAll: boolean } @@ -122,17 +124,28 @@ export type Description = { const VALIDATOR_32ETH = new BigNumber(32000000000) export default class OverviewController { - constructor(private refreshCallback: () => void = null, private userMaxValidators = 280) {} + constructor(private refreshCallback: () => void = null, private userMaxValidators = 280, private unit: UnitconvService = null) {} - public processDashboard(validators: Validator[], currentEpoch: EpochResponse, syncCommitteesStatsResponse = null) { - return this.process(validators, currentEpoch, false, syncCommitteesStatsResponse) + public processDashboard( + validators: Validator[], + currentEpoch: EpochResponse, + syncCommitteesStatsResponse = null, + proposalLuckResponse: ProposalLuckResponse = null + ) { + return this.process(validators, currentEpoch, false, syncCommitteesStatsResponse, proposalLuckResponse) } public processDetail(validators: Validator[], currentEpoch: EpochResponse) { - return this.process(validators, currentEpoch, true, null) + return this.process(validators, currentEpoch, true, null, null) } - private process(validators: Validator[], currentEpoch: EpochResponse, foreignValidator = false, syncCommitteesStatsResponse = null): OverviewData { + private process( + validators: Validator[], + currentEpoch: EpochResponse, + foreignValidator = false, + syncCommitteesStatsResponse = null, + proposalLuckResponse: ProposalLuckResponse = null + ): OverviewData { if (!validators || validators.length <= 0 || currentEpoch == null) return null const effectiveBalance = sumBigInt(validators, (cur) => cur.data.effectivebalance) @@ -165,13 +178,27 @@ export default class OverviewController { const executionPerf = this.getExecutionPerformance(validators, validatorDepositActive, aprPerformance31dExecution) + const smoothingPoolClaimed = this.sumRocketpoolSmoothingBigIntPerNodeAddress( + true, + validators, + (cur) => cur.rocketpool.claimed_smoothing_pool, + (cur) => cur.execshare + ).dividedBy(new BigNumber('1e9')) + + const smoothingPoolUnclaimed = this.sumRocketpoolSmoothingBigIntPerNodeAddress( + true, + validators, + (cur) => cur.rocketpool.unclaimed_smoothing_pool, + (cur) => cur.execshare + ).dividedBy(new BigNumber('1e9')) + const combinedPerf = { - performance1d: consensusPerf.performance1d.plus(executionPerf.performance1d), - performance31d: consensusPerf.performance31d.plus(executionPerf.performance31d), - performance7d: consensusPerf.performance7d.plus(executionPerf.performance7d), - performance365d: consensusPerf.performance365d.plus(executionPerf.performance365d), - apr: this.getAPRFromMonth(validatorDepositActive, aprPerformance31dExecution.plus(consensusPerf.performance31d)), - total: consensusPerf.total.plus(executionPerf.total), + performance1d: consensusPerf.performance1d.plus(this.unit.convertELtoCL(executionPerf.performance1d)), + performance31d: consensusPerf.performance31d.plus(this.unit.convertELtoCL(executionPerf.performance31d)), + performance7d: consensusPerf.performance7d.plus(this.unit.convertELtoCL(executionPerf.performance7d)), + performance365d: consensusPerf.performance365d.plus(this.unit.convertELtoCL(executionPerf.performance365d)), + apr: this.getAPRFromMonth(validatorDepositActive, this.unit.convertELtoCL(aprPerformance31dExecution).plus(consensusPerf.performance31d)), + total: consensusPerf.total.plus(this.unit.convertELtoCL(executionPerf.total)).plus(smoothingPoolClaimed).plus(smoothingPoolUnclaimed), } let attrEffectiveness = 0 @@ -242,10 +269,11 @@ export default class OverviewController { foreignValidatorWithdrawalCredsAre0x01: foreignWCAre0x01, effectiveBalance: effectiveBalance, currentEpoch: currentEpoch, - apr: consensusPerf.apr, + apr: combinedPerf.apr, currentSyncCommittee: currentSync ? currentSync.currentSyncCommittee : null, nextSyncCommittee: nextSync ? nextSync.nextSyncCommittee : null, syncCommitteesStats: this.calculateSyncCommitteeStats(syncCommitteesStatsResponse), + proposalLuckResponse: proposalLuckResponse, withdrawalsEnabledForAll: validators.filter((cur) => (cur.data.withdrawalcredentials.startsWith('0x01') ? true : false)).length == validatorCount, rocketpool: { @@ -276,18 +304,8 @@ export default class OverviewController { fee: feeAvg, status: foreignValidator && validators[0].rocketpool ? validators[0].rocketpool.minipool_status : null, depositType: foreignValidator && validators[0].rocketpool ? validators[0].rocketpool.minipool_deposit_type : null, - smoothingPoolClaimed: this.sumRocketpoolSmoothingBigIntPerNodeAddress( - true, - validators, - (cur) => cur.rocketpool.claimed_smoothing_pool, - (cur) => cur.execshare - ), - smoothingPoolUnclaimed: this.sumRocketpoolSmoothingBigIntPerNodeAddress( - true, - validators, - (cur) => cur.rocketpool.unclaimed_smoothing_pool, - (cur) => cur.execshare - ), + smoothingPoolClaimed: smoothingPoolClaimed, + smoothingPoolUnclaimed: smoothingPoolUnclaimed, rplUnclaimed: this.sumRocketpoolBigIntPerNodeAddress( true, validators, @@ -343,7 +361,7 @@ export default class OverviewController { performance7d: performance7d, performance365d: performance365d, apr: aprExecution, - total: total, + total: total, } } diff --git a/src/app/pages/validatordetail/validatordetail.page.ts b/src/app/pages/validatordetail/validatordetail.page.ts index 2ac101f8..01dc390e 100644 --- a/src/app/pages/validatordetail/validatordetail.page.ts +++ b/src/app/pages/validatordetail/validatordetail.page.ts @@ -24,6 +24,7 @@ import { ModalController } from '@ionic/angular' import OverviewController, { OverviewData } from '../../controllers/OverviewController' import { fromEvent, Subscription } from 'rxjs' import { MerchantUtils } from 'src/app/utils/MerchantUtils' +import { UnitconvService } from 'src/app/services/unitconv.service' @Component({ selector: 'app-validatordetail', @@ -44,7 +45,12 @@ export class ValidatordetailPage implements OnInit { scrolling = false - constructor(private validatorUtils: ValidatorUtils, private modalCtrl: ModalController, private merchant: MerchantUtils) {} + constructor( + private validatorUtils: ValidatorUtils, + private modalCtrl: ModalController, + private merchant: MerchantUtils, + private unit: UnitconvService + ) {} setInput(validator: Validator) { this.item = validator @@ -87,7 +93,7 @@ export class ValidatordetailPage implements OnInit { this.name = getDisplayName(item) const epoch = await this.validatorUtils.getRemoteCurrentEpoch() - const overviewController = new OverviewController(null, await this.merchant.getCurrentPlanMaxValidator()) + const overviewController = new OverviewController(null, await this.merchant.getCurrentPlanMaxValidator(), this.unit) this.data = overviewController.processDetail([item], epoch) } diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index b09bc3a7..4952cfb7 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -180,7 +180,7 @@ export class ApiService extends CacheModule { } isNotEthereumMainnet(): boolean { - const test = this.networkConfig.net != '' + const test = this.networkConfig.key != 'main' return test } diff --git a/src/app/services/unitconv.service.ts b/src/app/services/unitconv.service.ts index 192fbd9e..23936124 100644 --- a/src/app/services/unitconv.service.ts +++ b/src/app/services/unitconv.service.ts @@ -43,6 +43,8 @@ export class UnitconvService { public lastPrice: LastPrice private rplETHPrice: BigNumber = new BigNumber(1) + private mGNOXDAI = { value: 'GNOXDAI', type: 'cons', unit: Unit.DAI_GNO_HELPER } as Currency // dummy var to help us get the price of mGNO in xDAI + /* Users can quickly toggle between native currency and fiat currency by clicking on the value. This variable holds the previous setting (native or fiat) for each currency type (cons, exec, rpl. @@ -59,6 +61,7 @@ export class UnitconvService { this.lastPrice = { Cons: await this.getLastStoredPrice(this.pref.Cons), Exec: await this.getLastStoredPrice(this.pref.Exec), + mGNOXDAI: await this.getLastStoredPrice(this.mGNOXDAI), } as LastPrice this.pref.Cons = this.createCurrency(this.getPref(await this.loadStored(STORAGE_KEY_CONS), this.getNetworkDefaultCurrency('cons')), 'cons') @@ -77,6 +80,10 @@ export class UnitconvService { await this.init() } + public hasSameCLAndELCurrency() { + return this.getNetworkDefaultCurrency('cons') == this.getNetworkDefaultCurrency('exec') + } + public async changeCurrency(value: string) { UnitconvService.currencyPipe = { Cons: null, Exec: null, RPL: null } @@ -172,6 +179,13 @@ export class UnitconvService { return 'ETHER' } + public convertELtoCL(cl: BigNumber) { + if (this.hasSameCLAndELCurrency()) { + return cl + } + return cl.multipliedBy(this.lastPrice.mGNOXDAI) + } + public getFiatCurrency(type: RewardType) { if (type == 'cons') { if (this.isDefaultCurrency(this.pref.Cons)) { @@ -185,6 +199,12 @@ export class UnitconvService { } else { return this.pref.Exec.value } + } else if (type == 'rpl') { + if (this.isDefaultCurrency(this.pref.RPL)) { + return UnitconvService.currencyPipe.RPL + } else { + return this.pref.RPL.value + } } } @@ -221,12 +241,8 @@ export class UnitconvService { } private triggerPropertyChange() { - if (this.isDefaultCurrency(this.pref.Cons) || this.pref.Cons.value == 'mGNO') { - if (this.pref.Cons.value == 'mGNO') { - this.pref.Cons = this.createCurrency(this.pref.Cons.value, 'cons') - } else { - this.pref.Cons = this.createCurrency(this.getNetworkDefaultCurrency(this.pref.Cons), 'cons') - } + if (this.isDefaultCurrency(this.pref.Cons)) { + this.pref.Cons = this.createCurrency(this.getNetworkDefaultCurrency(this.pref.Cons), 'cons') this.pref.Exec = this.createCurrency(this.getNetworkDefaultCurrency(this.pref.Cons), 'exec') this.pref.RPL = this.createCurrency(this.getNetworkDefaultCurrency(this.pref.RPL), 'rpl') return @@ -276,7 +292,7 @@ export class UnitconvService { * (cons and exec could have different conversions so they cant use the same reference to unit) */ public convertNonFiat(value: BigNumber | number | string, from: string, to: string, displayable = true) { - if (MAPPING.get(to).coinbaseSpot != null && to != 'ETH' && to != 'ETHER' && from != to) { + if (MAPPING.get(to).coinbaseSpot != null && to != 'ETH' && to != 'ETHER' && to != this.getNetworkDefaultCurrency('cons') && from != to) { console.warn('convertNonFiat does not support fiat currencies. Use convert instead', value.toString(), from, to, displayable) } return this.convertBase(value, from, MAPPING.get(to), displayable) @@ -340,11 +356,13 @@ export class UnitconvService { } async updatePriceData() { + let skipFetchingMGNOtoDAIPrice = true const consPrice = await this.getPriceData(this.pref.Cons.unit) if (consPrice) { this.lastPrice.Cons = consPrice.multipliedBy(MAPPING.get(this.getNetworkDefaultCurrency(this.pref.Cons)).value) this.storage.setObject(this.getLastPriceKey(this.pref.Cons), { lastPrice: this.lastPrice.Cons } as LastPrice) } else { + skipFetchingMGNOtoDAIPrice = false this.lastPrice.Cons = this.pref.Cons.unit.value if (this.pref.Cons.value != 'mGNO') { this.pref.Cons.value = this.getNetworkDefaultCurrency(this.pref.Cons) @@ -356,10 +374,28 @@ export class UnitconvService { this.lastPrice.Exec = execPrice.multipliedBy(MAPPING.get(this.getNetworkDefaultCurrency(this.pref.Exec)).value) this.storage.setObject(this.getLastPriceKey(this.pref.Exec), { lastPrice: this.lastPrice.Exec } as LastPrice) } else { + skipFetchingMGNOtoDAIPrice = false this.lastPrice.Exec = this.pref.Exec.unit.value this.pref.Exec.value = this.getNetworkDefaultCurrency(this.pref.Exec) } + // Two ways to get the DAI <-> mGNO price + // First, simply ask coinbase for the price of mGNO in DAI. + // Alternatively to save API calls, we can also calculate the price of mGNO in DAI by dividing the price of mGNO in Fiat by the price of DAI in Fiat. + // For the second approach we need both fiat values though + if (!skipFetchingMGNOtoDAIPrice && this.api.isGnosis()) { + const daiToGno = await this.getPriceData(this.mGNOXDAI.unit) + if (daiToGno) { + this.lastPrice.mGNOXDAI = daiToGno.dividedBy(MAPPING.get(this.getNetworkDefaultCurrency(this.pref.Cons)).value) + this.storage.setObject(this.getLastPriceKey(this.mGNOXDAI), { lastPrice: this.lastPrice.mGNOXDAI } as LastPrice) + } else { + this.lastPrice.mGNOXDAI = this.mGNOXDAI.unit.value + } + } else { + // Keep in mind that both those values already contain any base unit convertion (fe mGNO to GNO) + this.lastPrice.mGNOXDAI = this.lastPrice.Exec.dividedBy(this.lastPrice.Cons) + } + this.triggerPropertyChange() } @@ -404,6 +440,7 @@ interface LastPrice { interface LastPrice { Cons: BigNumber Exec: BigNumber + mGNOXDAI: BigNumber } interface PreferredCurrency { diff --git a/src/app/tab-dashboard/tab-dashboard.page.ts b/src/app/tab-dashboard/tab-dashboard.page.ts index df7850cc..7155533e 100644 --- a/src/app/tab-dashboard/tab-dashboard.page.ts +++ b/src/app/tab-dashboard/tab-dashboard.page.ts @@ -120,13 +120,22 @@ export class Tab1Page { console.warn('error getRemoteCurrentEpoch', error) return null }) - const overviewController = new OverviewController(() => { - if (this.lastRefreshTs + 60 > this.getUnixSeconds()) return - this.api.invalidateCache() - this.refresh() - }, await this.merchant.getCurrentPlanMaxValidator()) - - this.overallData = overviewController.processDashboard(validators, epoch, this.validatorUtils.syncCommitteesStatsResponse) + const overviewController = new OverviewController( + () => { + if (this.lastRefreshTs + 60 > this.getUnixSeconds()) return + this.api.invalidateCache() + this.refresh() + }, + await this.merchant.getCurrentPlanMaxValidator(), + this.unitConv + ) + + this.overallData = overviewController.processDashboard( + validators, + epoch, + this.validatorUtils.syncCommitteesStatsResponse, + this.validatorUtils.proposalLuckResponse + ) this.lastRefreshTs = this.getUnixSeconds() } diff --git a/src/app/utils/EthereumUnits.ts b/src/app/utils/EthereumUnits.ts index acfc6c9c..bb7f051f 100644 --- a/src/app/utils/EthereumUnits.ts +++ b/src/app/utils/EthereumUnits.ts @@ -38,8 +38,9 @@ export default class Unit { public static RPL_NAKED = new Unit('RPL', new BigNumber('1'), 2) public static NO_CURRENCY = new Unit('', new BigNumber('1'), 0) public static RETH = new Unit('RETH', new BigNumber('1'), 2) + public static DAI_GNO_HELPER = new Unit('dummy', new BigNumber(1), 5, 'DAI-GNO') - public static XDAI = new Unit('xDAI', new BigNumber('1'), 3, 'XXX-DAI', 'xDAI') + public static XDAI = new Unit('DAI', new BigNumber('1'), 3, 'XXX-DAI', 'xDAI') public static GNO = new Unit('GNO', new BigNumber('0.03125'), 5, 'XXX-GNO', 'GNO') public static MGNO = new Unit('mGNO', new BigNumber('1'), 5, 'XXX-GNO', 'mGNO') diff --git a/src/app/utils/ValidatorUtils.ts b/src/app/utils/ValidatorUtils.ts index 29dc263a..a7b82881 100644 --- a/src/app/utils/ValidatorUtils.ts +++ b/src/app/utils/ValidatorUtils.ts @@ -37,6 +37,7 @@ import { SyncCommitteeResponse, ETH1ValidatorResponse, SyncCommitteesStatisticsResponse, + ProposalLuckResponse, } from '../requests/requests' import { MerchantUtils } from './MerchantUtils' import BigNumber from 'bignumber.js' @@ -90,6 +91,7 @@ export class ValidatorUtils { private olderEpoch: EpochResponse rocketpoolStats: RocketPoolNetworkStats syncCommitteesStatsResponse: SyncCommitteesStatisticsResponse + proposalLuckResponse: ProposalLuckResponse constructor( private api: ApiService, @@ -376,6 +378,7 @@ export class ValidatorUtils { const validatorsResponse = result.validators this.syncCommitteesStatsResponse = result.sync_committees_stats + this.proposalLuckResponse = result.proposal_luck_stats this.updateRplAndRethPrice() 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 36/53] 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 } From 78e9defdc80e1f119d1ffc0bc97ccaa5220fb37b Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Wed, 25 Oct 2023 14:52:36 +0200 Subject: [PATCH 37/53] styling --- .../components/dashboard/dashboard.component.scss | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/app/components/dashboard/dashboard.component.scss b/src/app/components/dashboard/dashboard.component.scss index 8554954b..e9c1ed57 100644 --- a/src/app/components/dashboard/dashboard.component.scss +++ b/src/app/components/dashboard/dashboard.component.scss @@ -25,8 +25,8 @@ ion-segment-button { text-transform: none !important; --ripple-color: transparent !important; //transition: color 0.15s ease; - font-size: 1.05em !important; - letter-spacing: normal !important; + font-family: 'Varela'; + font-size: 0.75em; opacity: 1 !important; } @@ -125,8 +125,13 @@ ion-segment { ion-list-header { padding-left: 0px; text-align: center; - font-size: 1em; - font-weight: bold; +} +ion-list-header ion-label { + font-size: 0.9em; + font-weight: 600 !important; + font-family: 'Varela'; + letter-spacing: 1.05px; + opacity: 1; } ion-item { From 105d9fa3919b763eb2047d143d06d929f09ef44b Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Wed, 25 Oct 2023 15:29:31 +0200 Subject: [PATCH 38/53] fix ios font size --- src/app/components/dashboard/dashboard.component.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/dashboard/dashboard.component.scss b/src/app/components/dashboard/dashboard.component.scss index e9c1ed57..88b4742b 100644 --- a/src/app/components/dashboard/dashboard.component.scss +++ b/src/app/components/dashboard/dashboard.component.scss @@ -127,7 +127,7 @@ ion-list-header { text-align: center; } ion-list-header ion-label { - font-size: 0.9em; + font-size: 0.9rem; font-weight: 600 !important; font-family: 'Varela'; letter-spacing: 1.05px; From 58269e695f19e2f609627862dafe0d03db738622 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Wed, 25 Oct 2023 15:59:15 +0200 Subject: [PATCH 39/53] typo --- src/app/components/dashboard/dashboard.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/dashboard/dashboard.component.html b/src/app/components/dashboard/dashboard.component.html index 8ce4507f..994999f1 100644 --- a/src/app/components/dashboard/dashboard.component.html +++ b/src/app/components/dashboard/dashboard.component.html @@ -437,7 +437,7 @@
- Last year + Last Year
From bb74ca7de680b150bf843f5c13296f463f4366fb Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:26:34 +0100 Subject: [PATCH 40/53] fix prices exchange rate 1 bug --- src/app/services/unitconv.service.ts | 37 ++++++++++++++++------------ 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/app/services/unitconv.service.ts b/src/app/services/unitconv.service.ts index 23936124..bf401254 100644 --- a/src/app/services/unitconv.service.ts +++ b/src/app/services/unitconv.service.ts @@ -357,26 +357,31 @@ export class UnitconvService { async updatePriceData() { let skipFetchingMGNOtoDAIPrice = true - const consPrice = await this.getPriceData(this.pref.Cons.unit) - if (consPrice) { - this.lastPrice.Cons = consPrice.multipliedBy(MAPPING.get(this.getNetworkDefaultCurrency(this.pref.Cons)).value) - this.storage.setObject(this.getLastPriceKey(this.pref.Cons), { lastPrice: this.lastPrice.Cons } as LastPrice) - } else { - skipFetchingMGNOtoDAIPrice = false - this.lastPrice.Cons = this.pref.Cons.unit.value - if (this.pref.Cons.value != 'mGNO') { - this.pref.Cons.value = this.getNetworkDefaultCurrency(this.pref.Cons) + + if (!this.isDefaultCurrency(this.pref.Cons)) { + const consPrice = await this.getPriceData(this.pref.Cons.unit) + if (consPrice) { + this.lastPrice.Cons = consPrice.multipliedBy(MAPPING.get(this.getNetworkDefaultCurrency(this.pref.Cons)).value) + this.storage.setObject(this.getLastPriceKey(this.pref.Cons), { lastPrice: this.lastPrice.Cons } as LastPrice) + } else { + skipFetchingMGNOtoDAIPrice = false + this.lastPrice.Cons = this.pref.Cons.unit.value + if (this.pref.Cons.value != 'mGNO') { + this.pref.Cons.value = this.getNetworkDefaultCurrency(this.pref.Cons) + } } - } - const execPrice = await this.getPriceData(this.pref.Exec.unit) - if (execPrice) { - this.lastPrice.Exec = execPrice.multipliedBy(MAPPING.get(this.getNetworkDefaultCurrency(this.pref.Exec)).value) - this.storage.setObject(this.getLastPriceKey(this.pref.Exec), { lastPrice: this.lastPrice.Exec } as LastPrice) + const execPrice = await this.getPriceData(this.pref.Exec.unit) + if (execPrice) { + this.lastPrice.Exec = execPrice.multipliedBy(MAPPING.get(this.getNetworkDefaultCurrency(this.pref.Exec)).value) + this.storage.setObject(this.getLastPriceKey(this.pref.Exec), { lastPrice: this.lastPrice.Exec } as LastPrice) + } else { + skipFetchingMGNOtoDAIPrice = false + this.lastPrice.Exec = this.pref.Exec.unit.value + this.pref.Exec.value = this.getNetworkDefaultCurrency(this.pref.Exec) + } } else { skipFetchingMGNOtoDAIPrice = false - this.lastPrice.Exec = this.pref.Exec.unit.value - this.pref.Exec.value = this.getNetworkDefaultCurrency(this.pref.Exec) } // Two ways to get the DAI <-> mGNO price From 7e5d72909cf3ae201910231490535a1bb6493fe1 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:26:57 +0100 Subject: [PATCH 41/53] remove yearly rewards since these are not reliable --- .../dashboard/dashboard.component.html | 77 ++++++------------- 1 file changed, 22 insertions(+), 55 deletions(-) diff --git a/src/app/components/dashboard/dashboard.component.html b/src/app/components/dashboard/dashboard.component.html index 994999f1..18b6ff84 100644 --- a/src/app/components/dashboard/dashboard.component.html +++ b/src/app/components/dashboard/dashboard.component.html @@ -248,7 +248,7 @@
- Last Week + Last 7d
@@ -259,18 +259,18 @@
- Last Month + Last 31d
- - {{ data.combinedPerformance.performance365d | mcurrency : 'GWEI' : unit.pref.Cons }} + + {{ data.combinedPerformance.total | mcurrency : 'GWEI' : unit.pref.Cons }}
- Last Year + Total
@@ -290,17 +290,6 @@
- -
-
- - {{ data.combinedPerformance.total | mcurrency : 'GWEI' : unit.pref.Cons }} - -
-
- Total -
-
@@ -328,7 +317,7 @@
- Last Week + Last 7d
@@ -339,18 +328,25 @@
- Last Month + Last 31d
- - {{ data.consensusPerformance.performance365d | mcurrency : 'GWEI' : unit.pref.Cons }} + + {{ data.consensusPerformance.total | mcurrency : 'GWEI' : unit.pref.Cons }}
- Last Year + + Total +
@@ -370,24 +366,6 @@
- -
-
- - {{ data.consensusPerformance.total | mcurrency : 'GWEI' : unit.pref.Cons }} - -
-
- - Total - -
-
@@ -415,7 +393,7 @@
- Last Week + Last 7d
@@ -426,18 +404,18 @@
- Last Month + Last 31d
- - {{ data.executionPerformance.performance365d | mcurrency : 'GWEI' : unit.pref.Exec }} + + {{ data.executionPerformance.total | mcurrency : 'GWEI' : unit.pref.Exec }}
- Last Year + Total
@@ -457,17 +435,6 @@
- -
-
- - {{ data.executionPerformance.total | mcurrency : 'GWEI' : unit.pref.Exec }} - -
-
- Total -
-
From f96f10bddcb148b44dbc929c8ef8369c2c58f652 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:49:29 +0100 Subject: [PATCH 42/53] fix wrong apr when using stake share --- src/app/controllers/OverviewController.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/controllers/OverviewController.ts b/src/app/controllers/OverviewController.ts index a88b77b7..2ac0f95b 100644 --- a/src/app/controllers/OverviewController.ts +++ b/src/app/controllers/OverviewController.ts @@ -151,7 +151,7 @@ export default class OverviewController { const effectiveBalance = sumBigInt(validators, (cur) => cur.data.effectivebalance) const validatorDepositActive = sumBigInt(validators, (cur) => { if (cur.data.activationepoch <= currentEpoch.epoch) { - if (!cur.rocketpool || !cur.rocketpool.node_address) return VALIDATOR_32ETH + if (!cur.rocketpool || !cur.rocketpool.node_address) return VALIDATOR_32ETH.multipliedBy(new BigNumber(cur.share == null ? 1 : cur.share)) let nodeDeposit if (cur.rocketpool.node_deposit_balance) { @@ -159,7 +159,7 @@ export default class OverviewController { } else { nodeDeposit = VALIDATOR_32ETH.dividedBy(new BigNumber(2)) } - return nodeDeposit + return nodeDeposit.multipliedBy(new BigNumber(cur.share == null ? 1 : cur.share)) } else { return new BigNumber(0) } From beb653097d99f32278e8c80ccc5c246904d97a85 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Tue, 31 Oct 2023 11:54:43 +0100 Subject: [PATCH 43/53] addressed awesome feedback provided --- .../dashboard/dashboard.component.html | 2 +- .../dashboard/dashboard.component.ts | 37 ++++++++++++------- src/app/models/StorageTypes.ts | 2 + src/app/requests/requests.ts | 1 + src/app/services/unitconv.service.ts | 35 +++++++++++++++--- .../tab-validators/tab-validators.page.html | 2 +- src/app/utils/EthereumUnits.ts | 2 +- src/app/utils/NetworkData.ts | 14 +++++++ 8 files changed, 74 insertions(+), 21 deletions(-) diff --git a/src/app/components/dashboard/dashboard.component.html b/src/app/components/dashboard/dashboard.component.html index 18b6ff84..76639765 100644 --- a/src/app/components/dashboard/dashboard.component.html +++ b/src/app/components/dashboard/dashboard.component.html @@ -232,7 +232,7 @@
Today { - const slot = Math.floor((ts / 1000 - genesisTs) / 12) - const epoch = Math.floor(slot / 32) + const slot = Math.floor((ts / 1000 - genesisTs) / slotTime) + const epoch = Math.floor(slot / slotsPerEpoch) return Math.max(0, epoch) } @@ -590,7 +590,13 @@ export class DashboardComponent implements OnInit { shared: true, formatter: (tooltip) => { // date and epoch - let text = this.getChartToolTipCaption(tooltip.chart.hoverPoints[0].x, network.genesisTs, tooltip.chart.hoverPoints[0].dataGroup.length) + let text = this.getChartToolTipCaption( + tooltip.chart.hoverPoints[0].x, + network.genesisTs, + network.slotsTime, + network.slotPerEpoch, + tooltip.chart.hoverPoints[0].dataGroup.length + ) // summary for (let i = 0; i < tooltip.chart.hoverPoints.length; i++) { @@ -663,14 +669,17 @@ export class DashboardComponent implements OnInit { const network = this.api.getNetwork() const getValueString = (value: BigNumber, type: RewardType): string => { - let text = `${value.toFixed(5)} ` + this.unit.getNetworkDefaultUnit(type).display + let text if (type == 'cons') { + text = `${value.toFixed(5)} ` + this.unit.getNetworkDefaultUnit(type).display if (!this.unit.isDefaultCurrency(this.unit.pref.Cons)) { text += ` (${this.unit.convertToPref(value, this.unit.getNetworkDefaultCurrency(type), type)})` } } else if (type == 'exec') { + // Gnosis: All values provided by the API are in the CL currency, including the el rewards + text = `${this.unit.convertCLtoEL(value).toFixed(5)} ` + this.unit.getNetworkDefaultUnit(type).display if (!this.unit.isDefaultCurrency(this.unit.pref.Exec)) { - text += ` (${this.unit.convertToPref(value, this.unit.getNetworkDefaultCurrency(type), type)})` + text += ` (${this.unit.convertToPref(this.unit.convertCLtoEL(value), this.unit.getNetworkDefaultCurrency(type), type)})` } } @@ -719,10 +728,16 @@ export class DashboardComponent implements OnInit { shared: true, formatter: (tooltip) => { // date and epoch - let text = this.getChartToolTipCaption(tooltip.chart.hoverPoints[0].x, network.genesisTs, tooltip.chart.hoverPoints[0].dataGroup.length) + let text = this.getChartToolTipCaption( + tooltip.chart.hoverPoints[0].x, + network.genesisTs, + network.slotsTime, + network.slotPerEpoch, + tooltip.chart.hoverPoints[0].dataGroup.length + ) // income - const consAndExecSameCurrency = this.unit.hasSameCLAndELCurrency() + // Gnosis: All values provided by the API are in the CL currency, including the el rewards which is why we can simply add them for total let total = new BigNumber(0) for (let i = 0; i < tooltip.chart.hoverPoints.length; i++) { const type = tooltip.chart.hoverPoints[i].series.name == 'Execution' ? 'exec' : 'cons' @@ -732,11 +747,7 @@ export class DashboardComponent implements OnInit { tooltip.chart.hoverPoints[i].series.name }: ${getValueString(value, type)}
` - if (!consAndExecSameCurrency && type == 'exec') { - total = total.plus(this.unit.convertELtoCL(value)) - } else { - total = total.plus(value) - } + total = total.plus(value) } // add total if hovered point contains rewards for both EL and CL diff --git a/src/app/models/StorageTypes.ts b/src/app/models/StorageTypes.ts index aee550ea..d9d16c35 100644 --- a/src/app/models/StorageTypes.ts +++ b/src/app/models/StorageTypes.ts @@ -36,6 +36,8 @@ export interface ApiNetwork { genesisTs: number elCurrency: NetworkMainCurrency clCurrency: NetworkMainCurrency + slotPerEpoch: number + slotsTime: number name: string } diff --git a/src/app/requests/requests.ts b/src/app/requests/requests.ts index e06b5b83..b25571d5 100644 --- a/src/app/requests/requests.ts +++ b/src/app/requests/requests.ts @@ -376,6 +376,7 @@ export class ValidatorViaWithdrawalAddress extends APIRequest diff --git a/src/app/utils/EthereumUnits.ts b/src/app/utils/EthereumUnits.ts index bb7f051f..bc0092ac 100644 --- a/src/app/utils/EthereumUnits.ts +++ b/src/app/utils/EthereumUnits.ts @@ -40,7 +40,7 @@ export default class Unit { public static RETH = new Unit('RETH', new BigNumber('1'), 2) public static DAI_GNO_HELPER = new Unit('dummy', new BigNumber(1), 5, 'DAI-GNO') - public static XDAI = new Unit('DAI', new BigNumber('1'), 3, 'XXX-DAI', 'xDAI') + public static XDAI = new Unit('DAI', new BigNumber('1'), 4, 'XXX-DAI', 'xDAI') public static GNO = new Unit('GNO', new BigNumber('0.03125'), 5, 'XXX-GNO', 'GNO') public static MGNO = new Unit('mGNO', new BigNumber('1'), 5, 'XXX-GNO', 'mGNO') diff --git a/src/app/utils/NetworkData.ts b/src/app/utils/NetworkData.ts index 58873f34..a4f347cf 100644 --- a/src/app/utils/NetworkData.ts +++ b/src/app/utils/NetworkData.ts @@ -33,6 +33,8 @@ export const MAP: ApiNetwork[] = [ genesisTs: 1606824023, clCurrency: NetworkMainCurrency.ETH, elCurrency: NetworkMainCurrency.ETH, + slotPerEpoch: 32, + slotsTime: 12, name: 'Ethereum', }, { @@ -47,6 +49,8 @@ export const MAP: ApiNetwork[] = [ genesisTs: 1638993340, clCurrency: NetworkMainCurrency.GNO, elCurrency: NetworkMainCurrency.xDAI, + slotPerEpoch: 16, + slotsTime: 5, name: 'Gnosis', }, { @@ -61,6 +65,8 @@ export const MAP: ApiNetwork[] = [ genesisTs: 1616508000, clCurrency: NetworkMainCurrency.ETH, elCurrency: NetworkMainCurrency.ETH, + slotPerEpoch: 32, + slotsTime: 12, name: 'Ethereum', }, { @@ -75,6 +81,8 @@ export const MAP: ApiNetwork[] = [ genesisTs: 1655733600, clCurrency: NetworkMainCurrency.ETH, elCurrency: NetworkMainCurrency.ETH, + slotPerEpoch: 32, + slotsTime: 12, name: 'Ethereum', }, { @@ -89,6 +97,8 @@ export const MAP: ApiNetwork[] = [ genesisTs: 1695902400, clCurrency: NetworkMainCurrency.ETH, elCurrency: NetworkMainCurrency.ETH, + slotPerEpoch: 32, + slotsTime: 12, name: 'Ethereum', }, { @@ -103,6 +113,8 @@ export const MAP: ApiNetwork[] = [ genesisTs: 1606824023, clCurrency: NetworkMainCurrency.ETH, elCurrency: NetworkMainCurrency.ETH, + slotPerEpoch: 32, + slotsTime: 12, name: 'Ethereum', }, { @@ -117,6 +129,8 @@ export const MAP: ApiNetwork[] = [ genesisTs: 1606824023, clCurrency: NetworkMainCurrency.ETH, elCurrency: NetworkMainCurrency.ETH, + slotPerEpoch: 32, + slotsTime: 12, name: 'Ethereum', }, ] From 2e28d0d39a084cb57ebc4a6f71f016307ff1c541 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 31 Oct 2023 13:16:48 +0100 Subject: [PATCH 44/53] fix ios prices, fix ios segment button opacity --- .../dashboard/dashboard.component.scss | 11 ++++++++-- src/app/utils/MerchantUtils.ts | 22 ++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/app/components/dashboard/dashboard.component.scss b/src/app/components/dashboard/dashboard.component.scss index 88b4742b..b4c93f60 100644 --- a/src/app/components/dashboard/dashboard.component.scss +++ b/src/app/components/dashboard/dashboard.component.scss @@ -27,7 +27,11 @@ ion-segment-button { //transition: color 0.15s ease; font-family: 'Varela'; font-size: 0.75em; - opacity: 1 !important; + --background-focused-opacity: 1; +} + +ion-segment-button.md::part(native) { + opacity: 0.7; } ion-segment { @@ -39,6 +43,10 @@ ion-segment { font-weight: 600 !important; } +.segment-button-checked.md::part(native) { + opacity: 1 !important; +} + .status ion-icon { width: 100%; display: flex; @@ -131,7 +139,6 @@ ion-list-header ion-label { font-weight: 600 !important; font-family: 'Varela'; letter-spacing: 1.05px; - opacity: 1; } ion-item { diff --git a/src/app/utils/MerchantUtils.ts b/src/app/utils/MerchantUtils.ts index fd8f4876..e60257ae 100644 --- a/src/app/utils/MerchantUtils.ts +++ b/src/app/utils/MerchantUtils.ts @@ -102,8 +102,12 @@ export class MerchantUtils { return } + this.init() + } + + private async init() { try { - this.initProducts() + await this.initProducts() this.initCustomValidator() this.setupListeners() } catch (e) { @@ -174,7 +178,7 @@ export class MerchantUtils { return result } - private initProducts() { + private async initProducts() { let platform = CdvPurchase.Platform.GOOGLE_PLAY if (this.platform.is('ios')) { platform = CdvPurchase.Platform.APPLE_APPSTORE @@ -189,7 +193,16 @@ export class MerchantUtils { } } - CdvPurchase.store.initialize() + await CdvPurchase.store.initialize() + for (let i = 0; i < CdvPurchase.store.products.length; i++) { + const lastIndex = CdvPurchase.store.products[i].offers[0].pricingPhases.length - 1 + if (lastIndex < 0) { + console.warn('no pricingphases found', CdvPurchase.store.products[i]) + continue + } + + this.updatePrice(CdvPurchase.store.products[i].id, CdvPurchase.store.products[i].offers[0].pricingPhases[lastIndex].price) + } } private updatePrice(id, price) { @@ -204,9 +217,6 @@ export class MerchantUtils { // General query to all products CdvPurchase.store .when() - .productUpdated((p: CdvPurchase.Product) => { - this.updatePrice(p.id, p.pricing.price) - }) .approved((p: CdvPurchase.Transaction) => { // Handle the product deliverable this.currentPlan = p.products[0].id From c806fdc7d3b33d757e539da3a0256faf14640103 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 31 Oct 2023 13:31:49 +0100 Subject: [PATCH 45/53] update cocoapods --- ios/App/Podfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/App/Podfile.lock b/ios/App/Podfile.lock index 0b74c70b..4a86b7bf 100644 --- a/ios/App/Podfile.lock +++ b/ios/App/Podfile.lock @@ -196,4 +196,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 0abccb83d0d503da1628c7d1ca5579739161bb33 -COCOAPODS: 1.10.2 +COCOAPODS: 1.14.2 From 9e512ff2b3a03c80cf8e1f47d4d2a0bebfbc8e03 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 31 Oct 2023 13:34:15 +0100 Subject: [PATCH 46/53] ios++ --- ios/App/App.xcodeproj/project.pbxproj | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index 8af810dd..ac03c77e 100644 --- a/ios/App/App.xcodeproj/project.pbxproj +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -500,17 +500,17 @@ CODE_SIGN_ENTITLEMENTS = App/App.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 103; DEVELOPMENT_TEAM = 3HYX3N9WTV; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 3.5.2; + MARKETING_VERSION = 4.5.0; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; PRODUCT_BUNDLE_IDENTIFIER = in.beaconcha.mobile; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OBJC_BRIDGING_HEADER = "App/App-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -528,12 +528,12 @@ CODE_SIGN_ENTITLEMENTS = App/AppRelease.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 103; DEVELOPMENT_TEAM = 3HYX3N9WTV; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 3.5.2; + MARKETING_VERSION = 4.5.0; PRODUCT_BUNDLE_IDENTIFIER = in.beaconcha.mobile; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -556,12 +556,12 @@ CODE_SIGN_ENTITLEMENTS = "Beaconchain WidgetExtension.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 103; DEVELOPMENT_TEAM = 3HYX3N9WTV; INFOPLIST_FILE = "Beaconchain Widget/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.5.2; + MARKETING_VERSION = 4.5.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "in.beaconcha.mobile.Beaconchain-Widget"; @@ -584,12 +584,12 @@ CODE_SIGN_ENTITLEMENTS = "Beaconchain WidgetExtension.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 75; + CURRENT_PROJECT_VERSION = 103; DEVELOPMENT_TEAM = 3HYX3N9WTV; INFOPLIST_FILE = "Beaconchain Widget/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; - MARKETING_VERSION = 3.5.2; + MARKETING_VERSION = 4.5.0; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "in.beaconcha.mobile.Beaconchain-Widget"; PRODUCT_NAME = "$(TARGET_NAME)"; From 38712ccf0c6cfe2dc2f12bc95a7c3d6dd0f9dd37 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Tue, 31 Oct 2023 13:45:33 +0100 Subject: [PATCH 47/53] fix scroll ion-segment-button opacity --- src/app/components/dashboard/dashboard.component.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/components/dashboard/dashboard.component.scss b/src/app/components/dashboard/dashboard.component.scss index b4c93f60..65cced08 100644 --- a/src/app/components/dashboard/dashboard.component.scss +++ b/src/app/components/dashboard/dashboard.component.scss @@ -28,6 +28,7 @@ ion-segment-button { font-family: 'Varela'; font-size: 0.75em; --background-focused-opacity: 1; + opacity: 1 !important; } ion-segment-button.md::part(native) { From da29d194b0fbbe43446e37aa4d60c00cf7327ffc Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Tue, 31 Oct 2023 14:12:53 +0100 Subject: [PATCH 48/53] network switch currency bugfix(?) --- src/app/services/unitconv.service.ts | 1 + src/app/tab-preferences/tab-preferences.page.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/app/services/unitconv.service.ts b/src/app/services/unitconv.service.ts index 6824ab1e..f2ac0aa8 100644 --- a/src/app/services/unitconv.service.ts +++ b/src/app/services/unitconv.service.ts @@ -77,6 +77,7 @@ export class UnitconvService { } public async networkSwitchReload() { + UnitconvService.currencyPipe = { Cons: null, Exec: null, RPL: null } await this.init() } diff --git a/src/app/tab-preferences/tab-preferences.page.ts b/src/app/tab-preferences/tab-preferences.page.ts index 5feb7dd5..47450e48 100644 --- a/src/app/tab-preferences/tab-preferences.page.ts +++ b/src/app/tab-preferences/tab-preferences.page.ts @@ -330,6 +330,7 @@ export class Tab3Page { this.merchant, false ) + this.currentFiatCurrency = await this.unit.getCurrentConsFiat() } async openIconCredit() { From 428fd5e6a5facbea481bbece9f02cf4c959f3da7 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Tue, 31 Oct 2023 15:00:55 +0100 Subject: [PATCH 49/53] fix migration to 3.2 --- src/app/utils/ValidatorUtils.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/app/utils/ValidatorUtils.ts b/src/app/utils/ValidatorUtils.ts index aa20b94d..eac75edb 100644 --- a/src/app/utils/ValidatorUtils.ts +++ b/src/app/utils/ValidatorUtils.ts @@ -124,10 +124,14 @@ export class ValidatorUtils { async migrateTo3Dot2() { const share = await this.storage.getStakingShare() const valis = await this.getAllValidatorsLocal() - for (let i = 0; i < valis.length; i++) { - valis[i].share = share.toNumber() + if (valis) { + for (let i = 0; i < valis.length; i++) { + if (share) { + valis[i].share = share.toNumber() + } + } + this.saveValidatorsLocal(valis) } - this.saveValidatorsLocal(valis) } public async getMap(storageKey: string): Promise> { From 8a8e9de0dc5986533a7ee40c60f3ed359d676ee0 Mon Sep 17 00:00:00 2001 From: Manuel <5877862+manuelsc@users.noreply.github.com> Date: Tue, 31 Oct 2023 15:03:04 +0100 Subject: [PATCH 50/53] fix typo --- src/app/components/dashboard/dashboard.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/components/dashboard/dashboard.component.html b/src/app/components/dashboard/dashboard.component.html index 76639765..994e9d49 100644 --- a/src/app/components/dashboard/dashboard.component.html +++ b/src/app/components/dashboard/dashboard.component.html @@ -232,7 +232,7 @@
Today Date: Tue, 31 Oct 2023 15:06:40 +0100 Subject: [PATCH 51/53] fix typo --- src/app/components/dashboard/dashboard.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/components/dashboard/dashboard.component.html b/src/app/components/dashboard/dashboard.component.html index 994e9d49..84b81852 100644 --- a/src/app/components/dashboard/dashboard.component.html +++ b/src/app/components/dashboard/dashboard.component.html @@ -301,7 +301,7 @@
Today Today Date: Fri, 3 Nov 2023 08:04:33 +0100 Subject: [PATCH 52/53] replace hardcoded slots and epoch data with config --- android/app/build.gradle | 2 +- .../dashboard/dashboard.component.ts | 2 +- src/app/controllers/OverviewController.ts | 71 +++++++++++-------- src/app/models/StorageTypes.ts | 1 + .../validatordetail/validatordetail.page.ts | 6 +- src/app/tab-dashboard/tab-dashboard.page.ts | 3 +- src/app/utils/BlockUtils.ts | 2 +- src/app/utils/NetworkData.ts | 7 ++ 8 files changed, 60 insertions(+), 34 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 23aa535b..ee949e5f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -8,7 +8,7 @@ android { namespace "in.beaconcha.mobile" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 105 + versionCode 107 versionName "4.5.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/src/app/components/dashboard/dashboard.component.ts b/src/app/components/dashboard/dashboard.component.ts index 57dd3cdc..c1a94a10 100644 --- a/src/app/components/dashboard/dashboard.component.ts +++ b/src/app/components/dashboard/dashboard.component.ts @@ -217,7 +217,7 @@ export class DashboardComponent implements OnInit { epochToTimestamp(epoch: number) { const network = this.api.getNetwork() - return (network.genesisTs + epoch * 32 * 12) * 1000 + return (network.genesisTs + epoch * network.slotPerEpoch * network.slotsTime) * 1000 } updateActiveSyncCommitteeMessage(committee: SyncCommitteeResponse) { diff --git a/src/app/controllers/OverviewController.ts b/src/app/controllers/OverviewController.ts index 2ac0f95b..ccbea2a7 100644 --- a/src/app/controllers/OverviewController.ts +++ b/src/app/controllers/OverviewController.ts @@ -25,6 +25,7 @@ import { getValidatorQueryString, ValidatorState, Validator } from '../utils/Val import { formatDate } from '@angular/common' import { SyncCommitteesStatistics, SyncCommitteesStatisticsResponse } from '../requests/requests' import { UnitconvService } from '../services/unitconv.service' +import { ApiNetwork } from '../models/StorageTypes' export type OverviewData = { overallBalance: BigNumber @@ -130,13 +131,14 @@ export default class OverviewController { validators: Validator[], currentEpoch: EpochResponse, syncCommitteesStatsResponse = null, - proposalLuckResponse: ProposalLuckResponse = null + proposalLuckResponse: ProposalLuckResponse = null, + network: ApiNetwork ) { - return this.process(validators, currentEpoch, false, syncCommitteesStatsResponse, proposalLuckResponse) + return this.process(validators, currentEpoch, false, syncCommitteesStatsResponse, proposalLuckResponse, network) } - public processDetail(validators: Validator[], currentEpoch: EpochResponse) { - return this.process(validators, currentEpoch, true, null, null) + public processDetail(validators: Validator[], currentEpoch: EpochResponse, network: ApiNetwork) { + return this.process(validators, currentEpoch, true, null, null, network) } private process( @@ -144,7 +146,8 @@ export default class OverviewController { currentEpoch: EpochResponse, foreignValidator = false, syncCommitteesStatsResponse = null, - proposalLuckResponse: ProposalLuckResponse = null + proposalLuckResponse: ProposalLuckResponse = null, + network: ApiNetwork ): OverviewData { if (!validators || validators.length <= 0 || currentEpoch == null) return null @@ -261,7 +264,7 @@ export default class OverviewController { consensusPerformance: consensusPerf, executionPerformance: executionPerf, combinedPerformance: combinedPerf, - dashboardState: this.getDashboardState(validators, currentEpoch, foreignValidator), + dashboardState: this.getDashboardState(validators, currentEpoch, foreignValidator, network), lazyLoadChart: true, lazyChartValidators: getValidatorQueryString(validators, 2000, this.userMaxValidators - 1), foreignValidator: foreignValidator, @@ -272,7 +275,7 @@ export default class OverviewController { apr: combinedPerf.apr, currentSyncCommittee: currentSync ? currentSync.currentSyncCommittee : null, nextSyncCommittee: nextSync ? nextSync.nextSyncCommittee : null, - syncCommitteesStats: this.calculateSyncCommitteeStats(syncCommitteesStatsResponse), + syncCommitteesStats: this.calculateSyncCommitteeStats(syncCommitteesStatsResponse, network), proposalLuckResponse: proposalLuckResponse, withdrawalsEnabledForAll: validators.filter((cur) => (cur.data.withdrawalcredentials.startsWith('0x01') ? true : false)).length == validatorCount, @@ -567,7 +570,7 @@ export default class OverviewController { return new BigNumber(performance.toString()).multipliedBy('1177').dividedBy(validatorDepositActive).decimalPlaces(1).toNumber() } - private getDashboardState(validators: Validator[], currentEpoch: EpochResponse, foreignValidator): DashboardStatus { + private getDashboardState(validators: Validator[], currentEpoch: EpochResponse, foreignValidator, network: ApiNetwork): DashboardStatus { // collect data const validatorCount = validators.length const activeValidators = this.getActiveValidators(validators) @@ -597,7 +600,7 @@ export default class OverviewController { // The first one that matches will set the icon and its css as well as, if only one state matches, the extendedDescription // handle slashed validators - this.updateStateSlashed(dashboardStatus, slashedCount, validatorCount, foreignValidator, slashedValidators[0]?.data, currentEpoch) + this.updateStateSlashed(dashboardStatus, slashedCount, validatorCount, foreignValidator, slashedValidators[0]?.data, currentEpoch, network) // handle offline validators const offlineCount = validatorCount - activeValidatorCount - exitedValidatorsCount - slashedCount - awaitingCount - eligibilityCount @@ -605,19 +608,27 @@ export default class OverviewController { // handle awaiting activation validators if (awaitingCount > 0) { - this.updateStateAwaiting(dashboardStatus, awaitingCount, validatorCount, foreignValidator, awaitingActivation[0].data, currentEpoch) + this.updateStateAwaiting(dashboardStatus, awaitingCount, validatorCount, foreignValidator, awaitingActivation[0].data, currentEpoch, network) } // handle eligable validators if (eligibilityCount > 0) { - this.updateStateEligibility(dashboardStatus, eligibilityCount, validatorCount, foreignValidator, activationEligibility[0].data, currentEpoch) + this.updateStateEligibility( + dashboardStatus, + eligibilityCount, + validatorCount, + foreignValidator, + activationEligibility[0].data, + currentEpoch, + network + ) } // handle exited validators this.updateExitedState(dashboardStatus, exitedValidatorsCount, validatorCount, foreignValidator) // handle ok state, always call last - this.updateStateOk(dashboardStatus, activeValidatorCount, validatorCount, foreignValidator, activeValidators[0]?.data, currentEpoch) + this.updateStateOk(dashboardStatus, activeValidatorCount, validatorCount, foreignValidator, activeValidators[0]?.data, currentEpoch, network) if (dashboardStatus.state == StateType.mixed) { // remove extended description if more than one state is shown @@ -632,12 +643,12 @@ export default class OverviewController { return foreignValidator ? foreignText : myText } - private getExitingDescription(validatorResp: ValidatorResponse, currentEpoch: EpochResponse): Description { + private getExitingDescription(validatorResp: ValidatorResponse, currentEpoch: EpochResponse, network: ApiNetwork): Description { if (!validatorResp.exitepoch) return { extendedDescriptionPre: null, extendedDescription: null } const exitDiff = validatorResp.exitepoch - currentEpoch.epoch const isExiting = exitDiff >= 0 && exitDiff < 6480 // ~ 1 month - const exitingDate = isExiting ? this.getEpochDate(validatorResp.exitepoch, currentEpoch) : null + const exitingDate = isExiting ? this.getEpochDate(validatorResp.exitepoch, currentEpoch, network) : null return { extendedDescriptionPre: isExiting ? 'Exiting on ' : null, @@ -659,7 +670,8 @@ export default class OverviewController { validatorCount: number, foreignValidator: boolean, validatorResp: ValidatorResponse, - currentEpoch: EpochResponse + currentEpoch: EpochResponse, + network: ApiNetwork ) { if (slashedCount == 0) { return @@ -668,7 +680,7 @@ export default class OverviewController { if (dashboardStatus.state == StateType.none) { dashboardStatus.icon = 'alert-circle-outline' dashboardStatus.iconCss = 'err' - const exitingDescription = this.getExitingDescription(validatorResp, currentEpoch) + const exitingDescription = this.getExitingDescription(validatorResp, currentEpoch, network) dashboardStatus.extendedDescriptionPre = exitingDescription.extendedDescriptionPre dashboardStatus.extendedDescription = exitingDescription.extendedDescription dashboardStatus.state = StateType.slashed @@ -690,14 +702,15 @@ export default class OverviewController { validatorCount: number, foreignValidator: boolean, awaitingActivation: ValidatorResponse, - currentEpoch: EpochResponse + currentEpoch: EpochResponse, + network: ApiNetwork ) { if (awaitingCount == 0) { return } if (dashboardStatus.state == StateType.none) { - const estEta = this.getEpochDate(awaitingActivation.activationeligibilityepoch, currentEpoch) + const estEta = this.getEpochDate(awaitingActivation.activationeligibilityepoch, currentEpoch, network) dashboardStatus.icon = 'timer-outline' dashboardStatus.iconCss = 'waiting' @@ -724,13 +737,14 @@ export default class OverviewController { validatorCount: number, foreignValidator: boolean, eligbleState: ValidatorResponse, - currentEpoch: EpochResponse + currentEpoch: EpochResponse, + network: ApiNetwork ) { if (eligibilityCount == 0) { return } - const estEta = this.getEpochDate(eligbleState.activationeligibilityepoch, currentEpoch) + const estEta = this.getEpochDate(eligbleState.activationeligibilityepoch, currentEpoch, network) if (dashboardStatus.state == StateType.none) { dashboardStatus.icon = 'timer-outline' @@ -804,7 +818,8 @@ export default class OverviewController { validatorCount: number, foreignValidator: boolean, validatorResp: ValidatorResponse, - currentEpoch: EpochResponse + currentEpoch: EpochResponse, + network: ApiNetwork ) { if (dashboardStatus.state != StateType.mixed && dashboardStatus.state != StateType.none) { return @@ -818,14 +833,14 @@ export default class OverviewController { highlight: true, }) - const exitingDescription = this.getExitingDescription(validatorResp, currentEpoch) + const exitingDescription = this.getExitingDescription(validatorResp, currentEpoch, network) dashboardStatus.extendedDescriptionPre = exitingDescription.extendedDescriptionPre dashboardStatus.extendedDescription = exitingDescription.extendedDescription dashboardStatus.state = StateType.online } } - private getEpochDate(activationEpoch: number, currentEpoch: EpochResponse) { + private getEpochDate(activationEpoch: number, currentEpoch: EpochResponse, network: ApiNetwork) { try { const diff = activationEpoch - currentEpoch.epoch if (diff <= 0) { @@ -835,7 +850,7 @@ export default class OverviewController { const date = new Date(currentEpoch.lastCachedTimestamp) - const inEpochOffset = (32 - currentEpoch.scheduledblocks) * 12 // block time 12s + const inEpochOffset = (network.slotPerEpoch - currentEpoch.scheduledblocks) * network.slotsTime // block time 12s date.setSeconds(diff * 6.4 * 60 - inEpochOffset) @@ -883,15 +898,15 @@ export default class OverviewController { }) } - private calculateSyncCommitteeStats(stats: SyncCommitteesStatisticsResponse): SyncCommitteesStatistics { + private calculateSyncCommitteeStats(stats: SyncCommitteesStatisticsResponse, network: ApiNetwork): SyncCommitteesStatistics { if (stats) { // if no slots where expected yet, don't show any statistic as either no validator is subscribed or they have not been active in the selected timeframe if (stats.expectedSlots > 0) { const slotsTotal = stats.participatedSlots + stats.missedSlots - const slotsPerSyncPeriod = 32 * 256 + const slotsPerSyncPeriod = network.slotPerEpoch * network.epochsPerSyncPeriod const r: SyncCommitteesStatistics = { - committeesParticipated: Math.ceil(slotsTotal / 32 / 256), - committeesExpected: Math.round((stats.expectedSlots * 100) / 32 / 256) / 100, + committeesParticipated: Math.ceil(slotsTotal / network.slotPerEpoch / network.epochsPerSyncPeriod), + committeesExpected: Math.round((stats.expectedSlots * 100) / network.slotPerEpoch / network.epochsPerSyncPeriod) / 100, slotsPerSyncCommittee: slotsPerSyncPeriod, slotsLeftInSyncCommittee: slotsPerSyncPeriod - stats.scheduledSlots, slotsParticipated: stats.participatedSlots, diff --git a/src/app/models/StorageTypes.ts b/src/app/models/StorageTypes.ts index d9d16c35..be7660c4 100644 --- a/src/app/models/StorageTypes.ts +++ b/src/app/models/StorageTypes.ts @@ -38,6 +38,7 @@ export interface ApiNetwork { clCurrency: NetworkMainCurrency slotPerEpoch: number slotsTime: number + epochsPerSyncPeriod: number name: string } diff --git a/src/app/pages/validatordetail/validatordetail.page.ts b/src/app/pages/validatordetail/validatordetail.page.ts index 01dc390e..2fb125fb 100644 --- a/src/app/pages/validatordetail/validatordetail.page.ts +++ b/src/app/pages/validatordetail/validatordetail.page.ts @@ -25,6 +25,7 @@ import OverviewController, { OverviewData } from '../../controllers/OverviewCont import { fromEvent, Subscription } from 'rxjs' import { MerchantUtils } from 'src/app/utils/MerchantUtils' import { UnitconvService } from 'src/app/services/unitconv.service' +import { ApiService } from 'src/app/services/api.service' @Component({ selector: 'app-validatordetail', @@ -49,7 +50,8 @@ export class ValidatordetailPage implements OnInit { private validatorUtils: ValidatorUtils, private modalCtrl: ModalController, private merchant: MerchantUtils, - private unit: UnitconvService + private unit: UnitconvService, + private api: ApiService ) {} setInput(validator: Validator) { @@ -94,7 +96,7 @@ export class ValidatordetailPage implements OnInit { const epoch = await this.validatorUtils.getRemoteCurrentEpoch() const overviewController = new OverviewController(null, await this.merchant.getCurrentPlanMaxValidator(), this.unit) - this.data = overviewController.processDetail([item], epoch) + this.data = overviewController.processDetail([item], epoch, this.api.getNetwork()) } tag() { diff --git a/src/app/tab-dashboard/tab-dashboard.page.ts b/src/app/tab-dashboard/tab-dashboard.page.ts index 7155533e..c47ff596 100644 --- a/src/app/tab-dashboard/tab-dashboard.page.ts +++ b/src/app/tab-dashboard/tab-dashboard.page.ts @@ -134,7 +134,8 @@ export class Tab1Page { validators, epoch, this.validatorUtils.syncCommitteesStatsResponse, - this.validatorUtils.proposalLuckResponse + this.validatorUtils.proposalLuckResponse, + this.api.getNetwork() ) this.lastRefreshTs = this.getUnixSeconds() } diff --git a/src/app/utils/BlockUtils.ts b/src/app/utils/BlockUtils.ts index 312fbad4..00ea5d0b 100644 --- a/src/app/utils/BlockUtils.ts +++ b/src/app/utils/BlockUtils.ts @@ -82,7 +82,7 @@ export class BlockUtils { luckPercentage: proposalLuckStats.proposal_luck, timeFrameName: proposalLuckStats.time_frame_name, userValidators: valis.length, - expectedBlocksPerMonth: MONTH / (proposalLuckStats.average_proposal_interval * 12), + expectedBlocksPerMonth: MONTH / (proposalLuckStats.average_proposal_interval * this.api.getNetwork().slotsTime), nextBlockEstimate: proposalLuckStats.next_proposal_estimate_ts * 1000, } as Luck } diff --git a/src/app/utils/NetworkData.ts b/src/app/utils/NetworkData.ts index a4f347cf..2376b16b 100644 --- a/src/app/utils/NetworkData.ts +++ b/src/app/utils/NetworkData.ts @@ -35,6 +35,7 @@ export const MAP: ApiNetwork[] = [ elCurrency: NetworkMainCurrency.ETH, slotPerEpoch: 32, slotsTime: 12, + epochsPerSyncPeriod: 256, name: 'Ethereum', }, { @@ -51,6 +52,7 @@ export const MAP: ApiNetwork[] = [ elCurrency: NetworkMainCurrency.xDAI, slotPerEpoch: 16, slotsTime: 5, + epochsPerSyncPeriod: 512, name: 'Gnosis', }, { @@ -67,6 +69,7 @@ export const MAP: ApiNetwork[] = [ elCurrency: NetworkMainCurrency.ETH, slotPerEpoch: 32, slotsTime: 12, + epochsPerSyncPeriod: 256, name: 'Ethereum', }, { @@ -83,6 +86,7 @@ export const MAP: ApiNetwork[] = [ elCurrency: NetworkMainCurrency.ETH, slotPerEpoch: 32, slotsTime: 12, + epochsPerSyncPeriod: 256, name: 'Ethereum', }, { @@ -99,6 +103,7 @@ export const MAP: ApiNetwork[] = [ elCurrency: NetworkMainCurrency.ETH, slotPerEpoch: 32, slotsTime: 12, + epochsPerSyncPeriod: 256, name: 'Ethereum', }, { @@ -115,6 +120,7 @@ export const MAP: ApiNetwork[] = [ elCurrency: NetworkMainCurrency.ETH, slotPerEpoch: 32, slotsTime: 12, + epochsPerSyncPeriod: 256, name: 'Ethereum', }, { @@ -131,6 +137,7 @@ export const MAP: ApiNetwork[] = [ elCurrency: NetworkMainCurrency.ETH, slotPerEpoch: 32, slotsTime: 12, + epochsPerSyncPeriod: 256, name: 'Ethereum', }, ] From 47c9902cad6f989252005757921377298184d9c2 Mon Sep 17 00:00:00 2001 From: D13ce Date: Fri, 3 Nov 2023 08:56:01 +0100 Subject: [PATCH 53/53] Remove deprecated comment --- src/app/controllers/OverviewController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/controllers/OverviewController.ts b/src/app/controllers/OverviewController.ts index ccbea2a7..5ab41a48 100644 --- a/src/app/controllers/OverviewController.ts +++ b/src/app/controllers/OverviewController.ts @@ -850,7 +850,7 @@ export default class OverviewController { const date = new Date(currentEpoch.lastCachedTimestamp) - const inEpochOffset = (network.slotPerEpoch - currentEpoch.scheduledblocks) * network.slotsTime // block time 12s + const inEpochOffset = (network.slotPerEpoch - currentEpoch.scheduledblocks) * network.slotsTime date.setSeconds(diff * 6.4 * 60 - inEpochOffset)