Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Balance subscriptions and manual refresh #397

Merged
merged 2 commits into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions background/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@ export type QiWalletBalance = {
dataSource: "local"
}

export type QiLedgerLastFullScan = {
chainID: string
retrievedAt: number
}

export type QiCoinbaseAddressBalance = {
address: string
balance: bigint
retrievedAt: number
chainID: string
dataSource: "local"
}

/**
* An address on a particular network. That's it. That's the comment.
*/
Expand Down
14 changes: 14 additions & 0 deletions background/redux-slices/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -600,3 +600,17 @@ export const addQiCoinbaseAddress = createBackgroundAsyncThunk(
)
}
)

export const triggerManualBalanceUpdate = createBackgroundAsyncThunk(
"account/triggerManualBalanceUpdate",
async (_, { getState, extra: { main } }) => {
const state = getState() as RootState
const uiState = state.ui
const isUtxoSelected = uiState.isUtxoSelected
if (isUtxoSelected) {
await main.chainService.syncQiWallet(true)
} else {
await main.manuallyCheckBalances()
}
}
)
3 changes: 1 addition & 2 deletions background/redux-slices/selectors/uiSelectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,8 @@ export const selectQiBalanceForCurrentUtxoAccountCyprus1 = createSelector(
(state: RootState) => state.ui.selectedUtxoAccount?.balances,
(balances) => {
const amount = balances?.[Zone.Cyprus1]?.assetAmount?.amount

return amount !== undefined && amount !== null && !isNaN(Number(amount))
? Number(formatQi(amount)).toFixed(3)
? formatQi(amount)
: null
}
)
64 changes: 64 additions & 0 deletions background/services/chain/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { UNIXTime } from "../../types"
import {
AccountBalance,
AddressOnNetwork,
QiCoinbaseAddressBalance,
QiLedgerLastFullScan,
QiWalletBalance,
} from "../../accounts"
import { NetworkBaseAsset } from "../../networks"
Expand Down Expand Up @@ -52,6 +54,13 @@ export class ChainDatabase extends Dexie {

private baseAssets!: Dexie.Table<NetworkBaseAsset, string>

private qiCoinbaseAddressBalances!: Dexie.Table<
QiCoinbaseAddressBalance,
number
>

private qiLedgerLastFullScan!: Dexie.Table<QiLedgerLastFullScan, string>

constructor(options?: DexieOptions) {
super("pelagus/chain", options)
this.version(1).stores({
Expand All @@ -71,6 +80,12 @@ export class ChainDatabase extends Dexie {
qiLedgerBalance:
"[paymentCode+assetAmount.asset.symbol+network.chainID],paymentCode,assetAmount.amount,assetAmount.asset.symbol,network.baseAsset.name,blockHeight,retrievedAt",
})

this.version(3).stores({
qiCoinbaseAddressBalances:
"&[address+chainID],address,chainID,balance,retrievedAt,dataSource",
// qiLedgerLastFullScan: "&chainID,retrievedAt",
})
}

async initialize(): Promise<void> {
Expand Down Expand Up @@ -251,6 +266,55 @@ export class ChainDatabase extends Dexie {
)[0] ?? null
)
}

async setQiCoinbaseAddressBalance(balance: {
address: string
balance: bigint
chainID: string
}): Promise<void> {
await this.qiCoinbaseAddressBalances.put({
...balance,
retrievedAt: Date.now(),
dataSource: "local",
})
}

async getQiCoinbaseAddressBalance(
address: string,
chainID: string
): Promise<bigint> {
return BigInt(
(
await this.qiCoinbaseAddressBalances
.where("[address+chainID]")
.equals([address, chainID])
.toArray()
)[0]?.balance ?? "0"
)
}

async getQiCoinbaseAddressBalances(
chainID: string
): Promise<QiCoinbaseAddressBalance[]> {
const balances = await this.qiCoinbaseAddressBalances
.where("chainID")
.equals(chainID)
.toArray()
return balances
}

async setQiLedgerLastFullScan(chainID: string): Promise<void> {
await this.qiLedgerLastFullScan.put({
chainID,
retrievedAt: Date.now(),
})
}

async getQiLedgerLastFullScan(
chainID: string
): Promise<QiLedgerLastFullScan | undefined> {
return this.qiLedgerLastFullScan.get(chainID)
}
}

