From 4053a740648be2ee0cf30cc1efdbc8e6287354bd Mon Sep 17 00:00:00 2001 From: Artem Burakov Date: Wed, 23 Oct 2024 13:48:07 +0300 Subject: [PATCH 01/10] feat: Added updateQiBalanceCallback & draft subscribeOnQiBalances --- background/services/chain/index.ts | 175 ++++++++++++++++++++++++++++- 1 file changed, 173 insertions(+), 2 deletions(-) diff --git a/background/services/chain/index.ts b/background/services/chain/index.ts index 4c7e93ca..2ad2f338 100644 --- a/background/services/chain/index.ts +++ b/background/services/chain/index.ts @@ -21,11 +21,12 @@ import { QiWalletOnNetwork, } from "../../accounts" import { + AnyAsset, AnyAssetAmount, AssetTransfer, SmartContractFungibleAsset, } from "../../assets" -import { HOUR, MAILBOX_CONTRACT_ADDRESS, MINUTE, QI } from "../../constants" +import { HOUR, MAILBOX_CONTRACT_ADDRESS, MINUTE, QI, QUAI } from "../../constants" import PreferenceService from "../preferences" import { ServiceCreatorFunction, ServiceLifecycleEvents } from "../types" import { ChainDatabase, initializeChainDatabase } from "./db" @@ -37,6 +38,10 @@ import KeyringService from "../keyring" import type { ValidatedAddEthereumChainParameter } from "../provider-bridge/utils" import { Outpoint } from "quais/lib/commonjs/transaction/utxo" import { MAILBOX_INTERFACE } from "../../contracts/payment-channel-mailbox" +import { + updateAccountBalance, + updateUtxoAccountsBalances, +} from "../../redux-slices/accounts" // The number of blocks to query at a time for historic asset transfers. // Unfortunately there's no "right" answer here that works well across different @@ -113,6 +118,8 @@ export default class ChainService extends BaseService { public supportedNetworks = PELAGUS_NETWORKS + private activeSubscriptions: Map> = new Map() + subscribedAccounts: { account: string provider: JsonRpcProvider @@ -229,9 +236,11 @@ export default class ChainService extends BaseService { this.emitter.emit("supportedNetworks", PELAGUS_NETWORKS) await this.subscribeOnNetworksAndAddresses(this.supportedNetworks, accounts) + + await this.startAddressBalanceSubscriber() } - public switchNetwork(network: NetworkInterface): void { + public async switchNetwork(network: NetworkInterface): Promise { const { jsonRpcProvider, webSocketProvider } = this.providerFactory.getProvidersForNetwork(network.chainID) @@ -242,9 +251,170 @@ export default class ChainService extends BaseService { this.subscribedAccounts.map((item) => this.getLatestBaseAccountBalance({ address: item.account, network }) ) + + // if (this.isNetworkSubscribed(network)) { + // console.log( + // `Network ${network.chainID} is already subscribed. Skipping subscription.` + // ) + // return + // } + + this.startAddressBalanceSubscriber() } // -------------------------------------------------------------------------------------------------- + private async startAddressBalanceSubscriber(): Promise { + const accounts = await this.getTrackedAddressesOnNetwork( + this.selectedNetwork + ) + this.subscribeOnBalances(this.selectedNetwork, accounts) + this.trackSubscriptions(this.selectedNetwork, accounts) + } + + private trackSubscriptions( + network: NetworkInterface, + accounts: AddressOnNetwork[] + ) { + const subscribedAccounts = + this.activeSubscriptions.get(network.chainID) || new Set() + + accounts.forEach((account) => subscribedAccounts.add(account.address)) + + this.activeSubscriptions.set(network.chainID, subscribedAccounts) + } + + private async subscribeOnBalances( + network: NetworkInterface, + accounts: AddressOnNetwork[] + ): Promise { + console.log("subscribing to accounts balances", accounts, network) + + accounts.forEach(({ address }) => { + this.webSocketProvider.on({ type: "balance", address }, (balance) => { + console.log("new balance for account", address, balance) + this.updateQuaiBalanceCallback({ + network, + balance, + address, + asset: network.baseAsset, + }) + }) + }) + } + + private async subscribeOnQiBalances() { + const qiWallet = await this.keyringService.getQiHDWallet() + qiWallet.connect(this.jsonRpcProvider) + const addressesForZone = qiWallet.getAddressesForZone(Zone.Cyprus1) + const paymentCode = qiWallet.getPaymentCode(0) + + if (!addressesForZone?.length) return + + this.supportedNetworks.map((supportedNetwork) => { + addressesForZone.map((qiAddress) => { + this.webSocketProvider.on( + { type: "balance", address: qiAddress.address }, + (balance) => { + const zoneBalance = qiWallet.getBalanceForZone(Zone.Cyprus1) + + console.log("new balance for account", paymentCode, balance) + this.updateQiBalanceCallback({ + network: supportedNetwork, + balance: zoneBalance, + paymentCode, + asset: QI, // TO WHICH ASSET AMOUNT UPDATED // + }) + } + ) + }) + }) + } + + private isNetworkSubscribed(network: NetworkInterface): boolean { + return this.activeSubscriptions.has(network.chainID) + } + + public async onNewAccountCreated( + network: NetworkInterface, + newAccount: AddressOnNetwork + ) { + console.log(`New account created on network: ${network.chainID}`) + + const subscribedAccounts = this.activeSubscriptions.get(network.chainID) + + if (subscribedAccounts) { + this.subscribeOnBalances(network, [newAccount]) + subscribedAccounts.add(newAccount.address) + } else { + const accounts = await this.getTrackedAddressesOnNetwork(network) + accounts.push(newAccount) + + this.subscribeOnBalances(network, accounts) + this.trackSubscriptions(network, accounts) + } + } + + public async updateQuaiBalanceCallback({ + network, + address, + balance, + asset, + }: { + network: NetworkInterface + address: string + balance: bigint + asset: AnyAsset + }) { + globalThis.main.store.dispatch( + updateAccountBalance({ + balances: [ + { + address, + assetAmount: { + amount: balance, + asset, + }, + network, + retrievedAt: Date.now(), + dataSource: "local", + }, + ], + addressOnNetwork: { + address, + network, + }, + }) + ) + } + + public async updateQiBalanceCallback({ + network, + paymentCode, + balance, + asset, + }: { + network: NetworkInterface + paymentCode: string + balance: bigint + asset: AnyAsset + }) { + globalThis.main.store.dispatch( + updateUtxoAccountsBalances({ + balances: [ + { + paymentCode, + network, + assetAmount: { + asset, + amount: balance, + }, + dataSource: "local", + retrievedAt: Date.now(), + }, + ], + }) + ) + } private subscribeOnNetworksAndAddresses = async ( networks: NetworkInterface[], @@ -449,6 +619,7 @@ export default class ChainService extends BaseService { addressOnNetwork: addressNetwork, source, }) + await this.onNewAccountCreated(addressNetwork.network, addressNetwork) } this.getLatestBaseAccountBalance(addressNetwork).catch((e) => { From 7c742021a0d2ec09a37de7b527d2db5af4bf6116 Mon Sep 17 00:00:00 2001 From: MichaelKostroma Date: Wed, 23 Oct 2024 18:21:10 +0300 Subject: [PATCH 02/10] fix: Updated subscriptions managing for quai acc --- background/services/chain/index.ts | 34 +++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/background/services/chain/index.ts b/background/services/chain/index.ts index 2ad2f338..b1f6b8fc 100644 --- a/background/services/chain/index.ts +++ b/background/services/chain/index.ts @@ -252,18 +252,19 @@ export default class ChainService extends BaseService { this.getLatestBaseAccountBalance({ address: item.account, network }) ) - // if (this.isNetworkSubscribed(network)) { - // console.log( - // `Network ${network.chainID} is already subscribed. Skipping subscription.` - // ) - // return - // } - this.startAddressBalanceSubscriber() } // -------------------------------------------------------------------------------------------------- private async startAddressBalanceSubscriber(): Promise { + console.log(this.activeSubscriptions) + if (this.isNetworkSubscribed(this.selectedNetwork)) { + logger.info( + `Network ${this.selectedNetwork.chainID} is already subscribed. Skipping subscription.` + ) + return + } + const accounts = await this.getTrackedAddressesOnNetwork( this.selectedNetwork ) @@ -289,8 +290,15 @@ export default class ChainService extends BaseService { ): Promise { console.log("subscribing to accounts balances", accounts, network) + const { webSocketProvider } = this.providerFactory.getProvidersForNetwork( + network.chainID + ) + + if (!webSocketProvider) + logger.error("WebSocketProvider for balance subscription not found") + accounts.forEach(({ address }) => { - this.webSocketProvider.on({ type: "balance", address }, (balance) => { + webSocketProvider.on({ type: "balance", address }, (balance) => { console.log("new balance for account", address, balance) this.updateQuaiBalanceCallback({ network, @@ -345,9 +353,15 @@ export default class ChainService extends BaseService { if (subscribedAccounts) { this.subscribeOnBalances(network, [newAccount]) subscribedAccounts.add(newAccount.address) - } else { + return + } + + const provider = this.providerFactory.getProvidersForNetwork( + network.chainID + ) + + if (provider) { const accounts = await this.getTrackedAddressesOnNetwork(network) - accounts.push(newAccount) this.subscribeOnBalances(network, accounts) this.trackSubscriptions(network, accounts) From 4d5a98df542a2f7dbf2f50a86573542f92ddaff6 Mon Sep 17 00:00:00 2001 From: MichaelKostroma Date: Thu, 24 Oct 2024 14:49:37 +0300 Subject: [PATCH 03/10] feat: Removed balance checker for quai native tokens and replaced Set on Array in activeSubscriptions --- background/main.ts | 9 --------- background/services/chain/index.ts | 17 ++++++++++------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/background/main.ts b/background/main.ts index f10f699e..935773e8 100644 --- a/background/main.ts +++ b/background/main.ts @@ -577,21 +577,12 @@ export default class Main extends BaseService { asset.contractAddress ) ).amount - } else if (isBuiltInNetworkBaseAsset(asset, selectedAccount.network)) { - newBalance = ( - await this.chainService.getLatestBaseAccountBalance(selectedAccount) - ).assetAmount.amount } else { logger.error( `Unknown asset type for balance checker, asset: ${asset.symbol}` ) continue } - // isSmartContractFungibleAsset(asset) - // ? logger.info( - // `Balance checker: ${asset.symbol} ${newBalance} ${asset.contractAddress}` - // ) - // : logger.info(`Balance checker: ${asset.symbol} ${newBalance}`) if (newBalance > amount && !this.keyringService.isLocked()) { const parsedAmount = bigIntToDecimal(newBalance - amount) diff --git a/background/services/chain/index.ts b/background/services/chain/index.ts index b1f6b8fc..6e3497bb 100644 --- a/background/services/chain/index.ts +++ b/background/services/chain/index.ts @@ -26,7 +26,7 @@ import { AssetTransfer, SmartContractFungibleAsset, } from "../../assets" -import { HOUR, MAILBOX_CONTRACT_ADDRESS, MINUTE, QI, QUAI } from "../../constants" +import { HOUR, MAILBOX_CONTRACT_ADDRESS, MINUTE, QI } from "../../constants" import PreferenceService from "../preferences" import { ServiceCreatorFunction, ServiceLifecycleEvents } from "../types" import { ChainDatabase, initializeChainDatabase } from "./db" @@ -118,7 +118,7 @@ export default class ChainService extends BaseService { public supportedNetworks = PELAGUS_NETWORKS - private activeSubscriptions: Map> = new Map() + private activeSubscriptions: Map = new Map() subscribedAccounts: { account: string @@ -277,9 +277,9 @@ export default class ChainService extends BaseService { accounts: AddressOnNetwork[] ) { const subscribedAccounts = - this.activeSubscriptions.get(network.chainID) || new Set() + this.activeSubscriptions.get(network.chainID) || [] - accounts.forEach((account) => subscribedAccounts.add(account.address)) + accounts.forEach((account) => subscribedAccounts.push(account.address)) this.activeSubscriptions.set(network.chainID, subscribedAccounts) } @@ -348,11 +348,14 @@ export default class ChainService extends BaseService { ) { console.log(`New account created on network: ${network.chainID}`) - const subscribedAccounts = this.activeSubscriptions.get(network.chainID) + const subscribedAccountsOnNetwork = this.activeSubscriptions.get( + network.chainID + ) - if (subscribedAccounts) { + if (subscribedAccountsOnNetwork) { this.subscribeOnBalances(network, [newAccount]) - subscribedAccounts.add(newAccount.address) + subscribedAccountsOnNetwork.push(newAccount.address) + return } From 87a92c5a79160e3379a95b823be34fe7ddb00ae7 Mon Sep 17 00:00:00 2001 From: Artem Burakov Date: Thu, 24 Oct 2024 16:13:27 +0300 Subject: [PATCH 04/10] uses events in chain service for balance updates --- background/main.ts | 8 +- background/services/chain/index.ts | 161 ++++++++++++----------------- 2 files changed, 73 insertions(+), 96 deletions(-) diff --git a/background/main.ts b/background/main.ts index 935773e8..eae3b7f6 100644 --- a/background/main.ts +++ b/background/main.ts @@ -402,7 +402,7 @@ export default class Main extends BaseService { * A promise to the indexing service, keeping track of token balances and * prices. The promise will be resolved when the service is initialized. */ - private indexingService: IndexingService, + public indexingService: IndexingService, /** * A promise to the keyring service, which stores key material, derives * accounts, and signs messagees and transactions. The promise will be @@ -1936,6 +1936,12 @@ export default class Main extends BaseService { index, zone, }) + await this.chainService.onNewQiAccountCreated({ + address, + account, + index, + zone, + }) return { address, account, index, zone } } diff --git a/background/services/chain/index.ts b/background/services/chain/index.ts index 6e3497bb..e77fa90e 100644 --- a/background/services/chain/index.ts +++ b/background/services/chain/index.ts @@ -17,11 +17,11 @@ import { HexString, UNIXTime } from "../../types" import { AccountBalance, AddressOnNetwork, + QiCoinbaseAddress, QiWalletBalance, QiWalletOnNetwork, } from "../../accounts" import { - AnyAsset, AnyAssetAmount, AssetTransfer, SmartContractFungibleAsset, @@ -38,10 +38,6 @@ import KeyringService from "../keyring" import type { ValidatedAddEthereumChainParameter } from "../provider-bridge/utils" import { Outpoint } from "quais/lib/commonjs/transaction/utxo" import { MAILBOX_INTERFACE } from "../../contracts/payment-channel-mailbox" -import { - updateAccountBalance, - updateUtxoAccountsBalances, -} from "../../redux-slices/accounts" // The number of blocks to query at a time for historic asset transfers. // Unfortunately there's no "right" answer here that works well across different @@ -270,6 +266,10 @@ export default class ChainService extends BaseService { ) this.subscribeOnBalances(this.selectedNetwork, accounts) this.trackSubscriptions(this.selectedNetwork, accounts) + + const qiAccounts = + await globalThis.main.indexingService.getQiCoinbaseAddresses() + this.subscribeOnQiBalances(this.selectedNetwork, qiAccounts) } private trackSubscriptions( @@ -298,41 +298,68 @@ export default class ChainService extends BaseService { logger.error("WebSocketProvider for balance subscription not found") accounts.forEach(({ address }) => { - webSocketProvider.on({ type: "balance", address }, (balance) => { - console.log("new balance for account", address, balance) - this.updateQuaiBalanceCallback({ - network, - balance, + webSocketProvider.on({ type: "balance", address }, async (balance) => { + const asset = await this.db.getBaseAssetForNetwork(network.chainID) + const accountBalance: AccountBalance = { address, - asset: network.baseAsset, + network, + assetAmount: { + asset, + amount: balance ?? toBigInt(0), + }, + dataSource: "local", + retrievedAt: Date.now(), + } + this.emitter.emit("accountsWithBalances", { + balances: [accountBalance], + addressOnNetwork: { + address, + network, + }, }) + await this.db.addBalance(accountBalance) }) }) } - private async subscribeOnQiBalances() { + private async subscribeOnQiBalances( + network: NetworkInterface, + accounts: QiCoinbaseAddress[] + ) { const qiWallet = await this.keyringService.getQiHDWallet() - qiWallet.connect(this.jsonRpcProvider) - const addressesForZone = qiWallet.getAddressesForZone(Zone.Cyprus1) - const paymentCode = qiWallet.getPaymentCode(0) - - if (!addressesForZone?.length) return - - this.supportedNetworks.map((supportedNetwork) => { - addressesForZone.map((qiAddress) => { - this.webSocketProvider.on( - { type: "balance", address: qiAddress.address }, - (balance) => { - const zoneBalance = qiWallet.getBalanceForZone(Zone.Cyprus1) - - console.log("new balance for account", paymentCode, balance) - this.updateQiBalanceCallback({ - network: supportedNetwork, - balance: zoneBalance, - paymentCode, - asset: QI, // TO WHICH ASSET AMOUNT UPDATED // - }) - } + const paymentCode = qiWallet.getPaymentCode() + const { webSocketProvider } = this.providerFactory.getProvidersForNetwork( + network.chainID + ) + if (!webSocketProvider) { + logger.error("WebSocketProvider for balance subscription not found") + } + + accounts.forEach(({ address }) => { + webSocketProvider.on({ type: "balance", address }, async (balance) => { + const qiWalletBalance: QiWalletBalance = { + paymentCode, + network, + assetAmount: { + asset: QI, + amount: balance, + }, + dataSource: "local", + retrievedAt: Date.now(), + } + + this.emitter.emit("updatedQiLedgerBalance", { + balances: [qiWalletBalance], + addressOnNetwork: { + paymentCode, + network, + }, + }) + await this.db.addQiLedgerBalance(qiWalletBalance) + await qiWallet.scan(Zone.Cyprus1) + await this.keyringService.vaultManager.add( + { qiHDWallet: qiWallet.serialize() }, + {} ) }) }) @@ -342,6 +369,12 @@ export default class ChainService extends BaseService { return this.activeSubscriptions.has(network.chainID) } + public async onNewQiAccountCreated( + qiCoinbaseAddress: QiCoinbaseAddress + ): Promise { + this.subscribeOnQiBalances(this.selectedNetwork, [qiCoinbaseAddress]) + } + public async onNewAccountCreated( network: NetworkInterface, newAccount: AddressOnNetwork @@ -371,68 +404,6 @@ export default class ChainService extends BaseService { } } - public async updateQuaiBalanceCallback({ - network, - address, - balance, - asset, - }: { - network: NetworkInterface - address: string - balance: bigint - asset: AnyAsset - }) { - globalThis.main.store.dispatch( - updateAccountBalance({ - balances: [ - { - address, - assetAmount: { - amount: balance, - asset, - }, - network, - retrievedAt: Date.now(), - dataSource: "local", - }, - ], - addressOnNetwork: { - address, - network, - }, - }) - ) - } - - public async updateQiBalanceCallback({ - network, - paymentCode, - balance, - asset, - }: { - network: NetworkInterface - paymentCode: string - balance: bigint - asset: AnyAsset - }) { - globalThis.main.store.dispatch( - updateUtxoAccountsBalances({ - balances: [ - { - paymentCode, - network, - assetAmount: { - asset, - amount: balance, - }, - dataSource: "local", - retrievedAt: Date.now(), - }, - ], - }) - ) - } - private subscribeOnNetworksAndAddresses = async ( networks: NetworkInterface[], accounts: AddressOnNetwork[] From c3705d2910da87caf1bf8939e4b277ade07860e5 Mon Sep 17 00:00:00 2001 From: MichaelKostroma Date: Thu, 24 Oct 2024 18:05:18 +0300 Subject: [PATCH 05/10] fix: Wrong balance fetching --- background/redux-slices/convertAssets.ts | 1 - background/services/chain/index.ts | 10 ++++------ background/services/chain/utils/asset-data-helper.ts | 6 +++--- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/background/redux-slices/convertAssets.ts b/background/redux-slices/convertAssets.ts index 1b61c7ba..b50106e1 100644 --- a/background/redux-slices/convertAssets.ts +++ b/background/redux-slices/convertAssets.ts @@ -87,7 +87,6 @@ export const setConvertRateHandle = createBackgroundAsyncThunk( return } - // console.log("10 Quai", parseQuai("10")) rate = await jsonRpcProvider.getLatestQuaiRate( Zone.Cyprus1, parseQuai("10") diff --git a/background/services/chain/index.ts b/background/services/chain/index.ts index e77fa90e..b1d91f66 100644 --- a/background/services/chain/index.ts +++ b/background/services/chain/index.ts @@ -253,7 +253,6 @@ export default class ChainService extends BaseService { // -------------------------------------------------------------------------------------------------- private async startAddressBalanceSubscriber(): Promise { - console.log(this.activeSubscriptions) if (this.isNetworkSubscribed(this.selectedNetwork)) { logger.info( `Network ${this.selectedNetwork.chainID} is already subscribed. Skipping subscription.` @@ -288,8 +287,6 @@ export default class ChainService extends BaseService { network: NetworkInterface, accounts: AddressOnNetwork[] ): Promise { - console.log("subscribing to accounts balances", accounts, network) - const { webSocketProvider } = this.providerFactory.getProvidersForNetwork( network.chainID ) @@ -379,8 +376,6 @@ export default class ChainService extends BaseService { network: NetworkInterface, newAccount: AddressOnNetwork ) { - console.log(`New account created on network: ${network.chainID}`) - const subscribedAccountsOnNetwork = this.activeSubscriptions.get( network.chainID ) @@ -531,7 +526,10 @@ export default class ChainService extends BaseService { let balance: bigint | undefined = toBigInt(0) try { - balance = await this.jsonRpcProvider.getBalance(address, "latest") + const { jsonRpcProvider } = this.providerFactory.getProvidersForNetwork( + network.chainID + ) + balance = await jsonRpcProvider.getBalance(address, "latest") } catch (error) { if (error instanceof Error) { logger.error("Error getting balance for address", address, error) diff --git a/background/services/chain/utils/asset-data-helper.ts b/background/services/chain/utils/asset-data-helper.ts index 77162114..d4b9ad3f 100644 --- a/background/services/chain/utils/asset-data-helper.ts +++ b/background/services/chain/utils/asset-data-helper.ts @@ -32,7 +32,7 @@ export default class AssetDataHelper { ): Promise { const prevShard = globalThis.main.GetShard() globalThis.main.SetShard(getExtendedZoneForAddress(smartContractAddress)) - const provider = this.providerTracker + const provider = globalThis.main.chainService.jsonRpcProvider if (!provider) { throw logger.buildError( @@ -64,7 +64,7 @@ export default class AssetDataHelper { globalThis.main.SetShard( getExtendedZoneForAddress(addressOnNetwork.address) ) - const provider = this.providerTracker + const provider = globalThis.main.chainService.jsonRpcProvider if (!provider) throw new Error("Failed get provider for network") globalThis.main.SetShard(prevShard) @@ -93,7 +93,7 @@ export default class AssetDataHelper { async getTokenMetadata( tokenSmartContract: SmartContract ): Promise { - const provider = this.providerTracker + const provider = globalThis.main.chainService.jsonRpcProvider if (!provider) throw new Error("Failed get provider for network") return getERC20Metadata(provider, tokenSmartContract) From 834eb88077cc11e9873dac0547f79480aeddbeed Mon Sep 17 00:00:00 2001 From: Artem Burakov Date: Wed, 23 Oct 2024 13:48:07 +0300 Subject: [PATCH 06/10] feat: Added updateQiBalanceCallback & draft subscribeOnQiBalances --- background/services/chain/index.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/background/services/chain/index.ts b/background/services/chain/index.ts index b1d91f66..55ac7d14 100644 --- a/background/services/chain/index.ts +++ b/background/services/chain/index.ts @@ -22,11 +22,12 @@ import { QiWalletOnNetwork, } from "../../accounts" import { + AnyAsset, AnyAssetAmount, AssetTransfer, SmartContractFungibleAsset, } from "../../assets" -import { HOUR, MAILBOX_CONTRACT_ADDRESS, MINUTE, QI } from "../../constants" +import { HOUR, MAILBOX_CONTRACT_ADDRESS, MINUTE, QI, QUAI } from "../../constants" import PreferenceService from "../preferences" import { ServiceCreatorFunction, ServiceLifecycleEvents } from "../types" import { ChainDatabase, initializeChainDatabase } from "./db" @@ -38,6 +39,10 @@ import KeyringService from "../keyring" import type { ValidatedAddEthereumChainParameter } from "../provider-bridge/utils" import { Outpoint } from "quais/lib/commonjs/transaction/utxo" import { MAILBOX_INTERFACE } from "../../contracts/payment-channel-mailbox" +import { + updateAccountBalance, + updateUtxoAccountsBalances, +} from "../../redux-slices/accounts" // The number of blocks to query at a time for historic asset transfers. // Unfortunately there's no "right" answer here that works well across different From b4717aa135e3ca383b6ca3ee4c3db97dcd8c4199 Mon Sep 17 00:00:00 2001 From: MichaelKostroma Date: Thu, 24 Oct 2024 14:49:37 +0300 Subject: [PATCH 07/10] feat: Removed balance checker for quai native tokens and replaced Set on Array in activeSubscriptions --- background/services/chain/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/background/services/chain/index.ts b/background/services/chain/index.ts index 55ac7d14..2ba88d3a 100644 --- a/background/services/chain/index.ts +++ b/background/services/chain/index.ts @@ -27,7 +27,7 @@ import { AssetTransfer, SmartContractFungibleAsset, } from "../../assets" -import { HOUR, MAILBOX_CONTRACT_ADDRESS, MINUTE, QI, QUAI } from "../../constants" +import { HOUR, MAILBOX_CONTRACT_ADDRESS, MINUTE, QI } from "../../constants" import PreferenceService from "../preferences" import { ServiceCreatorFunction, ServiceLifecycleEvents } from "../types" import { ChainDatabase, initializeChainDatabase } from "./db" From 44fd3d12a040c81be90a3f9cea281f7f0b151d62 Mon Sep 17 00:00:00 2001 From: Artem Burakov Date: Thu, 24 Oct 2024 16:46:45 +0300 Subject: [PATCH 08/10] adds quai and qi callback functions & minor improvements --- background/main.ts | 51 ---------- background/services/chain/index.ts | 153 ++++++++++++++++------------- 2 files changed, 87 insertions(+), 117 deletions(-) diff --git a/background/main.ts b/background/main.ts index eae3b7f6..6b4ba51a 100644 --- a/background/main.ts +++ b/background/main.ts @@ -39,7 +39,6 @@ import { AddressOnNetwork, NameOnNetwork, QiCoinbaseAddress, - QiWalletBalance, } from "./accounts" import rootReducer from "./redux-slices" import { @@ -168,7 +167,6 @@ import { LocalNodeNetworkStatusEventTypes } from "./services/provider-factory/ev import NotificationsManager from "./services/notifications" import BlockService from "./services/block" import TransactionService from "./services/transactions" -import { QI } from "./constants" // This sanitizer runs on store and action data before serializing for remote // redux devtools. The goal is to end up with an object that is directly @@ -494,54 +492,6 @@ export default class Main extends BaseService { await this.store.dispatch(setNewNetworkConnectError(chainIdWithError)) } - async startQiMiningAddressBalanceChecker(): Promise { - const interval = setInterval(async () => { - const qiMiningAddresses = - await this.indexingService.getQiCoinbaseAddresses() - const allOutpoints = ( - await Promise.all( - qiMiningAddresses.map(async (qiAddress) => { - const outpoints = await this.chainService.getOutpointsForQiAddress( - qiAddress.address - ) - return outpoints.map((outpoint) => ({ - outpoint, - address: qiAddress.address, - account: qiAddress.account, - zone: Zone.Cyprus1, - })) - }) - ) - ).flat() - - if (allOutpoints.length === 0) return - - const qiWallet = await this.keyringService.getQiHDWallet() - qiWallet.importOutpoints(allOutpoints) - const serializedQiHDWallet = qiWallet.serialize() - await this.keyringService.vaultManager.add( - { qiHDWallet: serializedQiHDWallet }, - {} - ) - - const qiWalletBalance: QiWalletBalance = { - paymentCode: qiWallet.getPaymentCode(0), - network: this.chainService.selectedNetwork, - assetAmount: { - asset: QI, - amount: qiWallet.getBalanceForZone(Zone.Cyprus1), - }, - dataSource: "local", - retrievedAt: Date.now(), - } - - this.store.dispatch( - updateUtxoAccountsBalances({ balances: [qiWalletBalance] }) - ) - }, 30000) - this.qiMiningAddressBalanceChecker = interval - } - async startBalanceChecker(): Promise { const interval = setInterval(async () => { if (!walletOpen) return @@ -699,7 +649,6 @@ export default class Main extends BaseService { this.signingService.startService(), this.analyticsService.startService(), this.startBalanceChecker(), - this.startQiMiningAddressBalanceChecker(), ] await Promise.all(independentServices) diff --git a/background/services/chain/index.ts b/background/services/chain/index.ts index 2ba88d3a..a93b788a 100644 --- a/background/services/chain/index.ts +++ b/background/services/chain/index.ts @@ -3,12 +3,14 @@ /* eslint-disable import/no-cycle */ import { Contract, + isQiAddress, JsonRpcProvider, Shard, toBigInt, WebSocketProvider, Zone, } from "quais" +import { Outpoint } from "quais/lib/commonjs/transaction/utxo" import { PELAGUS_NETWORKS } from "../../constants/networks/networks" import ProviderFactory from "../provider-factory/provider-factory" import { NetworkInterface } from "../../constants/networks/networkTypes" @@ -22,7 +24,6 @@ import { QiWalletOnNetwork, } from "../../accounts" import { - AnyAsset, AnyAssetAmount, AssetTransfer, SmartContractFungibleAsset, @@ -37,12 +38,7 @@ import { sameQuaiAddress } from "../../lib/utils" import AssetDataHelper from "./utils/asset-data-helper" import KeyringService from "../keyring" import type { ValidatedAddEthereumChainParameter } from "../provider-bridge/utils" -import { Outpoint } from "quais/lib/commonjs/transaction/utxo" import { MAILBOX_INTERFACE } from "../../contracts/payment-channel-mailbox" -import { - updateAccountBalance, - updateUtxoAccountsBalances, -} from "../../redux-slices/accounts" // The number of blocks to query at a time for historic asset transfers. // Unfortunately there's no "right" answer here that works well across different @@ -273,7 +269,7 @@ export default class ChainService extends BaseService { const qiAccounts = await globalThis.main.indexingService.getQiCoinbaseAddresses() - this.subscribeOnQiBalances(this.selectedNetwork, qiAccounts) + await this.subscribeOnBalances(this.selectedNetwork, qiAccounts) } private trackSubscriptions( @@ -290,7 +286,7 @@ export default class ChainService extends BaseService { private async subscribeOnBalances( network: NetworkInterface, - accounts: AddressOnNetwork[] + accounts: AddressOnNetwork[] | QiCoinbaseAddress[] ): Promise { const { webSocketProvider } = this.providerFactory.getProvidersForNetwork( network.chainID @@ -300,71 +296,96 @@ export default class ChainService extends BaseService { logger.error("WebSocketProvider for balance subscription not found") accounts.forEach(({ address }) => { - webSocketProvider.on({ type: "balance", address }, async (balance) => { - const asset = await this.db.getBaseAssetForNetwork(network.chainID) - const accountBalance: AccountBalance = { - address, - network, - assetAmount: { - asset, - amount: balance ?? toBigInt(0), - }, - dataSource: "local", - retrievedAt: Date.now(), + webSocketProvider.on( + { type: "balance", address }, + async (balance: bigint) => { + if (isQiAddress(address)) { + await this.handleQiMiningAddressBalanceUpdate(network) + } else { + await this.handleQuaiAddressBalanceUpdate(network, address, balance) + } } - this.emitter.emit("accountsWithBalances", { - balances: [accountBalance], - addressOnNetwork: { - address, - network, - }, - }) - await this.db.addBalance(accountBalance) - }) + ) }) } - private async subscribeOnQiBalances( + async handleQuaiAddressBalanceUpdate( network: NetworkInterface, - accounts: QiCoinbaseAddress[] - ) { - const qiWallet = await this.keyringService.getQiHDWallet() - const paymentCode = qiWallet.getPaymentCode() - const { webSocketProvider } = this.providerFactory.getProvidersForNetwork( - network.chainID - ) - if (!webSocketProvider) { - logger.error("WebSocketProvider for balance subscription not found") + address: string, + balance: bigint + ): Promise { + const asset = await this.db.getBaseAssetForNetwork(network.chainID) + const accountBalance: AccountBalance = { + address, + network, + assetAmount: { + asset, + amount: balance ?? toBigInt(0), + }, + dataSource: "local", + retrievedAt: Date.now(), } + this.emitter.emit("accountsWithBalances", { + balances: [accountBalance], + addressOnNetwork: { + address, + network, + }, + }) + await this.db.addBalance(accountBalance) + } - accounts.forEach(({ address }) => { - webSocketProvider.on({ type: "balance", address }, async (balance) => { - const qiWalletBalance: QiWalletBalance = { - paymentCode, - network, - assetAmount: { - asset: QI, - amount: balance, - }, - dataSource: "local", - retrievedAt: Date.now(), - } - - this.emitter.emit("updatedQiLedgerBalance", { - balances: [qiWalletBalance], - addressOnNetwork: { - paymentCode, - network, - }, + async handleQiMiningAddressBalanceUpdate( + network: NetworkInterface + ): Promise { + const qiMiningAddresses = + await globalThis.main.indexingService.getQiCoinbaseAddresses() + const allOutpoints = ( + await Promise.all( + qiMiningAddresses.map(async (qiAddress) => { + const outpoints = await this.getOutpointsForQiAddress( + qiAddress.address + ) + return outpoints.map((outpoint) => ({ + outpoint, + address: qiAddress.address, + account: qiAddress.account, + zone: Zone.Cyprus1, + })) }) - await this.db.addQiLedgerBalance(qiWalletBalance) - await qiWallet.scan(Zone.Cyprus1) - await this.keyringService.vaultManager.add( - { qiHDWallet: qiWallet.serialize() }, - {} - ) - }) + ) + ).flat() + + if (allOutpoints.length === 0) return + + const qiWallet = await this.keyringService.getQiHDWallet() + const paymentCode = qiWallet.getPaymentCode(0) + qiWallet.importOutpoints(allOutpoints) + + const serializedQiHDWallet = qiWallet.serialize() + await this.keyringService.vaultManager.update({ + qiHDWallet: serializedQiHDWallet, + }) + + const qiWalletBalance: QiWalletBalance = { + paymentCode, + network, + assetAmount: { + asset: QI, + amount: qiWallet.getBalanceForZone(Zone.Cyprus1), + }, + dataSource: "local", + retrievedAt: Date.now(), + } + + this.emitter.emit("updatedQiLedgerBalance", { + balances: [qiWalletBalance], + addressOnNetwork: { + paymentCode, + network, + }, }) + await this.db.addQiLedgerBalance(qiWalletBalance) } private isNetworkSubscribed(network: NetworkInterface): boolean { @@ -374,7 +395,7 @@ export default class ChainService extends BaseService { public async onNewQiAccountCreated( qiCoinbaseAddress: QiCoinbaseAddress ): Promise { - this.subscribeOnQiBalances(this.selectedNetwork, [qiCoinbaseAddress]) + await this.subscribeOnBalances(this.selectedNetwork, [qiCoinbaseAddress]) } public async onNewAccountCreated( @@ -512,7 +533,7 @@ export default class ChainService extends BaseService { } async getOutpointsForQiAddress(address: string): Promise { - return await this.jsonRpcProvider.getOutpointsByAddress(address) + return this.jsonRpcProvider.getOutpointsByAddress(address) } async getLatestBaseAccountBalance({ From 7af6aaa1e4a504a6612105a19660047a814d9788 Mon Sep 17 00:00:00 2001 From: Artem Burakov Date: Thu, 24 Oct 2024 19:00:16 +0300 Subject: [PATCH 09/10] improves the process of subscribing to account balance updates --- background/services/chain/index.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/background/services/chain/index.ts b/background/services/chain/index.ts index a93b788a..12dad636 100644 --- a/background/services/chain/index.ts +++ b/background/services/chain/index.ts @@ -237,7 +237,7 @@ export default class ChainService extends BaseService { await this.startAddressBalanceSubscriber() } - public async switchNetwork(network: NetworkInterface): Promise { + public switchNetwork(network: NetworkInterface): void { const { jsonRpcProvider, webSocketProvider } = this.providerFactory.getProvidersForNetwork(network.chainID) @@ -254,22 +254,25 @@ export default class ChainService extends BaseService { // -------------------------------------------------------------------------------------------------- private async startAddressBalanceSubscriber(): Promise { - if (this.isNetworkSubscribed(this.selectedNetwork)) { + const { selectedNetwork } = this + if (this.isNetworkSubscribed(selectedNetwork)) { logger.info( - `Network ${this.selectedNetwork.chainID} is already subscribed. Skipping subscription.` + `Network ${selectedNetwork.chainID} is already subscribed. Skipping subscription.` ) return } - const accounts = await this.getTrackedAddressesOnNetwork( - this.selectedNetwork - ) - this.subscribeOnBalances(this.selectedNetwork, accounts) - this.trackSubscriptions(this.selectedNetwork, accounts) + const [quaiAccounts, qiAccounts] = await Promise.all([ + this.getTrackedAddressesOnNetwork(selectedNetwork), + globalThis.main.indexingService.getQiCoinbaseAddresses(), + ]) - const qiAccounts = - await globalThis.main.indexingService.getQiCoinbaseAddresses() - await this.subscribeOnBalances(this.selectedNetwork, qiAccounts) + await Promise.all([ + this.subscribeOnBalances(selectedNetwork, quaiAccounts), + this.subscribeOnBalances(selectedNetwork, qiAccounts), + ]) + + this.trackSubscriptions(selectedNetwork, quaiAccounts) } private trackSubscriptions( From f0c1ac29781435869b23d45f265ef44f9a6eab66 Mon Sep 17 00:00:00 2001 From: Artem Burakov Date: Fri, 25 Oct 2024 17:36:48 +0300 Subject: [PATCH 10/10] removes unused timeout --- background/main.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/background/main.ts b/background/main.ts index 6b4ba51a..b200f4d4 100644 --- a/background/main.ts +++ b/background/main.ts @@ -284,8 +284,6 @@ export default class Main extends BaseService { balanceChecker: NodeJS.Timeout - qiMiningAddressBalanceChecker: NodeJS.Timeout - static create: ServiceCreatorFunction = async () => { const preferenceService = PreferenceService.create() const providerFactoryService = ProviderFactory.create(preferenceService) @@ -674,7 +672,6 @@ export default class Main extends BaseService { this.transactionService.stopService(), this.blockService.stopService(), clearInterval(this.balanceChecker), - clearInterval(this.qiMiningAddressBalanceChecker), ] await Promise.all(servicesToBeStopped)