Skip to content

Commit

Permalink
move balance fetch into evm class
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkNerdi committed Apr 30, 2024
1 parent e074490 commit 72d8d95
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 83 deletions.
2 changes: 1 addition & 1 deletion packages/desktop/components/NetworkCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
network.coinType,
$selectedAccount as IAccountState
)
pollEvmBalancesForAccount($activeProfile.id, $selectedAccount as IAccountState)
pollEvmBalancesForAccount($selectedAccount as IAccountState)
if ($activeProfile.type === ProfileType.Ledger) {
$networkConfigRouter.goTo(NetworkConfigRoute.ConfirmLedgerEvmAddress)
}
Expand Down
3 changes: 1 addition & 2 deletions packages/desktop/components/menus/TokenListMenu.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<script lang="ts">
import { activeProfileId } from '@core/profile/stores'
import { showNotification } from '@auxiliary/notification'
import { IconName, Menu } from '@bloomwalletio/ui'
import { localize } from '@core/i18n'
Expand All @@ -10,7 +9,7 @@
let menu: Menu | undefined = undefined
function onSyncTokensClick(): void {
fetchEvmBalancesForAllAccounts($activeProfileId as string, true)
fetchEvmBalancesForAllAccounts(true)
showNotification({
variant: 'success',
text: localize('notifications.syncTokens.success'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { setClipboard } from '@core/utils'
import { getActiveNetworkId, getNetwork, NetworkId, NetworkNamespace } from '@core/network'
import { generateAndStoreEvmAddressForAccounts, pollEvmBalancesForAccount } from '@core/layer-2/actions'
import { activeProfile, activeProfileId } from '@core/profile/stores'
import { activeProfile } from '@core/profile/stores'
import { checkActiveProfileAuth } from '@core/profile/actions'
import { LedgerAppName } from '@core/ledger'
import PopupTemplate from '../PopupTemplate.svelte'
Expand Down Expand Up @@ -42,7 +42,7 @@
try {
await generateAndStoreEvmAddressForAccounts($activeProfile.type, coinType, account)
pollEvmBalancesForAccount($activeProfileId as string, account)
pollEvmBalancesForAccount(account)
updateNetworkNameAndAddress()
} catch (error) {
handleError(error)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
try {
await generateAndStoreEvmAddressForAccounts($activeProfile.type, network.coinType, account)
pollEvmBalancesForAccount($activeProfile.id, account)
pollEvmBalancesForAccount(account)
if ($activeProfile.type === ProfileType.Ledger) {
setSelectedChain(network)
toggleDashboardDrawer({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import { updateAccountForConnectedDapps } from '@auxiliary/wallet-connect/actions'
import { pollEvmBalancesForAccount } from '@core/layer-2/actions/pollEvmBalancesForAccount'
import { activeAccounts, getActiveProfileId, updateActiveProfile } from '@core/profile/stores'
import { activeAccounts, updateActiveProfile } from '@core/profile/stores'
import { clearFilters } from '@core/utils'
import { resetSendOptionIndex } from '@core/wallet/stores'
import { get } from 'svelte/store'
import { selectedAccountIndex } from '../stores'

export function setSelectedAccount(index: number): void {
const activeProfileId = getActiveProfileId()
const account = get(activeAccounts)?.find((_account) => _account.index === index)
if (account) {
selectedAccountIndex.set(index)
updateAccountForConnectedDapps(account)
updateActiveProfile({ lastUsedAccountIndex: index })
clearFilters()
pollEvmBalancesForAccount(activeProfileId, account)
pollEvmBalancesForAccount(account)
resetSendOptionIndex()
} else {
throw new Error(`Account with ID ${index} cannot be found!`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export async function tryCreateAdditionalAccount(alias: string, color: string):
const coinType = getEvmNetworks()[0]?.coinType
if (coinType !== undefined) {
void generateAndStoreEvmAddressForAccounts(activeProfile.type, coinType, account)
void pollEvmBalancesForAccount(activeProfile.id, account)
void pollEvmBalancesForAccount(account)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,75 +1,17 @@
import { IAccountState } from '@core/account/interfaces'
import { ContractType, fetchIscAssetsForAccount } from '@core/layer-2'
import { IEvmNetwork } from '@core/network/interfaces'
import { getEvmNetworks } from '@core/network/stores'
import { updateErc721NftsOwnership } from '@core/nfts/actions'
import { getActiveProfile } from '@core/profile/stores'
import { getOrRequestTokenFromPersistedTokens } from '@core/token/actions'
import { Converter } from '@core/utils/convert'
import { BASE_TOKEN_ID, ITokenBalance, TokenTrackingStatus } from '@core/token'
import features from '@features/features'
import { IError } from '@core/error'
import { isIscChain } from '@core/network/utils/isIscChain'
import { ITokenBalance } from '@core/token'
import { setLayer2AccountBalanceForChain } from '@core/layer-2/stores'

export function fetchEvmBalancesForAccount(profileId: string, account: IAccountState): void {
const { evmAddresses, index } = account
export function fetchEvmBalancesForAccount(account: IAccountState): void {
const evmNetworks = getEvmNetworks()
evmNetworks.forEach(async (evmNetwork) => {
const { coinType, id: networkId } = evmNetwork
const evmAddress = evmAddresses?.[coinType]
if (!evmAddress) {
return
}

if (features.collectibles.erc721.enabled) {
void updateErc721NftsOwnership(account, evmNetwork.id)
}

try {
const tokenBalance = isIscChain(evmNetwork)
? await fetchIscAssetsForAccount(profileId, evmAddress, evmNetwork, account)
: await getEvmBaseCoinBalanceForAddress(evmAddress, evmNetwork)
const tokenBalance: ITokenBalance = (await evmNetwork.getBalance(account)) ?? {}

const erc20Balances = await getErc20BalancesForAddress(evmAddress, evmNetwork)
for (const [tokenId, balance] of Object.entries(erc20Balances)) {
await getOrRequestTokenFromPersistedTokens(tokenId, networkId)
tokenBalance[tokenId] = Number.isNaN(Number(balance)) ? BigInt(0) : balance
}
setLayer2AccountBalanceForChain(index, networkId, tokenBalance)
setLayer2AccountBalanceForChain(account.index, evmNetwork.id, tokenBalance)
} catch (error) {
console.error(error)
}
})
}

async function getEvmBaseCoinBalanceForAddress(address: string, network: IEvmNetwork): Promise<ITokenBalance> {
const rawBalance = await network.provider.eth.getBalance(address)
return { [BASE_TOKEN_ID]: Converter.bigIntLikeToBigInt(rawBalance) }
}

async function getErc20BalancesForAddress(evmAddress: string, evmNetwork: IEvmNetwork): Promise<ITokenBalance> {
const networkId = evmNetwork.id
const coinType = evmNetwork.coinType

const trackedTokens = getActiveProfile()?.trackedTokens?.[networkId] ?? {}
const erc20TokenBalances: ITokenBalance = {}
for (const [erc20Address, trackingStatus] of Object.entries(trackedTokens)) {
try {
if (trackingStatus === TokenTrackingStatus.Untracked) {
continue
}

const contract = evmNetwork?.getContract(ContractType.Erc20, erc20Address)
if (!contract || !coinType) {
continue
}
const rawBalance = await contract.methods.balanceOf(evmAddress).call()
erc20TokenBalances[erc20Address] = Converter.bigIntLikeToBigInt(rawBalance)
} catch (err) {
const error = (err as IError)?.message ?? err
console.error(error)
}
}
return erc20TokenBalances
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import { handleError } from '@core/error/handlers'

let pollInterval: number

export function pollEvmBalancesForAccount(profileId: string, account: IAccountState): void {
export function pollEvmBalancesForAccount(account: IAccountState): void {
try {
clearL2TokensPoll()
checkForUntrackedTokens(account)
void checkForUntrackedNfts(account)
fetchEvmBalancesForAccount(profileId, account)
fetchEvmBalancesForAccount(account)
pollInterval = window.setInterval(() => {
fetchEvmBalancesForAccount(profileId, account)
fetchEvmBalancesForAccount(account)
}, LAYER2_TOKENS_POLL_INTERVAL)
} catch (err) {
handleError(err)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { activeAccounts } from '@core/profile/stores'
import { checkForUntrackedTokens, fetchEvmBalancesForAccount } from '../actions'
import { checkForUntrackedNfts } from '@core/nfts/actions'

export function fetchEvmBalancesForAllAccounts(profileId: string, addPreviouslyUntracked?: boolean): void {
export function fetchEvmBalancesForAllAccounts(addPreviouslyUntracked?: boolean): void {
for (const account of get(activeAccounts)) {
try {
checkForUntrackedTokens(account, addPreviouslyUntracked)
void checkForUntrackedNfts(account)
fetchEvmBalancesForAccount(profileId, account)
fetchEvmBalancesForAccount(account)
} catch (err) {
console.error(err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,15 @@ import { EvmNetworkType, NetworkHealth, NetworkNamespace, ChainId } from '../enu
import { IBlock, IEvmNetwork, IBaseEvmNetworkConfiguration } from '../interfaces'
import { CoinType } from '@iota/sdk/out/types'
import { EvmNetworkId, Web3Provider } from '../types'
import { IBaseToken } from '@core/token'
import { NETWORK_STATUS_POLL_INTERVAL } from '@core/network/constants'
import { BASE_TOKEN_ID, IBaseToken, ITokenBalance, TokenTrackingStatus } from '@core/token'
import features from '@features/features'
import { updateErc721NftsOwnership } from '@core/nfts/actions'
import { getOrRequestTokenFromPersistedTokens } from '@core/token/actions'
import { IAccountState } from '@core/account'
import { Converter } from '@core/utils'
import { getActiveProfile } from '@core/profile/stores'
import { IError } from '@core/error/interfaces'

export class BaseEvmNetwork implements IEvmNetwork {
public readonly provider: Web3Provider
Expand Down Expand Up @@ -94,4 +101,49 @@ export class BaseEvmNetwork implements IEvmNetwork {
const number = await this.provider.eth.getBlockNumber()
return this.provider.eth.getBlock(number)
}

async getBalance(account: IAccountState): Promise<ITokenBalance | undefined> {
const evmAddress = account.evmAddresses?.[this.coinType]
if (!evmAddress) {
return
}

if (features.collectibles.erc721.enabled) {
void updateErc721NftsOwnership(account, this.id)
}

const rawBalance = await this.provider.eth.getBalance(evmAddress)
const tokenBalance = { [BASE_TOKEN_ID]: Converter.bigIntLikeToBigInt(rawBalance) }

const erc20Balances = await this.getErc20BalancesForAddress(evmAddress)
for (const [tokenId, balance] of Object.entries(erc20Balances)) {
await getOrRequestTokenFromPersistedTokens(tokenId, this.id)
tokenBalance[tokenId] = Number.isNaN(Number(balance)) ? BigInt(0) : balance
}

return tokenBalance
}

async getErc20BalancesForAddress(evmAddress: string): Promise<ITokenBalance> {
const trackedTokens = getActiveProfile()?.trackedTokens?.[this.id] ?? {}
const erc20TokenBalances: ITokenBalance = {}
for (const [erc20Address, trackingStatus] of Object.entries(trackedTokens)) {
try {
if (trackingStatus === TokenTrackingStatus.Untracked) {
continue
}

const contract = this.getContract(ContractType.Erc20, erc20Address)
if (!contract || !this.coinType) {
continue
}
const rawBalance = await contract.methods.balanceOf(evmAddress).call()
erc20TokenBalances[erc20Address] = Converter.bigIntLikeToBigInt(rawBalance)
} catch (err) {
const error = (err as IError)?.message ?? err
console.error(error)
}
}
return erc20TokenBalances
}
}
19 changes: 15 additions & 4 deletions packages/shared/src/lib/core/network/classes/isc-chain.class.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { IBlock, IIscChainConfiguration, IIscChainMetadata } from '../interfaces'
import { IIscChainConfiguration, IIscChainMetadata } from '../interfaces'
import { Converter } from '@core/utils'
import { BaseEvmNetwork } from './base-evm-network.class'
import { IAccountState } from '@core/account/interfaces'
import { ITokenBalance } from '@core/token/interfaces'
import { fetchIscAssetsForAccount } from '@core/layer-2/utils'
import { getActiveProfileId } from '@core/profile/stores'

export class IscChain extends BaseEvmNetwork {
private readonly _chainApi: string
Expand Down Expand Up @@ -47,9 +51,16 @@ export class IscChain extends BaseEvmNetwork {
return (await response.json()) as IIscChainMetadata
}

async getLatestBlock(): Promise<IBlock> {
const number = await this.provider.eth.getBlockNumber()
return this.provider.eth.getBlock(number)
async getBalance(account: IAccountState): Promise<ITokenBalance | undefined> {
const evmAddress = account.evmAddresses?.[this.coinType]
if (!evmAddress) {
return undefined
}

const tokenBalance = (await super.getBalance(account)) ?? {}
const iscBalance = (await fetchIscAssetsForAccount(getActiveProfileId(), evmAddress, this, account)) ?? {}

return { ...tokenBalance, ...iscBalance }
}

async getGasEstimate(hex: string): Promise<bigint> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { EvmNetworkId, Web3Provider } from '../types'
import { IBlock } from './block.interface'
import { IBaseNetwork, IBaseNetworkMetadata } from './base-network.interface'
import { IIscChainMetadata } from './isc-chain-metadata.interface'
import { ITokenBalance } from '@core/token/interfaces'
import { IAccountState } from '@core/account/interfaces'

export interface IIscChain extends IEvmNetwork {
apiEndpoint: string
Expand All @@ -26,6 +28,7 @@ export interface IEvmNetwork extends IBaseNetwork, IBaseNetworkMetadata {
provider: Web3Provider

getGasPrice(): Promise<bigint | undefined>
getBalance(account: IAccountState): Promise<ITokenBalance | undefined>

getContract(type: ContractType, address: string): Contract
getLatestBlock(): Promise<IBlock>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export async function login(loginOptions?: ILoginOptions): Promise<void> {
incrementLoginProgress()
subscribeToWalletApiEventsForActiveProfile()
await startBackgroundSync({ syncIncomingTransactions: true })
fetchEvmBalancesForAllAccounts(id)
fetchEvmBalancesForAllAccounts()
void fetchAndPersistTransactionsForAccounts(_activeProfile.id, get(activeAccounts))

// Step 8: finish login
Expand Down

0 comments on commit 72d8d95

Please sign in to comment.