Skip to content

Commit

Permalink
Merge pull request #2880 from tallyhowallet/add_ethereum_chain
Browse files Browse the repository at this point in the history
  • Loading branch information
hyphenized authored Jan 19, 2023
2 parents 733ff47 + d9d0fcc commit 1c427db
Show file tree
Hide file tree
Showing 15 changed files with 330 additions and 63 deletions.
2 changes: 1 addition & 1 deletion background/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { HexString } from "./types"

/**
* An account balance at a particular time and block height, on a particular
* network. Flexible enough to represent base assets like ETH and BTC as well
* network. Flexible enough to represent base assets like ETH as well
* application-layer tokens like ERC-20s.
*/
export type AccountBalance = {
Expand Down
11 changes: 0 additions & 11 deletions background/constants/base-assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,8 @@ const BNB: NetworkBaseAsset = {
decimals: 18,
}

const BTC: NetworkBaseAsset = {
/**
* To persist base asset to indexDB chainID must be declared.
*/
chainID: "",
name: "Bitcoin",
symbol: "BTC",
decimals: 8,
}

export const BASE_ASSETS_BY_CUSTOM_NAME = {
ETH,
BTC,
MATIC,
RBTC,
AVAX,
Expand Down
2 changes: 0 additions & 2 deletions background/constants/coin-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
* Limited extension-specific list of coin types by asset symbol.
*/
export const coinTypesByAssetSymbol = {
BTC: 0,
"Testnet BTC": 1,
ETH: 60,
RBTC: 137,
MATIC: 966,
Expand Down
11 changes: 0 additions & 11 deletions background/constants/currencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,19 +90,8 @@ export const BNB: NetworkBaseAsset & Required<CoinGeckoAsset> = {
},
}

export const BTC: NetworkBaseAsset & Required<CoinGeckoAsset> = {
...BASE_ASSETS_BY_CUSTOM_NAME.BTC,
coinType: coinTypesByAssetSymbol.BTC,
metadata: {
coinGeckoID: "bitcoin",
tokenLists: [],
websiteURL: "https://bitcoin.org",
},
}

export const BUILT_IN_NETWORK_BASE_ASSETS = [
ETH,
BTC,
MATIC,
RBTC,
OPTIMISTIC_ETH,
Expand Down
10 changes: 1 addition & 9 deletions background/constants/networks.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { FeatureFlags, isEnabled } from "../features"
import { EVMNetwork, Network } from "../networks"
import { EVMNetwork } from "../networks"
import {
ARBITRUM_NOVA_ETH,
ARBITRUM_ONE_ETH,
AVAX,
BNB,
BTC,
ETH,
GOERLI_ETH,
MATIC,
Expand Down Expand Up @@ -85,13 +84,6 @@ export const GOERLI: EVMNetwork = {
coingeckoPlatformID: "ethereum",
}

export const BITCOIN: Network = {
name: "Bitcoin",
baseAsset: BTC,
family: "BTC",
coingeckoPlatformID: "bitcoin",
}

export const DEFAULT_NETWORKS = [
ETHEREUM,
POLYGON,
Expand Down
4 changes: 2 additions & 2 deletions background/networks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type {
* Each supported network family is generally incompatible with others from a
* transaction, consensus, and/or wire format perspective.
*/
export type NetworkFamily = "EVM" | "BTC"
export type NetworkFamily = "EVM"

// Should be structurally compatible with FungibleAsset or much code will
// likely explode.
Expand All @@ -34,7 +34,7 @@ export type Network = {
baseAsset: NetworkBaseAsset & CoinGeckoAsset
family: NetworkFamily
chainID?: string
coingeckoPlatformID: string
coingeckoPlatformID?: string
}

/**
Expand Down
101 changes: 100 additions & 1 deletion background/services/chain/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import {
NetworkBaseAsset,
} from "../../networks"
import { FungibleAsset } from "../../assets"
import { BASE_ASSETS, DEFAULT_NETWORKS, GOERLI, POLYGON } from "../../constants"
import {
BASE_ASSETS,
CHAIN_ID_TO_RPC_URLS,
DEFAULT_NETWORKS,
GOERLI,
POLYGON,
} from "../../constants"

export type Transaction = AnyEVMTransaction & {
dataSource: "alchemy" | "local"
Expand Down Expand Up @@ -73,6 +79,8 @@ export class ChainDatabase extends Dexie {

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

private rpcUrls!: Dexie.Table<{ chainID: string; rpcUrls: string[] }, string>

constructor(options?: DexieOptions) {
super("tally/chain", options)
this.version(1).stores({
Expand Down Expand Up @@ -153,6 +161,16 @@ export class ChainDatabase extends Dexie {
this.version(6).stores({
baseAssets: "&chainID,symbol,name",
})

this.version(7).stores({
rpcUrls: "&chainID, rpcUrls",
})
}

async initialize(): Promise<void> {
await this.initializeBaseAssets()
await this.initializeRPCs()
await this.initializeEVMNetworks()
}

async getLatestBlock(network: Network): Promise<AnyEVMBlock | null> {
Expand Down Expand Up @@ -183,14 +201,70 @@ export class ChainDatabase extends Dexie {
)
}

async addEVMNetwork({
chainName,
chainID,
decimals,
symbol,
assetName,
rpcUrls,
}: {
chainName: string
chainID: string
decimals: number
symbol: string
assetName: string
rpcUrls: string[]
}): Promise<void> {
await this.networks.put({
name: chainName,
chainID,
family: "EVM",
baseAsset: {
decimals,
symbol,
name: assetName,
chainID,
},
})
// A bit awkward that we are adding the base asset to the network as well
// as to its own separate table - but lets forge on for now.
await this.addBaseAsset(assetName, symbol, chainID, decimals)
await this.addRpcUrls(chainID, rpcUrls)
}

async getAllEVMNetworks(): Promise<EVMNetwork[]> {
return this.networks.where("family").equals("EVM").toArray()
}

private async addBaseAsset(
name: string,
symbol: string,
chainID: string,
decimals: number
) {
await this.baseAssets.put({
decimals,
name,
symbol,
chainID,
})
}

async getAllBaseAssets(): Promise<NetworkBaseAsset[]> {
return this.baseAssets.toArray()
}

async initializeRPCs(): Promise<void> {
await Promise.all(
Object.entries(CHAIN_ID_TO_RPC_URLS).map(async ([chainId, rpcUrls]) => {
if (rpcUrls) {
await this.addRpcUrls(chainId, rpcUrls)
}
})
)
}

async initializeBaseAssets(): Promise<void> {
await this.updateBaseAssets(BASE_ASSETS)
}
Expand All @@ -210,6 +284,31 @@ export class ChainDatabase extends Dexie {
)
}

async getRpcUrlsByChainId(chainId: string): Promise<string[]> {
const rpcUrls = await this.rpcUrls.where({ chainId }).first()
if (rpcUrls) {
return rpcUrls.rpcUrls
}
throw new Error(`No RPC Found for ${chainId}`)
}

private async addRpcUrls(chainID: string, rpcUrls: string[]): Promise<void> {
const existingRpcUrlsForChain = await this.rpcUrls.get(chainID)
if (existingRpcUrlsForChain) {
existingRpcUrlsForChain.rpcUrls.push(...rpcUrls)
existingRpcUrlsForChain.rpcUrls = [
...new Set(existingRpcUrlsForChain.rpcUrls),
]
await this.rpcUrls.put(existingRpcUrlsForChain)
} else {
await this.rpcUrls.put({ chainID, rpcUrls })
}
}

async getAllRpcUrls(): Promise<{ chainID: string; rpcUrls: string[] }[]> {
return this.rpcUrls.toArray()
}

async getAllSavedTransactionHashes(): Promise<IndexableTypeArray> {
return this.chainTransactions.orderBy("hash").keys()
}
Expand Down
30 changes: 23 additions & 7 deletions background/services/chain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import {
OPTIMISM_GAS_ORACLE_ADDRESS,
} from "./utils/optimismGasPriceOracle"
import KeyringService from "../keyring"
import type { ValidatedAddEthereumChainParameter } from "../internal-ethereum-provider"

// 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 @@ -299,10 +300,11 @@ export default class ChainService extends BaseService<Events> {
override async internalStartService(): Promise<void> {
await super.internalStartService()

await this.initializeBaseAssets()
await this.db.initialize()
await this.initializeNetworks()
const accounts = await this.getAccountsToTrack()
const trackedNetworks = await this.getTrackedNetworks()

const transactions = await this.db.getAllTransactions()

this.emitter.emit("initializeActivities", { transactions, accounts })
Expand Down Expand Up @@ -344,12 +346,8 @@ export default class ChainService extends BaseService<Events> {
)
}

async initializeBaseAssets(): Promise<void> {
await this.db.initializeBaseAssets()
}

async initializeNetworks(): Promise<void> {
await this.db.initializeEVMNetworks()
const rpcUrls = await this.db.getAllRpcUrls()
if (!this.supportedNetworks.length) {
this.supportedNetworks = await this.db.getAllEVMNetworks()
}
Expand All @@ -362,7 +360,10 @@ export default class ChainService extends BaseService<Events> {
evm: Object.fromEntries(
this.supportedNetworks.map((network) => [
network.chainID,
makeSerialFallbackProvider(network),
makeSerialFallbackProvider(
network,
rpcUrls.find((v) => v.chainID === network.chainID)?.rpcUrls || []
),
])
),
}
Expand Down Expand Up @@ -1863,4 +1864,19 @@ export default class ChainService extends BaseService<Events> {
)
}
}

// Used to add non-default chains via wallet_addEthereumChain
async addCustomChain(
chainInfo: ValidatedAddEthereumChainParameter
): Promise<void> {
await this.db.addEVMNetwork({
chainName: chainInfo.chainName,
chainID: chainInfo.chainId,
decimals: chainInfo.nativeCurrency.decimals,
symbol: chainInfo.nativeCurrency.symbol,
assetName: chainInfo.nativeCurrency.name,
rpcUrls: chainInfo.rpcUrls,
})
this.supportedNetworks = await this.db.getAllEVMNetworks()
}
}
14 changes: 6 additions & 8 deletions background/services/chain/serial-fallback-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { utils } from "ethers"
import { getNetwork } from "@ethersproject/networks"
import {
SECOND,
CHAIN_ID_TO_RPC_URLS,
ALCHEMY_SUPPORTED_CHAIN_IDS,
RPC_METHOD_PROVIDER_ROUTING,
} from "../../constants"
Expand Down Expand Up @@ -931,7 +930,8 @@ export default class SerialFallbackProvider extends JsonRpcProvider {
}

export function makeSerialFallbackProvider(
network: EVMNetwork
network: EVMNetwork,
rpcUrls: string[]
): SerialFallbackProvider {
const alchemyProviderCreators = ALCHEMY_SUPPORTED_CHAIN_IDS.has(
network.chainID
Expand All @@ -956,12 +956,10 @@ export function makeSerialFallbackProvider(
]
: []

const genericProviders = (CHAIN_ID_TO_RPC_URLS[network.chainID] || []).map(
(rpcUrl) => ({
type: "generic" as const,
creator: () => new JsonRpcProvider(rpcUrl),
})
)
const genericProviders = rpcUrls.map((rpcUrl) => ({
type: "generic" as const,
creator: () => new JsonRpcProvider(rpcUrl),
}))

return new SerialFallbackProvider(network, [
// Prefer alchemy as the primary provider when available
Expand Down
Loading

0 comments on commit 1c427db

Please sign in to comment.