diff --git a/packages/shared/src/lib/core/network/classes/base-evm-network.class.ts b/packages/shared/src/lib/core/network/classes/base-evm-network.class.ts deleted file mode 100644 index df1b58c77f..0000000000 --- a/packages/shared/src/lib/core/network/classes/base-evm-network.class.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { writable, Writable } from 'svelte/store' - -import Web3 from 'web3' - -import { EVM_CONTRACT_ABIS } from '@core/layer-2/constants' -import { ContractType } from '@core/layer-2/enums' -import { Contract } from '@core/layer-2/types' - -import { NetworkHealth, NetworkNamespace, ChainId } from '../enums' -import { IBlock, IEvmNetwork, IBaseEvmNetworkConfiguration } from '../interfaces' -import { CoinType } from '@iota/sdk/out/types' -import { EvmNetworkId, Web3Provider } from '../types' -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 Omit { - public readonly provider: Web3Provider - - public readonly id: EvmNetworkId - public readonly namespace: NetworkNamespace.Evm - public readonly chainId: ChainId - public readonly coinType: CoinType - public readonly name: string - public readonly baseToken: IBaseToken - public readonly explorerUrl: string | undefined - public readonly rpcEndpoint: string - - public health: Writable = writable(NetworkHealth.Operational) - public statusPoll: number | undefined - - constructor({ - id, - namespace, - chainId, - coinType, - baseToken, - name, - explorerUrl, - rpcEndpoint, - }: IBaseEvmNetworkConfiguration) { - try { - const _rpcEndpoint = new URL(rpcEndpoint).href - this.provider = new Web3(`${_rpcEndpoint}`) - - this.id = id - this.namespace = namespace - this.chainId = chainId - this.coinType = coinType - this.baseToken = baseToken - this.name = name - this.explorerUrl = explorerUrl - this.rpcEndpoint = _rpcEndpoint - - void this.startStatusPoll() - } catch (err) { - console.error(err) - throw new Error('Failed to construct EVM Network!') - } - } - - startStatusPoll(): void { - this.statusPoll = window.setInterval(() => { - this.getLatestBlock() - .then(() => this.health.set(NetworkHealth.Operational)) - .catch(() => this.health.set(NetworkHealth.Disconnected)) - }, NETWORK_STATUS_POLL_INTERVAL) - } - - destroy(): void { - clearInterval(this.statusPoll) - } - - getContract(type: ContractType, address: string): Contract { - const abi = EVM_CONTRACT_ABIS[type] - if (!abi) { - throw new Error(`Unable to determine contract type "${type}"`) - } - return new this.provider.eth.Contract(abi, address) - } - - async getGasPrice(): Promise { - try { - const gasPrice = await this.provider.eth.getGasPrice() - return BigInt(gasPrice) - } catch { - return undefined - } - } - - async getLatestBlock(): Promise { - const number = await this.provider.eth.getBlockNumber() - return this.provider.eth.getBlock(number) - } - - async getBalance(account: IAccountState): Promise { - 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 { - 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 - } -} diff --git a/packages/shared/src/lib/core/network/classes/evm-network.class.ts b/packages/shared/src/lib/core/network/classes/evm-network.class.ts index 52df8eafee..5578f505c7 100644 --- a/packages/shared/src/lib/core/network/classes/evm-network.class.ts +++ b/packages/shared/src/lib/core/network/classes/evm-network.class.ts @@ -1,11 +1,147 @@ -import { NetworkType } from '../enums' -import { IEvmNetwork, IPureEvmNetworkConfiguration } from '../interfaces' -import { BaseEvmNetwork } from './base-evm-network.class' +import { writable, Writable } from 'svelte/store' -export class EvmNetwork extends BaseEvmNetwork implements IEvmNetwork { - public readonly type = NetworkType.Evm +import Web3 from 'web3' - constructor(chainConfiguration: IPureEvmNetworkConfiguration) { - super(chainConfiguration) +import { EVM_CONTRACT_ABIS } from '@core/layer-2/constants' +import { ContractType } from '@core/layer-2/enums' +import { Contract } from '@core/layer-2/types' + +import { IAccountState } from '@core/account' +import { IError } from '@core/error/interfaces' +import { NETWORK_STATUS_POLL_INTERVAL } from '@core/network/constants' +import { updateErc721NftsOwnership } from '@core/nfts/actions' +import { getActiveProfile } from '@core/profile/stores' +import { BASE_TOKEN_ID, IBaseToken, ITokenBalance, TokenTrackingStatus } from '@core/token' +import { getOrRequestTokenFromPersistedTokens } from '@core/token/actions' +import { Converter } from '@core/utils' +import features from '@features/features' +import { CoinType } from '@iota/sdk/out/types' +import { ChainId, NetworkHealth, NetworkNamespace, NetworkType } from '../enums' +import { IBaseEvmNetworkConfiguration, IBlock, IEvmNetwork } from '../interfaces' +import { EvmNetworkId, EvmNetworkType, Web3Provider } from '../types' + +export class EvmNetwork implements IEvmNetwork { + public readonly provider: Web3Provider + + public readonly id: EvmNetworkId + public readonly namespace: NetworkNamespace.Evm + public readonly chainId: ChainId + public readonly coinType: CoinType + public readonly name: string + public readonly baseToken: IBaseToken + public readonly explorerUrl: string | undefined + public readonly rpcEndpoint: string + public readonly type: EvmNetworkType = NetworkType.Evm + + public health: Writable = writable(NetworkHealth.Operational) + public statusPoll: number | undefined + + constructor({ + id, + namespace, + chainId, + coinType, + baseToken, + name, + explorerUrl, + rpcEndpoint, + }: IBaseEvmNetworkConfiguration) { + try { + const _rpcEndpoint = new URL(rpcEndpoint).href + this.provider = new Web3(`${_rpcEndpoint}`) + + this.id = id + this.namespace = namespace + this.chainId = chainId + this.coinType = coinType + this.baseToken = baseToken + this.name = name + this.explorerUrl = explorerUrl + this.rpcEndpoint = _rpcEndpoint + + void this.startStatusPoll() + } catch (err) { + console.error(err) + throw new Error('Failed to construct EVM Network!') + } + } + + startStatusPoll(): void { + this.statusPoll = window.setInterval(() => { + this.getLatestBlock() + .then(() => this.health.set(NetworkHealth.Operational)) + .catch(() => this.health.set(NetworkHealth.Disconnected)) + }, NETWORK_STATUS_POLL_INTERVAL) + } + + destroy(): void { + clearInterval(this.statusPoll) + } + + getContract(type: ContractType, address: string): Contract { + const abi = EVM_CONTRACT_ABIS[type] + if (!abi) { + throw new Error(`Unable to determine contract type "${type}"`) + } + return new this.provider.eth.Contract(abi, address) + } + + async getGasPrice(): Promise { + try { + const gasPrice = await this.provider.eth.getGasPrice() + return BigInt(gasPrice) + } catch { + return undefined + } + } + + async getLatestBlock(): Promise { + const number = await this.provider.eth.getBlockNumber() + return this.provider.eth.getBlock(number) + } + + async getBalance(account: IAccountState): Promise { + 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 { + 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 } } diff --git a/packages/shared/src/lib/core/network/classes/isc-chain.class.ts b/packages/shared/src/lib/core/network/classes/isc-chain.class.ts index ec5cf395b3..2b0152f50a 100644 --- a/packages/shared/src/lib/core/network/classes/isc-chain.class.ts +++ b/packages/shared/src/lib/core/network/classes/isc-chain.class.ts @@ -1,13 +1,13 @@ import { IIscChain, IIscChainConfiguration, IIscChainMetadata } from '../interfaces' import { Converter } from '@core/utils' -import { BaseEvmNetwork } from './base-evm-network.class' +import { EvmNetwork } from './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' import { NetworkType } from '@core/network/enums' -export class IscChain extends BaseEvmNetwork implements IIscChain { +export class IscChain extends EvmNetwork implements IIscChain { private readonly _chainApi: string private _metadata: IIscChainMetadata | undefined diff --git a/packages/shared/src/lib/core/network/interfaces/base-network.interface.ts b/packages/shared/src/lib/core/network/interfaces/base-network.interface.ts index af2a84607e..47df3fd118 100644 --- a/packages/shared/src/lib/core/network/interfaces/base-network.interface.ts +++ b/packages/shared/src/lib/core/network/interfaces/base-network.interface.ts @@ -1,9 +1,10 @@ import { IBaseToken } from '@core/token' import { Writable } from 'svelte/store' -import { NetworkHealth, NetworkNamespace } from '../enums' +import { NetworkHealth, NetworkNamespace, NetworkType } from '../enums' import { NetworkId } from '../types' export interface IBaseNetwork { + type: NetworkType health: Writable startStatusPoll(): void diff --git a/packages/shared/src/lib/core/network/interfaces/evm-network.interface.ts b/packages/shared/src/lib/core/network/interfaces/evm-network.interface.ts index 8e1ed63276..97b8dd4820 100644 --- a/packages/shared/src/lib/core/network/interfaces/evm-network.interface.ts +++ b/packages/shared/src/lib/core/network/interfaces/evm-network.interface.ts @@ -3,12 +3,12 @@ import { ContractType } from '@core/layer-2/enums' import { Contract } from '@core/layer-2/types' import { ITokenBalance } from '@core/token/interfaces' import { ChainId, NetworkNamespace, NetworkType } from '../enums' -import { EvmNetworkId, Web3Provider } from '../types' +import { EvmNetworkId, EvmNetworkType, Web3Provider } from '../types' import { IBaseNetwork, IBaseNetworkMetadata } from './base-network.interface' import { IBlock } from './block.interface' import { IIscChainMetadata } from './isc-chain-metadata.interface' -export interface IIscChain extends Omit { +export interface IIscChain extends IEvmNetwork { type: NetworkType.Isc apiEndpoint: string aliasAddress: string @@ -17,8 +17,8 @@ export interface IIscChain extends Omit { getMetadata(): Promise } -export interface IEvmNetwork extends Omit, IBaseNetworkMetadata { - type: NetworkType.Evm +export interface IEvmNetwork extends IBaseNetwork, IBaseNetworkMetadata { + type: EvmNetworkType id: EvmNetworkId namespace: NetworkNamespace.Evm chainId: ChainId diff --git a/packages/shared/src/lib/core/network/interfaces/stardust-network.interface.ts b/packages/shared/src/lib/core/network/interfaces/stardust-network.interface.ts index 64c939e9a1..9aa948e5b3 100644 --- a/packages/shared/src/lib/core/network/interfaces/stardust-network.interface.ts +++ b/packages/shared/src/lib/core/network/interfaces/stardust-network.interface.ts @@ -4,9 +4,7 @@ import { NetworkId } from '../types' import { IIscChainConfiguration } from './evm-network-configuration.interface' import { IBaseNetwork, IscChain, IStardustNetworkMetadata } from '@core/network' -export interface IStardustNetwork - extends Omit, - Omit { +export interface IStardustNetwork extends IBaseNetwork, Omit { type: NetworkType.Stardust namespace: NetworkNamespace.Stardust bech32Hrp: string diff --git a/packages/shared/src/lib/core/network/types/evm-network.type.ts b/packages/shared/src/lib/core/network/types/evm-network.type.ts new file mode 100644 index 0000000000..3ea9017998 --- /dev/null +++ b/packages/shared/src/lib/core/network/types/evm-network.type.ts @@ -0,0 +1,3 @@ +import { NetworkType } from '../enums' + +export type EvmNetworkType = NetworkType.Evm | NetworkType.Isc diff --git a/packages/shared/src/lib/core/network/types/index.ts b/packages/shared/src/lib/core/network/types/index.ts index 234a93a173..eea4ee779c 100644 --- a/packages/shared/src/lib/core/network/types/index.ts +++ b/packages/shared/src/lib/core/network/types/index.ts @@ -1,3 +1,4 @@ +export * from './evm-network.type' export * from './network-id.type' export * from './network-metadata.type' export * from './network.type'