diff --git a/README.md b/README.md index 9d0f2880..cef16d25 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,13 @@ Avalanche SDK TypeScript provides a complete set of tools and libraries for deve ## Available SDKs +### [Chains SDK](./chains/) + +The `Chains` module provides a collection of Avalanche Layer 1 (L1) chain configurations in a standardized format. These configurations are useful for interacting with Avalanche networks in a consistent and type-safe way, and is compatible with libraries like [viem](https://viem.sh/). + +- ChainConfig compatible with libraries like `viem` +- Can be used with other Avalanche SDKs like `client` and `interchain` + ### [Client SDK](./client/) The main Avalanche client SDK for interacting with Avalanche nodes and building blockchain applications. diff --git a/chains/.gitignore b/chains/.gitignore new file mode 100644 index 00000000..da0a94d9 --- /dev/null +++ b/chains/.gitignore @@ -0,0 +1,22 @@ +/models +/models/errors +/types +/node_modules +/lib +/sdk +/funcs +/react-query +/mcp-server +/hooks +/index.* +/core.* +/bin +/cjs +/esm +/dist +/.tsbuildinfo +/.eslintcache +/.tshy +/.tshy-* +/__tests__ +/coverage \ No newline at end of file diff --git a/chains/README.md b/chains/README.md new file mode 100644 index 00000000..09d50379 --- /dev/null +++ b/chains/README.md @@ -0,0 +1,91 @@ +# Avalanche L1 Chains + +The `Chains` module provides a collection of Avalanche Layer 1 (L1) chain configurations in a standardized format. These configurations are useful for interacting with Avalanche networks in a consistent and type-safe way, and is compatible with libraries like [viem](https://viem.sh/). + +## Requirements + +- Node.js >= 20.0.0 +- TypeScript >= 5.0.0 + +## Installation + +```bash +npm install @avalanche-sdk/chains +``` + +## Usage + +Using any of the listed L1s from the `chains` module is very simple: + +```typescript +import { avalancheFuji, dispatch } from "@avalanche-sdk/chains"; + +console.log(avalancheFuji.id); +``` + +## ChainConfig + +The `ChainConfig` interface extends `viem`'s [official config](https://viem.sh/docs/chains/introduction) by enforcing the following Teleporter related properties. + +This config is used in other Avalanche SDKs like `interchain` and `client`. + +```typescript +export interface ChainConfig extends Chain { + blockchainId: string; + contracts: { + [key: string]: { + address: Address; + }; + teleporterRegistry: { + address: Address; + }; + teleporterManager: { + address: Address; + }; + } +} +``` + +## Extending ChainConfig + +If you want to test chains locally which are not in this module, you can easily define the chain config like this (see Viem's [Custom Chains](https://viem.sh/docs/chains/introduction#custom-chains)): + +```typescript +import { defineChain } from 'viem' + +export const zora = defineChain({ + id: 7777777, + name: 'Zora', + nativeCurrency: { + decimals: 18, + name: 'Ether', + symbol: 'ETH', + }, + rpcUrls: { + default: { + http: ['https://rpc.zora.energy'], + webSocket: ['wss://rpc.zora.energy'], + }, + }, + blockExplorers: { + default: { name: 'Explorer', url: 'https://explorer.zora.energy' }, + }, + contracts: { + multicall3: { + address: '0xcA11bde05977b3631167028862bE2a173976CA11', + blockCreated: 5882, + }, + teleporterRegistry: { + address: '0xF86Cb19Ad8405AEFa7d09C778215D2Cb6eBfB228', + }, + teleporterManager: { + address: '0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf', + }, + }, + blockchainId: 'abc'; +}) +``` + +## Contributing + +You can expand the official `Chains` module by adding the chain config following the above interface. diff --git a/chains/package-lock.json b/chains/package-lock.json new file mode 100644 index 00000000..02b1896e --- /dev/null +++ b/chains/package-lock.json @@ -0,0 +1,271 @@ +{ + "name": "@avalanche-sdk/chains", + "version": "0.0.1-alpha.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@avalanche-sdk/chains", + "version": "0.0.1-alpha.1", + "license": "BSD-3-Clause", + "devDependencies": { + "@types/node": "^20.0.0", + "typescript": "^5.0.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "viem": "^2.33.1" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz", + "integrity": "sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==", + "license": "MIT", + "peer": true + }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.2.tgz", + "integrity": "sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@types/node": { + "version": "20.19.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.10.tgz", + "integrity": "sha512-iAFpG6DokED3roLSP0K+ybeDdIX6Bc0Vd3mLW5uDqThPWtNos3E+EqOM11mPQHKzfWHqEBuLjIlsBQQ8CsISmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/abitype": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.8.tgz", + "integrity": "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg==", + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "license": "MIT", + "peer": true + }, + "node_modules/isows": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", + "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peer": true, + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/ox": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.8.6.tgz", + "integrity": "sha512-eiKcgiVVEGDtEpEdFi1EGoVVI48j6icXHce9nFwCNM7CKG3uoCXKdr4TPhS00Iy1TR2aWSF1ltPD0x/YgqIL9w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@adraffy/ens-normalize": "^1.11.0", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "^1.9.1", + "@noble/hashes": "^1.8.0", + "@scure/bip32": "^1.7.0", + "@scure/bip39": "^1.6.0", + "abitype": "^1.0.8", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/viem": { + "version": "2.33.3", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.33.3.tgz", + "integrity": "sha512-aWDr6i6r3OfNCs0h9IieHFhn7xQJJ8YsuA49+9T5JRyGGAkWhLgcbLq2YMecgwM7HdUZpx1vPugZjsShqNi7Gw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@noble/curves": "1.9.2", + "@noble/hashes": "1.8.0", + "@scure/bip32": "1.7.0", + "@scure/bip39": "1.6.0", + "abitype": "1.0.8", + "isows": "1.0.7", + "ox": "0.8.6", + "ws": "8.18.2" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ws": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/chains/package.json b/chains/package.json new file mode 100644 index 00000000..600d5389 --- /dev/null +++ b/chains/package.json @@ -0,0 +1,48 @@ +{ + "name": "@avalanche-sdk/chains", + "version": "0.0.1-alpha.1", + "description": "Chains package for Avalanche L1s", + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "engines": { + "node": ">=20.0.0" + }, + "scripts": { + "build": "npx tsc" + }, + "files": [ + "dist", + "src", + "!src/**/*.test.ts", + "!src/**/fixtures/**" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/ava-labs/avalanche-sdk-typescript.git", + "directory": "chains" + }, + "keywords": [ + "chains", + "l1", + "AvalancheL1", + "subnets" + ], + "author": "Raj Ranjan", + "license": "BSD-3-Clause", + "bugs": { + "url": "https://github.com/ava-labs/avalanche-sdk-typescript/issues" + }, + "homepage": "https://github.com/ava-labs/avalanche-sdk-typescript#readme", + "peerDependencies": { + "viem": "^2.33.1" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "typescript": "^5.0.0" + }, + "sideEffects": false, + "exports": { + ".": "./dist/index.js" + } +} diff --git a/chains/src/chains/index.ts b/chains/src/chains/index.ts new file mode 100644 index 00000000..2670871d --- /dev/null +++ b/chains/src/chains/index.ts @@ -0,0 +1,16 @@ +import { ChainConfig } from "../types/chainConfig"; +import { avalancheFuji } from "./testnet/avalancheFuji"; +import { dispatch } from "./testnet/dispatch"; + +export { avalancheFuji } from "./testnet/avalancheFuji"; +export { dispatch } from "./testnet/dispatch"; + +const chainsMap = new Map(); +[ + avalancheFuji, + dispatch, +].forEach((chain) => { + chainsMap.set(chain.id, chain); + chainsMap.set(chain.name, chain); +}); +export { chainsMap }; \ No newline at end of file diff --git a/chains/src/chains/testnet/avalancheFuji.ts b/chains/src/chains/testnet/avalancheFuji.ts new file mode 100644 index 00000000..9d901e3f --- /dev/null +++ b/chains/src/chains/testnet/avalancheFuji.ts @@ -0,0 +1,36 @@ +import { defineChain } from 'viem'; +import { ChainConfig } from '../../types/chainConfig'; + +export const avalancheFuji = defineChain({ + id: 43_113, + name: 'Avalanche Fuji', + nativeCurrency: { + decimals: 18, + name: 'Avalanche Fuji', + symbol: 'AVAX', + }, + rpcUrls: { + default: { http: ['https://api.avax-test.network/ext/bc/C/rpc'] }, + }, + blockExplorers: { + default: { + name: 'SnowTrace', + url: 'https://subnets-test.avax.network/c-chain', + apiUrl: 'https://glacier-api.avax.network/v1/chains/43113', + }, + }, + contracts: { + multicall3: { + address: '0xca11bde05977b3631167028862be2a173976ca11', + blockCreated: 7096959, + }, + teleporterRegistry: { + address: '0xF86Cb19Ad8405AEFa7d09C778215D2Cb6eBfB228', + }, + teleporterManager: { + address: '0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf', + }, + }, + testnet: true, + blockchainId: '0x7fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5', +}) as ChainConfig; diff --git a/chains/src/chains/testnet/dispatch.ts b/chains/src/chains/testnet/dispatch.ts new file mode 100644 index 00000000..32e632f5 --- /dev/null +++ b/chains/src/chains/testnet/dispatch.ts @@ -0,0 +1,34 @@ +import { defineChain } from "viem"; +import { ChainConfig } from "../../types/chainConfig"; + +export const dispatch = defineChain({ + id: 779672, + name: 'Dispatch L1', + blockchainId: '0x9f3be606497285d0ffbb5ac9ba24aa60346a9b1812479ed66cb329f394a4b1c7', + nativeCurrency: { + decimals: 18, + name: 'DIS', + symbol: 'DIS', + }, + rpcUrls: { + default: { + http: ['https://subnets.avax.network/dispatch/testnet/rpc'], + }, + }, + blockExplorers: { + default: { + name: 'Dispatch Explorer', + url: 'https://subnets-test.avax.network/dispatch', + apiUrl: 'https://glacier-api.avax.network/v1/chains/779672', + }, + }, + contracts: { + teleporterRegistry: { + address: '0xF86Cb19Ad8405AEFa7d09C778215D2Cb6eBfB228', + }, + teleporterManager: { + address: '0x253b2784c75e510dD0fF1da844684a1aC0aa5fcf', + }, + }, + testnet: true +}) as ChainConfig; \ No newline at end of file diff --git a/chains/src/index.ts b/chains/src/index.ts new file mode 100644 index 00000000..e0290337 --- /dev/null +++ b/chains/src/index.ts @@ -0,0 +1,3 @@ +export type { ChainConfig } from './types/chainConfig'; +export * from './chains'; +export { getChainByChainId, getChainByName } from './utils/getChain'; diff --git a/chains/src/types/chainConfig.ts b/chains/src/types/chainConfig.ts new file mode 100644 index 00000000..da677296 --- /dev/null +++ b/chains/src/types/chainConfig.ts @@ -0,0 +1,17 @@ +import { Address } from "viem"; +import { Chain } from "viem/chains"; + +export interface ChainConfig extends Chain { + blockchainId: string; + contracts: { + [key: string]: { + address: Address; + }; + teleporterRegistry: { + address: Address; + }; + teleporterManager: { + address: Address; + }; + } +} \ No newline at end of file diff --git a/chains/src/utils/getChain.ts b/chains/src/utils/getChain.ts new file mode 100644 index 00000000..6be45526 --- /dev/null +++ b/chains/src/utils/getChain.ts @@ -0,0 +1,10 @@ +import { chainsMap } from '../chains' +import { ChainConfig } from '../types/chainConfig'; + +export function getChainByChainId(chainId: string): ChainConfig | undefined { + return chainsMap.get(chainId); +} + +export function getChainByName(chainName: string): ChainConfig | undefined { + return chainsMap.get(chainName); +} diff --git a/chains/tsconfig.json b/chains/tsconfig.json new file mode 100644 index 00000000..5e20adb0 --- /dev/null +++ b/chains/tsconfig.json @@ -0,0 +1,43 @@ +{ + "compilerOptions": { + "incremental": true, + "tsBuildInfoFile": ".tsbuildinfo", + "target": "ES2022", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "jsx": "react-jsx", + + "module": "ES2022", + "moduleResolution": "bundler", + + "allowJs": true, + + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./dist", + "rootDir": "./src", + + + // https://github.com/tsconfig/bases/blob/a1bf7c0fa2e094b068ca3e1448ca2ece4157977e/bases/strictest.json + "strict": true, + "allowUnusedLabels": false, + "allowUnreachableCode": false, + "exactOptionalPropertyTypes": true, + "useUnknownInCatchVariables": true, + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noPropertyAccessFromIndexSignature": true, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "isolatedModules": true, + "checkJs": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] + } + \ No newline at end of file