From 0f215278d6018c4ea1e76e364af715e017709e20 Mon Sep 17 00:00:00 2001 From: Lucas Lovato Date: Mon, 4 Nov 2024 08:09:52 -0300 Subject: [PATCH 1/2] chore: refactor set active network - 1 --- .../Background/controllers/MainController.ts | 548 ++++++++++-------- 1 file changed, 297 insertions(+), 251 deletions(-) diff --git a/source/scripts/Background/controllers/MainController.ts b/source/scripts/Background/controllers/MainController.ts index 35c3460e1..8a04dfc4b 100644 --- a/source/scripts/Background/controllers/MainController.ts +++ b/source/scripts/Background/controllers/MainController.ts @@ -125,92 +125,6 @@ class MainController extends KeyringManager { this.bindMethods(); } - - private bindMethods() { - const proto = Object.getPrototypeOf(this); - for (const key of Object.getOwnPropertyNames(proto)) { - if (typeof this[key] === 'function' && key !== 'constructor') { - this[key] = this[key].bind(this); - } - } - } - - private createCancellablePromise( - executor: ( - resolve: (value: T) => void, - reject: (reason?: any) => void - ) => void - ): { cancel: () => void; promise: Promise } { - let cancel = () => { - // no-op - }; - const promise: Promise = new Promise((resolve, reject) => { - cancel = () => { - reject('Network change cancelled'); - }; - executor(resolve, reject); - }); - - return { promise, cancel }; - } - - private setActiveNetworkLogic = async ( - network: INetwork, - chain: string, - cancelled: boolean, - resolve: (value: { - activeChain: INetworkType; - chain: string; - chainId: string; - isBitcoinBased: boolean; - network: INetwork; - networkVersion: number; - wallet: IWalletState; - }) => void, - reject: (reason?: any) => void - ) => { - if (store.getState().vault.isNetworkChanging && !cancelled) { - return; - } - - store.dispatch(setIsNetworkChanging(true)); - store.dispatch(setIsLoadingBalances(true)); - store.dispatch(setCurrentBlock(undefined)); - - const isBitcoinBased = chain === INetworkType.Syscoin; - - const { sucess, wallet, activeChain } = await this.setSignerNetwork( - network, - chain - ); - const chainId = network.chainId.toString(16); - const networkVersion = network.chainId; - if (sucess) { - this.web3Provider = this.ethereumTransaction.web3Provider; - this.assetsManager = AssetsManager(this.ethereumTransaction.web3Provider); - this.assets = this.assetsManager; - this.transactionsManager = TransactionsManager( - this.ethereumTransaction.web3Provider - ); - this.balancesManager = BalancesManager( - this.ethereumTransaction.web3Provider - ); - resolve({ - activeChain, - chain, - chainId, - isBitcoinBased, - network, - networkVersion, - wallet, - }); - } else { - reject( - 'Pali: fail on setActiveNetwork - keyringManager.setSignerNetwork' - ); - } - }; - public setAutolockTimer(minutes: number) { store.dispatch(setTimer(minutes)); } @@ -432,7 +346,6 @@ class MainController extends KeyringManager { network: INetwork, chain: string ): Promise<{ chainId: string; networkVersion: number }> { - const controller = getController(); let cancelled = false; if (this.currentPromise) { this.currentPromise.cancel(); @@ -453,174 +366,15 @@ class MainController extends KeyringManager { this.currentPromise = promiseWrapper; promiseWrapper.promise .then(async ({ wallet, activeChain, isBitcoinBased }) => { - store.dispatch( - setNetworkChange({ - activeChain, - wallet, - }) - ); - store.dispatch(setIsBitcoinBased(isBitcoinBased)); - store.dispatch(setIsLoadingBalances(false)); - await this.utilsController.setFiat(); - - this.updateAssetsFromCurrentAccount({ - isBitcoinBased, - activeNetwork: network, - activeAccount: { - id: wallet.activeAccountId, - type: wallet.activeAccountType, - }, - }); - - this.updateUserTransactionsState({ - isPolling: false, + await this.handleNetworkChangeSuccess( + wallet, + activeChain, isBitcoinBased, - activeNetwork: network, - activeAccount: { - id: wallet.activeAccountId, - type: wallet.activeAccountType, - }, - }); - - controller.dapp.handleStateChange(PaliEvents.chainChanged, { - method: PaliEvents.chainChanged, - params: { - chainId: `0x${network.chainId.toString(16)}`, - networkVersion: network.chainId, - }, - }); - - controller.dapp.handleStateChange(PaliEvents.isBitcoinBased, { - method: PaliEvents.isBitcoinBased, - params: { isBitcoinBased }, - }); - - controller.dapp.handleBlockExplorerChange( - PaliSyscoinEvents.blockExplorerChanged, - { - method: PaliSyscoinEvents.blockExplorerChanged, - params: isBitcoinBased ? network.url : null, - } + network ); - - switch (isBitcoinBased) { - case true: - const isTestnet = this.verifyIfIsTestnet(); - - controller.dapp.handleStateChange(PaliEvents.isTestnet, { - method: PaliEvents.isTestnet, - params: { isTestnet }, - }); - - controller.dapp.handleStateChange(PaliEvents.xpubChanged, { - method: PaliEvents.xpubChanged, - params: - wallet.accounts[wallet.activeAccountType][ - wallet.activeAccountId - ].xpub, - }); - - controller.dapp.handleStateChange(PaliEvents.accountsChanged, { - method: PaliEvents.accountsChanged, - params: null, - }); - break; - case false: - controller.dapp.handleStateChange(PaliEvents.isTestnet, { - method: PaliEvents.isTestnet, - params: { isTestnet: undefined }, - }); - - controller.dapp.handleStateChange(PaliEvents.xpubChanged, { - method: PaliEvents.xpubChanged, - params: null, - }); - - controller.dapp.handleStateChange(PaliEvents.accountsChanged, { - method: PaliEvents.accountsChanged, - params: [ - wallet.accounts[wallet.activeAccountType][ - wallet.activeAccountId - ].address, - ], - }); - break; - default: - break; - } - - store.dispatch(setIsNetworkChanging(false)); - return; }) - .catch((reason) => { - if (reason === 'Network change cancelled') { - console.error('User asked to switch network - slow connection'); - } else { - const { - activeNetwork, - isBitcoinBased, - accounts, - activeAccount: { id: activeAccountId, type: activeAccountType }, - } = store.getState().vault; - - controller.dapp.handleStateChange(PaliEvents.chainChanged, { - method: PaliEvents.chainChanged, - params: { - chainId: `0x${activeNetwork.chainId.toString(16)}`, - networkVersion: activeNetwork.chainId, - }, - }); - controller.dapp.handleBlockExplorerChange( - PaliSyscoinEvents.blockExplorerChanged, - { - method: PaliSyscoinEvents.blockExplorerChanged, - params: isBitcoinBased ? network.url : null, - } - ); + .catch(this.handleNetworkChangeError); - switch (isBitcoinBased) { - case true: - const isTestnet = this.verifyIfIsTestnet(); - - controller.dapp.handleStateChange(PaliEvents.isTestnet, { - method: PaliEvents.isTestnet, - params: { isTestnet }, - }); - - controller.dapp.handleStateChange(PaliEvents.xpubChanged, { - method: PaliEvents.xpubChanged, - params: accounts[activeAccountType][activeAccountId].xpub, - }); - - controller.dapp.handleStateChange(PaliEvents.accountsChanged, { - method: PaliEvents.accountsChanged, - params: null, - }); - - break; - case false: - controller.dapp.handleStateChange(PaliEvents.isTestnet, { - method: PaliEvents.isTestnet, - params: { isTestnet: undefined }, - }); - - controller.dapp.handleStateChange(PaliEvents.xpubChanged, { - method: PaliEvents.xpubChanged, - params: null, - }); - - controller.dapp.handleStateChange(PaliEvents.accountsChanged, { - method: PaliEvents.accountsChanged, - params: [accounts[activeAccountType][activeAccountId].address], - }); - default: - break; - } - } - store.dispatch(setStoreError(true)); - store.dispatch(setIsNetworkChanging(false)); - store.dispatch(setIsLoadingBalances(false)); - }); return promiseWrapper.promise; } @@ -1560,6 +1314,298 @@ class MainController extends KeyringManager { }) { store.dispatch(setShouldShowFaucetModal({ chainId, isOpen })); } + + private handleStateChange( + events: { method: PaliEvents | PaliSyscoinEvents; params: any }[] + ) { + const controller = getController(); + events.forEach((event: { method: PaliEvents; params: any }) => { + controller.dapp.handleStateChange(event.method, event); + }); + } + + private handleNetworkChangeError = (reason: any) => { + const { + activeNetwork, + isBitcoinBased, + accounts, + activeAccount: { id: activeAccountId, type: activeAccountType }, + } = store.getState().vault; + + if (reason === 'Network change cancelled') { + console.error('User asked to switch network - slow connection'); + } else { + this.handleStateChange([ + { + method: PaliEvents.chainChanged, + params: { + chainId: `0x${activeNetwork.chainId.toString(16)}`, + networkVersion: activeNetwork.chainId, + }, + }, + { + method: PaliSyscoinEvents.blockExplorerChanged, + params: isBitcoinBased ? activeNetwork.url : null, + }, + { + method: PaliEvents.isTestnet, + params: { + isTestnet: isBitcoinBased ? this.verifyIfIsTestnet() : undefined, + }, + }, + { + method: PaliEvents.xpubChanged, + params: isBitcoinBased + ? accounts[activeAccountType][activeAccountId].xpub + : null, + }, + { + method: PaliEvents.accountsChanged, + params: isBitcoinBased + ? null + : [accounts[activeAccountType][activeAccountId].address], + }, + ]); + } + + store.dispatch(setStoreError(true)); + store.dispatch(setIsNetworkChanging(false)); + store.dispatch(setIsLoadingBalances(false)); + }; + private async configureNetwork( + network: INetwork, + chain: string + ): Promise<{ + activeChain: INetworkType; + success: boolean; + wallet: IWalletState; + }> { + const { + sucess: success, + wallet, + activeChain, + } = await this.setSignerNetwork(network, chain); + if (success) { + this.web3Provider = this.ethereumTransaction.web3Provider; + this.assetsManager = AssetsManager(this.ethereumTransaction.web3Provider); + this.assets = this.assetsManager; + this.transactionsManager = TransactionsManager( + this.ethereumTransaction.web3Provider + ); + this.balancesManager = BalancesManager( + this.ethereumTransaction.web3Provider + ); + } + return { success, wallet, activeChain }; + } + + private resolveNetworkConfiguration( + resolve: (value: { + activeChain: INetworkType; + chain: string; + chainId: string; + isBitcoinBased: boolean; + network: INetwork; + networkVersion: number; + wallet: IWalletState; + }) => void, + { + activeChain, + chain, + chainId, + isBitcoinBased, + network, + networkVersion, + wallet, + }: { + activeChain: INetworkType; + chain: string; + chainId: string; + isBitcoinBased: boolean; + network: INetwork; + networkVersion: number; + wallet: IWalletState; + } + ) { + resolve({ + activeChain, + chain, + chainId, + isBitcoinBased, + network, + networkVersion, + wallet, + }); + } + private bindMethods() { + const proto = Object.getPrototypeOf(this); + for (const key of Object.getOwnPropertyNames(proto)) { + if (typeof this[key] === 'function' && key !== 'constructor') { + this[key] = this[key].bind(this); + } + } + } + + private createCancellablePromise( + executor: ( + resolve: (value: T) => void, + reject: (reason?: any) => void + ) => void + ): { cancel: () => void; promise: Promise } { + let cancel = () => { + // no-op + }; + const promise: Promise = new Promise((resolve, reject) => { + cancel = () => { + reject('Network change cancelled'); + }; + executor(resolve, reject); + }); + + return { promise, cancel }; + } + + private setActiveNetworkLogic = async ( + network: INetwork, + chain: string, + cancelled: boolean, + resolve: (value: { + activeChain: INetworkType; + chain: string; + chainId: string; + isBitcoinBased: boolean; + network: INetwork; + networkVersion: number; + wallet: IWalletState; + }) => void, + reject: (reason?: any) => void + ) => { + if (store.getState().vault.isNetworkChanging && !cancelled) { + return; + } + + store.dispatch(setIsNetworkChanging(true)); + store.dispatch(setIsLoadingBalances(true)); + store.dispatch(setCurrentBlock(undefined)); + + const isBitcoinBased = chain === INetworkType.Syscoin; + const { success, wallet, activeChain } = await this.configureNetwork( + network, + chain + ); + const chainId = network.chainId.toString(16); + const networkVersion = network.chainId; + + if (success) { + this.resolveNetworkConfiguration(resolve, { + activeChain, + chain, + chainId, + isBitcoinBased, + network, + networkVersion, + wallet, + }); + } else { + reject( + 'Pali: fail on setActiveNetwork - keyringManager.setSignerNetwork' + ); + } + }; + + private async handleNetworkChangeSuccess( + wallet: IWalletState, + activeChain: INetworkType, + isBitcoinBased: boolean, + network: INetwork + ) { + store.dispatch( + setNetworkChange({ + activeChain, + wallet, + }) + ); + store.dispatch(setIsBitcoinBased(isBitcoinBased)); + store.dispatch(setIsLoadingBalances(false)); + await this.utilsController.setFiat(); + + this.updateAssetsFromCurrentAccount({ + isBitcoinBased, + activeNetwork: network, + activeAccount: { + id: wallet.activeAccountId, + type: wallet.activeAccountType, + }, + }); + + this.updateUserTransactionsState({ + isPolling: false, + isBitcoinBased, + activeNetwork: network, + activeAccount: { + id: wallet.activeAccountId, + type: wallet.activeAccountType, + }, + }); + + this.handleStateChange([ + { + method: PaliEvents.chainChanged, + params: { + chainId: `0x${network.chainId.toString(16)}`, + networkVersion: network.chainId, + }, + }, + { + method: PaliEvents.isBitcoinBased, + params: { isBitcoinBased }, + }, + { + method: PaliSyscoinEvents.blockExplorerChanged, + params: isBitcoinBased ? network.url : null, + }, + ]); + + if (isBitcoinBased) { + const isTestnet = this.verifyIfIsTestnet(); + this.handleStateChange([ + { + method: PaliEvents.isTestnet, + params: { isTestnet }, + }, + { + method: PaliEvents.xpubChanged, + params: + wallet.accounts[wallet.activeAccountType][wallet.activeAccountId] + .xpub, + }, + { + method: PaliEvents.accountsChanged, + params: null, + }, + ]); + } else { + this.handleStateChange([ + { + method: PaliEvents.isTestnet, + params: { isTestnet: undefined }, + }, + { + method: PaliEvents.xpubChanged, + params: null, + }, + { + method: PaliEvents.accountsChanged, + params: [ + wallet.accounts[wallet.activeAccountType][wallet.activeAccountId] + .address, + ], + }, + ]); + } + + store.dispatch(setIsNetworkChanging(false)); + } } export default MainController; From 7c94e61a05458bd505a9af16dec523583cf45647 Mon Sep 17 00:00:00 2001 From: Lucas Santos Date: Wed, 6 Nov 2024 18:57:26 -0300 Subject: [PATCH 2/2] fix: solve glitching balance bug --- .../Background/controllers/MainController.ts | 18 +++++++++++++++++- .../Background/controllers/balances/index.ts | 8 +++++--- .../Background/controllers/balances/types.ts | 5 ++++- source/scripts/Background/index.ts | 2 ++ source/state/store.ts | 6 +++++- 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/source/scripts/Background/controllers/MainController.ts b/source/scripts/Background/controllers/MainController.ts index 8a04dfc4b..43e2c8956 100644 --- a/source/scripts/Background/controllers/MainController.ts +++ b/source/scripts/Background/controllers/MainController.ts @@ -125,6 +125,7 @@ class MainController extends KeyringManager { this.bindMethods(); } + public setAutolockTimer(minutes: number) { store.dispatch(setTimer(minutes)); } @@ -1206,6 +1207,7 @@ class MainController extends KeyringManager { isBitcoinBased, activeNetwork, activeAccount, + isPolling, }: { activeAccount: { id: number; @@ -1213,11 +1215,22 @@ class MainController extends KeyringManager { }; activeNetwork: INetwork; isBitcoinBased: boolean; + isPolling?: boolean; }) { const { accounts } = store.getState().vault; const currentAccount = accounts[activeAccount.type][activeAccount.id]; + let internalProvider: CustomJsonRpcProvider | undefined; + + if (isPolling) { + const abortController = new AbortController(); + internalProvider = new CustomJsonRpcProvider( + abortController.signal, + activeNetwork.url + ); + } + const { currentPromise: balancePromise, cancel } = this.cancellablePromises.createCancellablePromise( async (resolve, reject) => { @@ -1226,7 +1239,8 @@ class MainController extends KeyringManager { await this.balancesManager.utils.getBalanceUpdatedForAccount( currentAccount, isBitcoinBased, - activeNetwork.url + activeNetwork.url, + internalProvider ); const actualUserBalance = isBitcoinBased @@ -1372,6 +1386,7 @@ class MainController extends KeyringManager { store.dispatch(setIsNetworkChanging(false)); store.dispatch(setIsLoadingBalances(false)); }; + private async configureNetwork( network: INetwork, chain: string @@ -1437,6 +1452,7 @@ class MainController extends KeyringManager { wallet, }); } + private bindMethods() { const proto = Object.getPrototypeOf(this); for (const key of Object.getOwnPropertyNames(proto)) { diff --git a/source/scripts/Background/controllers/balances/index.ts b/source/scripts/Background/controllers/balances/index.ts index 96df215fb..f9373d9a6 100644 --- a/source/scripts/Background/controllers/balances/index.ts +++ b/source/scripts/Background/controllers/balances/index.ts @@ -13,7 +13,8 @@ const BalancesManager = ( const getBalanceUpdatedForAccount = async ( currentAccount: IPaliAccount, isBitcoinBased: boolean, - networkUrl: string + networkUrl: string, + provider?: CustomJsonRpcProvider ) => { switch (isBitcoinBased) { case true: @@ -30,8 +31,9 @@ const BalancesManager = ( } case false: try { - const getEvmBalance = - await evmBalanceController.getEvmBalanceForAccount(currentAccount); + const getEvmBalance = await EvmBalanceController( + provider || web3Provider + ).getEvmBalanceForAccount(currentAccount); return getEvmBalance; } catch (evmBalanceError) { diff --git a/source/scripts/Background/controllers/balances/types.ts b/source/scripts/Background/controllers/balances/types.ts index 5f78ce85b..34cae1cb5 100644 --- a/source/scripts/Background/controllers/balances/types.ts +++ b/source/scripts/Background/controllers/balances/types.ts @@ -1,3 +1,5 @@ +import { CustomJsonRpcProvider } from '@pollum-io/sysweb3-keyring'; + import { IPaliAccount } from 'state/vault/types'; export interface IEvmBalanceController { @@ -15,7 +17,8 @@ export interface IBalancesManagerUtils { getBalanceUpdatedForAccount: ( currentAccount: IPaliAccount, isBitcoinBased: boolean, - networkUrl: string + networkUrl: string, + provider?: CustomJsonRpcProvider ) => Promise; } diff --git a/source/scripts/Background/index.ts b/source/scripts/Background/index.ts index a12472818..25e4d51ca 100755 --- a/source/scripts/Background/index.ts +++ b/source/scripts/Background/index.ts @@ -73,6 +73,7 @@ async function createOffscreen() { }) .catch(() => {}); } + chrome.runtime.onStartup.addListener(createOffscreen); self.onmessage = () => null; // keepAlive createOffscreen(); @@ -314,6 +315,7 @@ async function checkForUpdates() { walletMethods?.updateUserTransactionsState ) { walletMethods.updateUserNativeBalance({ + isPolling: true, isBitcoinBased, activeNetwork, activeAccount, diff --git a/source/state/store.ts b/source/state/store.ts index fb67c0ecc..ab279b994 100755 --- a/source/state/store.ts +++ b/source/state/store.ts @@ -5,6 +5,7 @@ import { Store, } from '@reduxjs/toolkit'; import isEqual from 'lodash/isEqual'; +import logger from 'redux-logger'; import { thunk } from 'redux-thunk'; import dapp from './dapp'; @@ -27,9 +28,12 @@ const middleware: any = [ ]; middleware.push(thunk); - const nodeEnv = process.env.NODE_ENV; +if (nodeEnv !== 'production' && nodeEnv !== 'test') { + middleware.push(logger as never); +} + const store: Store<{ dapp: IDAppState; price: IPriceState;