export function initializeChainDatabase(options?: DexieOptions): ChainDatabase {
Expand Down
94 changes: 63 additions & 31 deletions background/services/chain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ import AssetDataHelper from "./utils/asset-data-helper"
import KeyringService from "../keyring"
import type { ValidatedAddEthereumChainParameter } from "../provider-bridge/utils"
import { MAILBOX_INTERFACE } from "../../contracts/payment-channel-mailbox"
import { OutpointInfo } from "quais/lib/commonjs/wallet/qi-hdwallet"
import NotificationsManager from "../notifications"
import { bigIntToDecimal } from "../../redux-slices/utils/asset-utils"

// 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
Expand Down Expand Up @@ -198,7 +201,7 @@ export default class ChainService extends BaseService<Events> {
},
qiWalletSync: {
schedule: {
periodInMinutes: 1,
periodInMinutes: 5,
},
handler: () => this.syncQiWallet(),
},
Expand Down Expand Up @@ -303,7 +306,7 @@ export default class ChainService extends BaseService<Events> {
{ type: "balance", address },
async (balance: bigint) => {
if (isQiAddress(address)) {
await this.handleQiMiningAddressBalanceUpdate(network)
await this.handleQiMiningAddressBalanceUpdate(network, address)
} else {
await this.handleQuaiAddressBalanceUpdate(network, address, balance)
}
Expand All @@ -328,6 +331,35 @@ export default class ChainService extends BaseService<Events> {
dataSource: "local",
retrievedAt: Date.now(),
}

//get current selected account balance and compare to get amount of incoming assets
const selectedAccount = await this.preferenceService.getSelectedAccount()
const currentAccountState =
globalThis.main.store.getState().account.accountsData.evm[
selectedAccount.network.chainID
]?.[selectedAccount.address]
const currentNetworkChainID = selectedAccount.network.chainID

if (currentAccountState === "loading") return

const currentBalanceAmount =
currentAccountState?.balances["QUAI"].assetAmount.amount

// show this is the current network is selected
if (
currentBalanceAmount &&
balance > currentBalanceAmount &&
currentNetworkChainID === network.chainID &&
!this.keyringService.isLocked()
) {
const parsedAmount = bigIntToDecimal(balance - currentBalanceAmount)
NotificationsManager.createIncomingAssetsNotification(
parsedAmount,
asset.symbol,
address
)
}

this.emitter.emit("accountsWithBalances", {
balances: [accountBalance],
addressOnNetwork: {
Expand All @@ -339,31 +371,22 @@ export default class ChainService extends BaseService<Events> {
}

async handleQiMiningAddressBalanceUpdate(
network: NetworkInterface
network: NetworkInterface,
address: string
): Promise<void> {
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,
}))
})
)
).flat()
const outpointReqs = await this.getOutpointsForQiAddress(address)
const outpoints = outpointReqs.map((outpoint) => ({
outpoint,
address,
account: 0,
zone: Zone.Cyprus1,
}))

if (allOutpoints.length === 0) return
if (outpoints.length === 0) return

const qiWallet = await this.keyringService.getQiHDWallet()
const paymentCode = qiWallet.getPaymentCode(0)
qiWallet.importOutpoints(allOutpoints)
qiWallet.importOutpoints(outpoints)

const serializedQiHDWallet = qiWallet.serialize()
await this.keyringService.vaultManager.update({
Expand Down Expand Up @@ -474,7 +497,11 @@ export default class ChainService extends BaseService<Events> {
await this.db.removeAccountToTrack(address)
}

async syncQiWallet(): Promise<void> {
async getOutpointsForQAddresses(address: string): Promise<Outpoint[]> {
return await this.jsonRpcProvider.getOutpointsByAddress(address)
}

async syncQiWallet(forceFullScan = false): Promise<void> {
try {
const network = this.selectedNetwork
const qiWallet = await this.keyringService.getQiHDWallet()
Expand All @@ -500,17 +527,14 @@ export default class ChainService extends BaseService<Events> {
qiWallet.openChannel(paymentCode)
})

await qiWallet.sync(Zone.Cyprus1)
if (forceFullScan) {
await qiWallet.scan(Zone.Cyprus1)
} else {
await qiWallet.sync(Zone.Cyprus1)
}

const balance = qiWallet.getBalanceForZone(Zone.Cyprus1)

await this.keyringService.vaultManager.add(
{
qiHDWallet: qiWallet.serialize(),
},
{}
)

const qiWalletBalance: QiWalletBalance = {
paymentCode,
network,
Expand All @@ -529,7 +553,15 @@ export default class ChainService extends BaseService<Events> {
network,
},
})

await this.db.addQiLedgerBalance(qiWalletBalance)

await this.keyringService.vaultManager.add(
{
qiHDWallet: qiWallet.serialize(),
},
{}
)
} catch (error) {
logger.error("Error getting qi wallet balance for address", error)
}
Expand Down
13 changes: 13 additions & 0 deletions background/services/indexing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -770,4 +770,17 @@ export default class IndexingService extends BaseService<Events> {
async getQiCoinbaseAddresses(): Promise<QiCoinbaseAddress[]> {
return this.db.getQiCoinbaseAddresses()
}

// async persistQiCoinbaseAddressBalance(balance: {
// address: string
// balance: string
// }): Promise<void> {
// await this.chainService.setQiCoinbaseAddressBalance(balance)
// }

// async getQiCoinbaseAddressBalances(): Promise<
// { address: string; balance: string }[]
// > {
// return this.chainService.getQiCoinbaseAddressBalances()
// }
}
Loading