From b03e3c4e814eed7422d6b2825078ada35f996a79 Mon Sep 17 00:00:00 2001 From: Milton Tulli Date: Tue, 3 Mar 2026 23:12:22 -0300 Subject: [PATCH 1/3] feat(network): add Avalanche C-Chain (43114) network support Add support for Avalanche C-Chain, a standard EVM-compatible chain with high throughput. Implements network configuration, RPC client initialization (via direct EthereumClient instantiation since not in ClientFactory), and adapter routing. Closes #113. - Add Avalanche C-Chain entry to networks.json with CAIP-2 identifier and metadata - Update DataService to create EthereumClient for unsupported chains - Update AdapterFactory and NetworkAdapter to handle chain 43114 - Route to EVMAdapter (standard EVM implementation) --- bun.lock | 1 - src/config/networks.json | 29 +++++++++++++++++++ src/services/DataService.ts | 25 ++++++++++++---- .../adapters/EVMAdapter/EVMAdapter.ts | 2 +- src/services/adapters/NetworkAdapter.ts | 2 +- src/services/adapters/adaptersFactory.ts | 3 +- 6 files changed, 52 insertions(+), 10 deletions(-) diff --git a/bun.lock b/bun.lock index 780aa3d3..dd9e82c3 100644 --- a/bun.lock +++ b/bun.lock @@ -1,6 +1,5 @@ { "lockfileVersion": 1, - "configVersion": 1, "workspaces": { "": { "name": "openscan", diff --git a/src/config/networks.json b/src/config/networks.json index 3bec09b4..a7b49e61 100644 --- a/src/config/networks.json +++ b/src/config/networks.json @@ -185,6 +185,35 @@ } ] }, + { + "type": "evm", + "networkId": "eip155:43114", + "slug": "avax", + "name": "Avalanche C-Chain", + "shortName": "Avalanche", + "description": "EVM-compatible smart contract platform with high throughput", + "currency": "AVAX", + "color": "#E84142", + "isTestnet": false, + "logo": "assets/networks/43114.svg", + "links": [ + { + "name": "Website", + "url": "https://www.avax.network", + "description": "Official Avalanche website" + }, + { + "name": "Docs", + "url": "https://docs.avax.network", + "description": "Developer documentation" + }, + { + "name": "Bridge", + "url": "https://core.app/bridge", + "description": "Bridge assets to Avalanche" + } + ] + }, { "type": "evm", "networkId": "eip155:11155111", diff --git a/src/services/DataService.ts b/src/services/DataService.ts index 2d8ad997..b45fd42f 100644 --- a/src/services/DataService.ts +++ b/src/services/DataService.ts @@ -1,4 +1,9 @@ -import { type SupportedChainId, ClientFactory, BitcoinClient } from "@openscan/network-connectors"; +import { + type SupportedChainId, + ClientFactory, + BitcoinClient, + EthereumClient, +} from "@openscan/network-connectors"; import { AdapterFactory } from "./adapters/adaptersFactory"; import type { NetworkAdapter } from "./adapters/NetworkAdapter"; @@ -42,11 +47,19 @@ export class DataService { } else { // Create EVM client and adapter const chainId = getChainIdFromNetwork(network) as SupportedChainId; - const networkClient = ClientFactory.createTypedClient(chainId, { - rpcUrls, - type: strategy, - }); - this.networkAdapter = AdapterFactory.createAdapter(chainId, networkClient); + + // Chains not registered in ClientFactory need a direct EthereumClient + const unsupportedByFactory = [43114]; + if (unsupportedByFactory.includes(chainId)) { + const networkClient = new EthereumClient({ rpcUrls, type: strategy }); + this.networkAdapter = AdapterFactory.createAdapter(chainId, networkClient); + } else { + const networkClient = ClientFactory.createTypedClient(chainId, { + rpcUrls, + type: strategy, + }); + this.networkAdapter = AdapterFactory.createAdapter(chainId, networkClient); + } } } diff --git a/src/services/adapters/EVMAdapter/EVMAdapter.ts b/src/services/adapters/EVMAdapter/EVMAdapter.ts index d4e4cded..667274cd 100644 --- a/src/services/adapters/EVMAdapter/EVMAdapter.ts +++ b/src/services/adapters/EVMAdapter/EVMAdapter.ts @@ -22,7 +22,7 @@ import { NonceLookupService } from "../../NonceLookupService"; export class EVMAdapter extends NetworkAdapter { private client: EthereumClient; - constructor(networkId: SupportedChainId | 11155111 | 97 | 31337, client: EthereumClient) { + constructor(networkId: SupportedChainId | 11155111 | 97 | 31337 | 43114, client: EthereumClient) { super(networkId); this.client = client; diff --git a/src/services/adapters/NetworkAdapter.ts b/src/services/adapters/NetworkAdapter.ts index 1dcb2f3b..9cb08abc 100644 --- a/src/services/adapters/NetworkAdapter.ts +++ b/src/services/adapters/NetworkAdapter.ts @@ -51,7 +51,7 @@ export abstract class NetworkAdapter { isLocalHost: boolean; protected txSearch: AddressTransactionSearch | null = null; - constructor(networkId: SupportedChainId | 31337 | 11155111 | 97) { + constructor(networkId: SupportedChainId | 31337 | 11155111 | 97 | 43114) { this.networkId = networkId; this.isLocalHost = networkId === 31337; } diff --git a/src/services/adapters/adaptersFactory.ts b/src/services/adapters/adaptersFactory.ts index dd98c2f7..364ac322 100644 --- a/src/services/adapters/adaptersFactory.ts +++ b/src/services/adapters/adaptersFactory.ts @@ -24,7 +24,7 @@ export class AdapterFactory { * Create an EVM network adapter */ static createAdapter( - networkId: SupportedChainId | 11155111 | 97 | 31337, + networkId: SupportedChainId | 11155111 | 97 | 31337 | 43114, client: | EthereumClient | OptimismClient @@ -38,6 +38,7 @@ export class AdapterFactory { case 1: case 11155111: case 31337: + case 43114: return new EVMAdapter(networkId, client as EthereumClient); case 10: return new OptimismAdapter(networkId, client as OptimismClient); From c448dad6485132fda16a8378a1f96600161e4ac7 Mon Sep 17 00:00:00 2001 From: Milton Tulli Date: Tue, 3 Mar 2026 23:28:42 -0300 Subject: [PATCH 2/3] refactor(types): introduce AppChainId type for unified chain ID handling Consolidate scattered union types (SupportedChainId | 11155111 | 97 | 31337 | 43114) into a single AppChainId type, making it easier to add new networks. --- src/services/DataService.ts | 15 ++++++++------- src/services/adapters/EVMAdapter/EVMAdapter.ts | 5 +++-- src/services/adapters/NetworkAdapter.ts | 5 +++-- src/services/adapters/adaptersFactory.ts | 4 ++-- src/types/index.ts | 9 ++++++++- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/services/DataService.ts b/src/services/DataService.ts index b45fd42f..07fcca1b 100644 --- a/src/services/DataService.ts +++ b/src/services/DataService.ts @@ -8,7 +8,7 @@ import { import { AdapterFactory } from "./adapters/adaptersFactory"; import type { NetworkAdapter } from "./adapters/NetworkAdapter"; import type { BitcoinAdapter } from "./adapters/BitcoinAdapter/BitcoinAdapter"; -import type { NetworkConfig, RpcUrlsContextType } from "../types"; +import type { AppChainId, NetworkConfig, RpcUrlsContextType } from "../types"; import { getRPCUrls } from "../config/rpcConfig"; import { getNetworkRpcKey, getChainIdFromNetwork } from "../utils/networkResolver"; @@ -46,18 +46,19 @@ export class DataService { this.networkAdapter = null as unknown as NetworkAdapter; } else { // Create EVM client and adapter - const chainId = getChainIdFromNetwork(network) as SupportedChainId; + const chainId = getChainIdFromNetwork(network) as AppChainId; // Chains not registered in ClientFactory need a direct EthereumClient - const unsupportedByFactory = [43114]; + const unsupportedByFactory: number[] = [43114]; if (unsupportedByFactory.includes(chainId)) { const networkClient = new EthereumClient({ rpcUrls, type: strategy }); this.networkAdapter = AdapterFactory.createAdapter(chainId, networkClient); } else { - const networkClient = ClientFactory.createTypedClient(chainId, { - rpcUrls, - type: strategy, - }); + const factoryChainId = chainId as SupportedChainId; + const networkClient = ClientFactory.createTypedClient( + factoryChainId, + { rpcUrls, type: strategy }, + ); this.networkAdapter = AdapterFactory.createAdapter(chainId, networkClient); } } diff --git a/src/services/adapters/EVMAdapter/EVMAdapter.ts b/src/services/adapters/EVMAdapter/EVMAdapter.ts index 667274cd..a0d472dc 100644 --- a/src/services/adapters/EVMAdapter/EVMAdapter.ts +++ b/src/services/adapters/EVMAdapter/EVMAdapter.ts @@ -11,7 +11,8 @@ import { import { normalizeBlockNumber } from "../shared/normalizeBlockNumber"; import { mergeMetadata } from "../shared/mergeMetadata"; -import type { EthereumClient, SupportedChainId } from "@openscan/network-connectors"; +import type { EthereumClient } from "@openscan/network-connectors"; +import type { AppChainId } from "../../../types"; import { getRethClient, NONCE_LOOKUP_CHAIN_ID } from "../../../config/rethProviders"; import { NonceLookupService } from "../../NonceLookupService"; @@ -22,7 +23,7 @@ import { NonceLookupService } from "../../NonceLookupService"; export class EVMAdapter extends NetworkAdapter { private client: EthereumClient; - constructor(networkId: SupportedChainId | 11155111 | 97 | 31337 | 43114, client: EthereumClient) { + constructor(networkId: AppChainId, client: EthereumClient) { super(networkId); this.client = client; diff --git a/src/services/adapters/NetworkAdapter.ts b/src/services/adapters/NetworkAdapter.ts index 9cb08abc..b022d8b5 100644 --- a/src/services/adapters/NetworkAdapter.ts +++ b/src/services/adapters/NetworkAdapter.ts @@ -1,5 +1,6 @@ -import type { SupportedChainId, EthereumClient } from "@openscan/network-connectors"; +import type { EthereumClient } from "@openscan/network-connectors"; import type { + AppChainId, Block, Transaction, Address, @@ -51,7 +52,7 @@ export abstract class NetworkAdapter { isLocalHost: boolean; protected txSearch: AddressTransactionSearch | null = null; - constructor(networkId: SupportedChainId | 31337 | 11155111 | 97 | 43114) { + constructor(networkId: AppChainId) { this.networkId = networkId; this.isLocalHost = networkId === 31337; } diff --git a/src/services/adapters/adaptersFactory.ts b/src/services/adapters/adaptersFactory.ts index 364ac322..7dfac01f 100644 --- a/src/services/adapters/adaptersFactory.ts +++ b/src/services/adapters/adaptersFactory.ts @@ -15,8 +15,8 @@ import type { EthereumClient, OptimismClient, PolygonClient, - SupportedChainId, } from "@openscan/network-connectors"; +import type { AppChainId } from "../../types"; // biome-ignore lint/complexity/noStaticOnlyClass: export class AdapterFactory { @@ -24,7 +24,7 @@ export class AdapterFactory { * Create an EVM network adapter */ static createAdapter( - networkId: SupportedChainId | 11155111 | 97 | 31337 | 43114, + networkId: AppChainId, client: | EthereumClient | OptimismClient diff --git a/src/types/index.ts b/src/types/index.ts index 2abb8c9c..77a7751f 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,4 +1,4 @@ -import type { EthLog } from "@openscan/network-connectors"; +import type { EthLog, SupportedChainId } from "@openscan/network-connectors"; import type React from "react"; // ==================== NETWORK TYPES ==================== @@ -8,6 +8,13 @@ import type React from "react"; */ export type NetworkType = "evm" | "bitcoin"; +/** + * All EVM chain IDs supported by the app. + * Extends the connector library's SupportedChainId with additional chains. + * When adding a new EVM network, add its chain ID here. + */ +export type AppChainId = SupportedChainId | 43114; + // ==================== CORE DOMAIN TYPES ==================== export interface NetworkStats { From 13f2742a1a4624c8b0bf6f782af460ce2b6bfd96 Mon Sep 17 00:00:00 2001 From: Milton Tulli Date: Mon, 16 Mar 2026 11:26:33 -0300 Subject: [PATCH 3/3] refactor(network): use network-connectors v1.4.0 native Avalanche support Update @openscan/network-connectors to v1.4.0 which includes native AvalancheClient (43114). Remove the EthereumClient workaround for unsupported chains and use ClientFactory directly. Simplify AppChainId type since 43114 is now part of SupportedChainId. --- bun.lock | 4 ++-- package.json | 2 +- src/services/DataService.ts | 30 +++++++----------------- src/services/adapters/adaptersFactory.ts | 8 ++++--- src/types/index.ts | 6 ++--- 5 files changed, 19 insertions(+), 31 deletions(-) diff --git a/bun.lock b/bun.lock index dd9e82c3..df7592c8 100644 --- a/bun.lock +++ b/bun.lock @@ -5,7 +5,7 @@ "name": "openscan", "dependencies": { "@erc7730/sdk": "^0.1.3", - "@openscan/network-connectors": "1.3.2", + "@openscan/network-connectors": "1.4.0", "@rainbow-me/rainbowkit": "^2.2.8", "@react-native-async-storage/async-storage": "^1.24.0", "@tanstack/react-query": "^5.90.21", @@ -279,7 +279,7 @@ "@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], - "@openscan/network-connectors": ["@openscan/network-connectors@1.3.2", "", {}, "sha512-OdH+PqP/VNYkPrXBCaMhjNF2FQ5N5WH9wd9uGelgkCvbXXS0xXRC4PlPqWSSXqjZJUud0HAGDF5pbZfxIPFQnQ=="], + "@openscan/network-connectors": ["@openscan/network-connectors@1.4.0", "", {}, "sha512-a27b86OBZCXtCI5iKYgttXiEyG9I7NQ5QpRp2PQGydbBMrTF1U4XI3VcJJFm14FD9z8L5WW+VEZ8f7cglh3HPA=="], "@paulmillr/qr": ["@paulmillr/qr@0.2.1", "", {}, "sha512-IHnV6A+zxU7XwmKFinmYjUcwlyK9+xkG3/s9KcQhI9BjQKycrJ1JRO+FbNYPwZiPKW3je/DR0k7w8/gLa5eaxQ=="], diff --git a/package.json b/package.json index b8ca7382..d8af2777 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "dependencies": { "@erc7730/sdk": "^0.1.3", - "@openscan/network-connectors": "1.3.2", + "@openscan/network-connectors": "1.4.0", "@rainbow-me/rainbowkit": "^2.2.8", "@react-native-async-storage/async-storage": "^1.24.0", "@tanstack/react-query": "^5.90.21", diff --git a/src/services/DataService.ts b/src/services/DataService.ts index 07fcca1b..2d8ad997 100644 --- a/src/services/DataService.ts +++ b/src/services/DataService.ts @@ -1,14 +1,9 @@ -import { - type SupportedChainId, - ClientFactory, - BitcoinClient, - EthereumClient, -} from "@openscan/network-connectors"; +import { type SupportedChainId, ClientFactory, BitcoinClient } from "@openscan/network-connectors"; import { AdapterFactory } from "./adapters/adaptersFactory"; import type { NetworkAdapter } from "./adapters/NetworkAdapter"; import type { BitcoinAdapter } from "./adapters/BitcoinAdapter/BitcoinAdapter"; -import type { AppChainId, NetworkConfig, RpcUrlsContextType } from "../types"; +import type { NetworkConfig, RpcUrlsContextType } from "../types"; import { getRPCUrls } from "../config/rpcConfig"; import { getNetworkRpcKey, getChainIdFromNetwork } from "../utils/networkResolver"; @@ -46,21 +41,12 @@ export class DataService { this.networkAdapter = null as unknown as NetworkAdapter; } else { // Create EVM client and adapter - const chainId = getChainIdFromNetwork(network) as AppChainId; - - // Chains not registered in ClientFactory need a direct EthereumClient - const unsupportedByFactory: number[] = [43114]; - if (unsupportedByFactory.includes(chainId)) { - const networkClient = new EthereumClient({ rpcUrls, type: strategy }); - this.networkAdapter = AdapterFactory.createAdapter(chainId, networkClient); - } else { - const factoryChainId = chainId as SupportedChainId; - const networkClient = ClientFactory.createTypedClient( - factoryChainId, - { rpcUrls, type: strategy }, - ); - this.networkAdapter = AdapterFactory.createAdapter(chainId, networkClient); - } + const chainId = getChainIdFromNetwork(network) as SupportedChainId; + const networkClient = ClientFactory.createTypedClient(chainId, { + rpcUrls, + type: strategy, + }); + this.networkAdapter = AdapterFactory.createAdapter(chainId, networkClient); } } diff --git a/src/services/adapters/adaptersFactory.ts b/src/services/adapters/adaptersFactory.ts index 7dfac01f..7379a3ed 100644 --- a/src/services/adapters/adaptersFactory.ts +++ b/src/services/adapters/adaptersFactory.ts @@ -8,6 +8,7 @@ import { ArbitrumAdapter } from "./ArbitrumAdapter/ArbitrumAdapter"; import { BitcoinAdapter } from "./BitcoinAdapter/BitcoinAdapter"; import type { ArbitrumClient, + AvalancheClient, AztecClient, BaseClient, BitcoinClient, @@ -15,8 +16,8 @@ import type { EthereumClient, OptimismClient, PolygonClient, + SupportedChainId, } from "@openscan/network-connectors"; -import type { AppChainId } from "../../types"; // biome-ignore lint/complexity/noStaticOnlyClass: export class AdapterFactory { @@ -24,7 +25,7 @@ export class AdapterFactory { * Create an EVM network adapter */ static createAdapter( - networkId: AppChainId, + networkId: SupportedChainId, client: | EthereumClient | OptimismClient @@ -32,6 +33,7 @@ export class AdapterFactory { | PolygonClient | BaseClient | ArbitrumClient + | AvalancheClient | AztecClient, ): NetworkAdapter { switch (networkId) { @@ -39,7 +41,7 @@ export class AdapterFactory { case 11155111: case 31337: case 43114: - return new EVMAdapter(networkId, client as EthereumClient); + return new EVMAdapter(networkId, client as unknown as EthereumClient); case 10: return new OptimismAdapter(networkId, client as OptimismClient); case 56: diff --git a/src/types/index.ts b/src/types/index.ts index 77a7751f..8daf8c5e 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -10,10 +10,10 @@ export type NetworkType = "evm" | "bitcoin"; /** * All EVM chain IDs supported by the app. - * Extends the connector library's SupportedChainId with additional chains. - * When adding a new EVM network, add its chain ID here. + * Maps directly to the connector library's SupportedChainId. + * When adding a new EVM network, add its chain ID to network-connectors first. */ -export type AppChainId = SupportedChainId | 43114; +export type AppChainId = SupportedChainId; // ==================== CORE DOMAIN TYPES ====================