From 5a460e42cea7245eb8e6117ae7fb8a65ac128ae5 Mon Sep 17 00:00:00 2001 From: alexander-sei Date: Sat, 15 Aug 2026 12:23:55 +0200 Subject: [PATCH 1/3] feat(precompiles): export Sei chain definitions Expose canonical Viem mainnet and testnet chains plus a stable devnet definition through the package's public entrypoints. Co-authored-by: Cursor --- .changeset/restore-sei-chain-exports.md | 5 ++ .../src/viem/__tests__/chain.spec.ts | 47 +++++++++++++++++-- packages/precompiles/src/viem/chain.ts | 19 ++++++++ scripts/check-precompile-exports.ts | 14 +++++- 4 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 .changeset/restore-sei-chain-exports.md diff --git a/.changeset/restore-sei-chain-exports.md b/.changeset/restore-sei-chain-exports.md new file mode 100644 index 000000000..3eea93f0e --- /dev/null +++ b/.changeset/restore-sei-chain-exports.md @@ -0,0 +1,5 @@ +--- +'@sei-js/precompiles': minor +--- + +Export Sei chain definitions from the package root and `viem` entrypoint. Mainnet and testnet re-export Viem's canonical definitions, while devnet remains locally defined because current Viem releases no longer export it. diff --git a/packages/precompiles/src/viem/__tests__/chain.spec.ts b/packages/precompiles/src/viem/__tests__/chain.spec.ts index 1cfd54ced..5f4aed376 100644 --- a/packages/precompiles/src/viem/__tests__/chain.spec.ts +++ b/packages/precompiles/src/viem/__tests__/chain.spec.ts @@ -1,7 +1,39 @@ -import { seiLocal } from '../chain'; +import { sei as viemSei, seiTestnet as viemSeiTestnet } from 'viem/chains'; +import * as packageRoot from '../../index'; +import { sei, seiDevnet, seiLocal, seiTestnet } from '../chain'; +import * as viemEntryPoint from '../index'; -describe('seiLocal chain', () => { - it('should be a valid chain definition', () => { +describe('Sei chain definitions', () => { + it('re-exports Viem Sei mainnet', () => { + expect(sei).toBe(viemSei); + expect(sei.id).toBe(1329); + }); + + it('re-exports Viem Sei testnet', () => { + expect(seiTestnet).toBe(viemSeiTestnet); + expect(seiTestnet.id).toBe(1328); + }); + + it('defines Sei devnet', () => { + expect(seiDevnet).toMatchObject({ + id: 713715, + name: 'Sei Devnet', + nativeCurrency: { + name: 'Sei', + symbol: 'SEI', + decimals: 18 + }, + rpcUrls: { + default: { + http: ['https://evm-rpc-arctic-1.sei-apis.com'], + webSocket: ['wss://evm-ws-arctic-1.sei-apis.com'] + } + }, + testnet: true + }); + }); + + it('defines Sei local', () => { expect(seiLocal).toMatchObject({ id: 713714, name: 'Sei Local', @@ -17,4 +49,13 @@ describe('seiLocal chain', () => { } }); }); + + it('exports every chain from the public entry points', () => { + for (const entryPoint of [viemEntryPoint, packageRoot]) { + expect(entryPoint.sei).toBe(sei); + expect(entryPoint.seiTestnet).toBe(seiTestnet); + expect(entryPoint.seiDevnet).toBe(seiDevnet); + expect(entryPoint.seiLocal).toBe(seiLocal); + } + }); }); diff --git a/packages/precompiles/src/viem/chain.ts b/packages/precompiles/src/viem/chain.ts index ae0db9f2a..759c519e4 100644 --- a/packages/precompiles/src/viem/chain.ts +++ b/packages/precompiles/src/viem/chain.ts @@ -1,5 +1,24 @@ import { defineChain } from 'viem'; +export { sei, seiTestnet } from 'viem/chains'; + +/** + * The Viem chain definition for Sei devnet. + * @category Chain + */ +export const seiDevnet = defineChain({ + id: 713715, + name: 'Sei Devnet', + nativeCurrency: { name: 'Sei', symbol: 'SEI', decimals: 18 }, + rpcUrls: { + default: { + http: ['https://evm-rpc-arctic-1.sei-apis.com'], + webSocket: ['wss://evm-ws-arctic-1.sei-apis.com'] + } + }, + testnet: true +}); + /** * The Viem chain definition for the Sei local chain. * @category Chain diff --git a/scripts/check-precompile-exports.ts b/scripts/check-precompile-exports.ts index 00c462525..88311a21b 100644 --- a/scripts/check-precompile-exports.ts +++ b/scripts/check-precompile-exports.ts @@ -41,8 +41,18 @@ const precompilesModule = await import(join(pkgDir, 'dist/precompiles/index.js') if (typeof ethersModule.getBankPrecompileEthersV6Contract !== 'function') { missing.push('getBankPrecompileEthersV6Contract is not a function'); } -if (!viemModule.seiLocal?.id) { - missing.push('seiLocal chain export missing'); +for (const [name, chainId] of [ + ['sei', 1329], + ['seiTestnet', 1328], + ['seiDevnet', 713715], + ['seiLocal', 713714] +] as const) { + if (viemModule[name]?.id !== chainId) { + missing.push(`${name} chain export missing or invalid`); + } + if (rootModule[name] !== viemModule[name]) { + missing.push(`root and viem subpaths do not share ${name} chain identity`); + } } if (!precompilesModule.BANK_PRECOMPILE_ADDRESS) { missing.push('BANK_PRECOMPILE_ADDRESS missing'); From a63e71de52b5761bed1fe9b60d21a85d6d2a4eaa Mon Sep 17 00:00:00 2001 From: alexander-sei Date: Sat, 15 Aug 2026 12:47:11 +0200 Subject: [PATCH 2/3] feat!: remove Sei devnet support Limit public chain configuration to mainnet and testnet, and update Viem to the supported baseline. Co-authored-by: Cursor --- .changeset/migrate-monorepo-to-bun.md | 2 +- .changeset/restore-sei-chain-exports.md | 2 +- .changeset/retire-arctic-network.md | 6 ++ REVIEW.md | 4 +- bun.lock | 58 ++------------- .../templates/next-template/.env.example | 2 +- .../templates/next-template/README.md | 4 +- .../src/app/development/page.tsx | 3 +- .../src/components/providers/providers.tsx | 22 ------ packages/mcp-server/.env.example | 1 - packages/mcp-server/package.json | 2 +- packages/mcp-server/src/core/chains.ts | 11 ++- packages/mcp-server/src/core/prompts.ts | 22 ++---- packages/mcp-server/src/core/tools.ts | 71 +++++-------------- packages/mcp-server/src/server/args.ts | 7 +- .../mcp-server/src/tests/core/chains.test.ts | 17 +---- .../mcp-server/src/tests/server/args.test.ts | 2 - .../src/tests/server/server.test.ts | 4 +- packages/precompiles/README.md | 9 +++ packages/precompiles/package.json | 4 +- .../src/viem/__tests__/chain.spec.ts | 22 +----- packages/precompiles/src/viem/chain.ts | 17 ----- .../src/chain-info/__tests__/index.spec.ts | 2 +- packages/registry/src/chain-info/index.ts | 6 +- .../registry/src/gas/__tests__/index.spec.ts | 4 +- packages/registry/src/gas/index.ts | 5 +- .../registry/src/ibc/__tests__/index.spec.ts | 4 +- packages/registry/src/ibc/index.ts | 5 +- packages/registry/src/index.ts | 5 +- .../src/networks/__tests__/index.spec.ts | 4 +- packages/registry/src/networks/index.ts | 11 +-- .../src/tokens/__tests__/index.spec.ts | 4 +- packages/registry/src/tokens/index.ts | 5 +- scripts/check-precompile-exports.ts | 1 - 34 files changed, 105 insertions(+), 243 deletions(-) create mode 100644 .changeset/retire-arctic-network.md diff --git a/.changeset/migrate-monorepo-to-bun.md b/.changeset/migrate-monorepo-to-bun.md index 3fc201f1a..458a71136 100644 --- a/.changeset/migrate-monorepo-to-bun.md +++ b/.changeset/migrate-monorepo-to-bun.md @@ -10,4 +10,4 @@ Migrate the monorepo to Bun and publish ESM-only packages. **Breaking:** all packages drop CommonJS (`require`) entry points and flatten `dist/`. Consumers must use ESM `import`. `@sei-js/precompiles` now ships an `exports` map with working `./ethers`, `./viem`, and `./precompiles` subpaths. `@sei-js/sei-global-wallet` keeps the `./solana` entrypoint. -Install, build, and test with Bun (`bun install`, `bun run build`, `bun run test`). `@sei-js/mcp-server` now requires Node.js 20 or newer. The MCP server and precompiles development toolchain pin Viem to `2.37.2`, the last compatible release that includes `seiDevnet`. Generated apps target the new `@sei-js/precompiles` major. Packages continue to publish to npm via Changesets. +Install, build, and test with Bun (`bun install`, `bun run build`, `bun run test`). `@sei-js/mcp-server` now requires Node.js 20 or newer. The MCP server and precompiles development toolchain use Viem 2.55.16. Generated apps target the new `@sei-js/precompiles` major. Packages continue to publish to npm via Changesets. diff --git a/.changeset/restore-sei-chain-exports.md b/.changeset/restore-sei-chain-exports.md index 3eea93f0e..10364b564 100644 --- a/.changeset/restore-sei-chain-exports.md +++ b/.changeset/restore-sei-chain-exports.md @@ -2,4 +2,4 @@ '@sei-js/precompiles': minor --- -Export Sei chain definitions from the package root and `viem` entrypoint. Mainnet and testnet re-export Viem's canonical definitions, while devnet remains locally defined because current Viem releases no longer export it. +Export Viem's canonical Sei mainnet and testnet definitions from the package root and `viem` entrypoint. Viem 2.55.16 or newer is now required. diff --git a/.changeset/retire-arctic-network.md b/.changeset/retire-arctic-network.md new file mode 100644 index 000000000..0897dff75 --- /dev/null +++ b/.changeset/retire-arctic-network.md @@ -0,0 +1,6 @@ +--- +'@sei-js/mcp-server': major +'@sei-js/registry': major +--- + +Remove Sei devnet support. The MCP server no longer accepts `sei-devnet`, chain ID `713715`, or `DEVNET_RPC_URL`. Registry exports now contain only `pacific-1` and `atlantic-2`, and `CHAIN_IDS.devnet` has been removed. diff --git a/REVIEW.md b/REVIEW.md index 97c7cc6e4..4f5a58eff 100644 --- a/REVIEW.md +++ b/REVIEW.md @@ -32,8 +32,8 @@ invariants are load-bearing and are enforced in code rather than by convention: Beyond those, scrutinise anything that widens what a caller controls: contract ABIs reach `JSON.parse` from tool arguments in `src/core/tools.ts`, addresses and call arguments arrive unvalidated from the model, and RPC endpoints are -overridable through `MAINNET_RPC_URL` / `TESTNET_RPC_URL` / `DEVNET_RPC_URL` in -`src/core/chains.ts`. Private keys are read from the environment in +overridable through `MAINNET_RPC_URL` / `TESTNET_RPC_URL` in `src/core/chains.ts`. +Private keys are read from the environment in `src/core/config.ts` and must never reach a log line, an error message, or a tool response. diff --git a/bun.lock b/bun.lock index 5f9294119..8e18b81b7 100644 --- a/bun.lock +++ b/bun.lock @@ -40,7 +40,7 @@ "cors": "^2.8.5", "dotenv": "^16.5.0", "express": "^4.21.2", - "viem": "2.37.2", + "viem": "2.55.16", "zod": "^3.24.2", }, "devDependencies": { @@ -56,11 +56,11 @@ "esbuild": "^0.28.2", "ethers": "^6.0.0", "typescript": "^5.8.2", - "viem": "2.37.2", + "viem": "2.55.16", }, "peerDependencies": { "ethers": "^6.0.0", - "viem": "2.x", + "viem": "^2.55.16", }, }, "packages/registry": { @@ -474,7 +474,7 @@ "@wallet-standard/wallet": ["@wallet-standard/wallet@1.1.1", "", { "dependencies": { "@wallet-standard/base": "^1.1.1" } }, "sha512-8WiRPaKk/wNNRZhB2eVhpR/JW7/aqTCMoZhgVUCujuzDmxxmGvsosMxdCG4NAdYkoyozAHCX8/xLtlWUn5mNdQ=="], - "abitype": ["abitype@1.1.0", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A=="], + "abitype": ["abitype@1.2.3", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg=="], "ably": ["ably@2.17.1", "", { "dependencies": { "@ably/msgpack-js": "^0.4.0", "dequal": "^2.0.3", "fastestsmallesttextencoderdecoder": "^1.0.22", "got": "^11.8.5", "ulid": "^2.3.0", "ws": "^8.17.1" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-70yfXHoM7JtJD/8FCtPD1gkWW0f+AJqbJp0PsqDAqiyxFB8cPFY+FuKHgNTYb8eRHKXq8hT1xiDphUcY0+GHnA=="], @@ -906,7 +906,7 @@ "outdent": ["outdent@0.5.0", "", {}, "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q=="], - "ox": ["ox@0.9.3", "", { "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.9", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-KzyJP+fPV4uhuuqrTZyok4DC7vFzi7HLUFiUNEmpbyh59htKWkOC98IONC1zgXJPbHAhQgqs6B0Z6StCGhmQvg=="], + "ox": ["ox@0.14.33", "", { "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.2.3", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-rooA/4o7bBof4Ge2VH/eovfNPb/AEEYyrNj03wggc55g5HZD8Pjs/OeWhttgjic3dDcqn0r29bDuvQEdTiUemQ=="], "p-cancelable": ["p-cancelable@2.1.1", "", {}, "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="], @@ -1132,7 +1132,7 @@ "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], - "viem": ["viem@2.37.2", "", { "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.1.0", "isows": "1.0.7", "ox": "0.9.3", "ws": "8.18.3" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-soXSUhPEnHzXVo1sSFg2KiUUwOTCtqGNnR/NOHr+4vZcbM6sTyS62asg9EfDpaJQFNduRQituxTcflaK6OIaPA=="], + "viem": ["viem@2.55.16", "", { "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.2.3", "isows": "1.0.7", "ox": "0.14.33", "ws": "8.21.0" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-bKs/0PNIkiaG45ZM0ERddHSVtku7hvyebK2i3bMLuyJQevh15tpDh3MiE3PhLjx5kxGrvizaXQw67gWc9Jl7fQ=="], "wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="], @@ -1150,7 +1150,7 @@ "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + "ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], @@ -1182,20 +1182,10 @@ "@dynamic-labs-wallet/forward-mpc-client/@noble/hashes": ["@noble/hashes@2.3.0", "", {}, "sha512-oN+QwyX7VSHotibwubG3kpzbwKrfnyR6OOO+3Nk/53ADL7FmgHHz4TgrbaYKvvOw09u6QTx0oiH1cNCIOuN0CQ=="], - "@dynamic-labs-wallet/forward-mpc-client/ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], - "@dynamic-labs-wallet/forward-mpc-shared/@noble/ciphers": ["@noble/ciphers@0.4.1", "", {}, "sha512-QCOA9cgf3Rc33owG0AYBB9wszz+Ul2kramWN8tXG44Gyciud/tbkEqvxRF/IpqQaBpRBNi9f4jdNxqB2CQCIXg=="], "@dynamic-labs-wallet/forward-mpc-shared/@noble/hashes": ["@noble/hashes@2.3.0", "", {}, "sha512-oN+QwyX7VSHotibwubG3kpzbwKrfnyR6OOO+3Nk/53ADL7FmgHHz4TgrbaYKvvOw09u6QTx0oiH1cNCIOuN0CQ=="], - "@dynamic-labs/ethereum-aa-core/viem": ["viem@2.55.15", "", { "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.2.3", "isows": "1.0.7", "ox": "0.14.33", "ws": "8.21.0" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-ka9SfSJ3ZfhuUEzTGmufwALRvEPVKM068tF0AwfdRZagqA3yuFa/QoXIvALzr9y47m4Wiisl3yEoYWuFQso6Ng=="], - - "@dynamic-labs/ethereum-aa-zksync/viem": ["viem@2.55.15", "", { "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.2.3", "isows": "1.0.7", "ox": "0.14.33", "ws": "8.21.0" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-ka9SfSJ3ZfhuUEzTGmufwALRvEPVKM068tF0AwfdRZagqA3yuFa/QoXIvALzr9y47m4Wiisl3yEoYWuFQso6Ng=="], - - "@dynamic-labs/ethereum-core/viem": ["viem@2.55.15", "", { "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.2.3", "isows": "1.0.7", "ox": "0.14.33", "ws": "8.21.0" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-ka9SfSJ3ZfhuUEzTGmufwALRvEPVKM068tF0AwfdRZagqA3yuFa/QoXIvALzr9y47m4Wiisl3yEoYWuFQso6Ng=="], - - "@dynamic-labs/global-wallet-client/viem": ["viem@2.55.15", "", { "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.2.3", "isows": "1.0.7", "ox": "0.14.33", "ws": "8.21.0" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-ka9SfSJ3ZfhuUEzTGmufwALRvEPVKM068tF0AwfdRZagqA3yuFa/QoXIvALzr9y47m4Wiisl3yEoYWuFQso6Ng=="], - "@dynamic-labs/wallet-book/zod": ["zod@4.0.5", "", {}, "sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA=="], "@emnapi/runtime/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], @@ -1250,8 +1240,6 @@ "@swc/helpers/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "ably/ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], - "accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -1284,8 +1272,6 @@ "ethers/@types/node": ["@types/node@22.7.5", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ=="], - "ethers/ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], - "express-rate-limit/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], @@ -1306,8 +1292,6 @@ "marked-terminal/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], - "ox/abitype": ["abitype@1.2.3", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg=="], - "parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="], "publint/package-manager-detector": ["package-manager-detector@1.8.0", "", {}, "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A=="], @@ -1324,8 +1308,6 @@ "rpc-websockets/@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], - "rpc-websockets/ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], - "send/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], @@ -1342,36 +1324,10 @@ "yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "zksync-sso/abitype": ["abitype@1.2.3", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg=="], - "zksync-sso/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "@dynamic-labs-sdk/client/@dynamic-labs-wallet/browser-wallet-client/@dynamic-labs-wallet/core": ["@dynamic-labs-wallet/core@1.0.70", "", { "dependencies": { "@dynamic-labs-wallet/primitives": "1.0.70", "@dynamic-labs/sdk-api-core": "^0.0.984", "axios": "1.16.0", "uuid": "11.1.0" }, "peerDependencies": { "@dynamic-labs-wallet/forward-mpc-client": "1.0.1" } }, "sha512-brTbTiAwS2TEhB6G2lL43dFx9Bj19wB5GPngi49yiX6veQJkQP9kkqqcxv97zYSEIUbBaPP/GndkPSewXPXYew=="], - "@dynamic-labs/ethereum-aa-core/viem/abitype": ["abitype@1.2.3", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg=="], - - "@dynamic-labs/ethereum-aa-core/viem/ox": ["ox@0.14.33", "", { "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.2.3", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-rooA/4o7bBof4Ge2VH/eovfNPb/AEEYyrNj03wggc55g5HZD8Pjs/OeWhttgjic3dDcqn0r29bDuvQEdTiUemQ=="], - - "@dynamic-labs/ethereum-aa-core/viem/ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], - - "@dynamic-labs/ethereum-aa-zksync/viem/abitype": ["abitype@1.2.3", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg=="], - - "@dynamic-labs/ethereum-aa-zksync/viem/ox": ["ox@0.14.33", "", { "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.2.3", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-rooA/4o7bBof4Ge2VH/eovfNPb/AEEYyrNj03wggc55g5HZD8Pjs/OeWhttgjic3dDcqn0r29bDuvQEdTiUemQ=="], - - "@dynamic-labs/ethereum-aa-zksync/viem/ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], - - "@dynamic-labs/ethereum-core/viem/abitype": ["abitype@1.2.3", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg=="], - - "@dynamic-labs/ethereum-core/viem/ox": ["ox@0.14.33", "", { "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.2.3", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-rooA/4o7bBof4Ge2VH/eovfNPb/AEEYyrNj03wggc55g5HZD8Pjs/OeWhttgjic3dDcqn0r29bDuvQEdTiUemQ=="], - - "@dynamic-labs/ethereum-core/viem/ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], - - "@dynamic-labs/global-wallet-client/viem/abitype": ["abitype@1.2.3", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg=="], - - "@dynamic-labs/global-wallet-client/viem/ox": ["ox@0.14.33", "", { "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.2.3", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-rooA/4o7bBof4Ge2VH/eovfNPb/AEEYyrNj03wggc55g5HZD8Pjs/OeWhttgjic3dDcqn0r29bDuvQEdTiUemQ=="], - - "@dynamic-labs/global-wallet-client/viem/ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], - "@modelcontextprotocol/sdk/express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], "@modelcontextprotocol/sdk/express/body-parser": ["body-parser@2.3.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^2.0.0", "debug": "^4.4.3", "http-errors": "^2.0.1", "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", "qs": "^6.15.2", "raw-body": "^3.0.2", "type-is": "^2.1.0" } }, "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw=="], diff --git a/packages/create-sei/templates/next-template/.env.example b/packages/create-sei/templates/next-template/.env.example index 0f17c8953..ebb388c7a 100644 --- a/packages/create-sei/templates/next-template/.env.example +++ b/packages/create-sei/templates/next-template/.env.example @@ -1,5 +1,5 @@ # Chain configuration -# Options: mainnet, testnet, devnet +# Options: mainnet, testnet # Defaults to mainnet if not set; this example overrides it to testnet NEXT_PUBLIC_CHAIN=testnet diff --git a/packages/create-sei/templates/next-template/README.md b/packages/create-sei/templates/next-template/README.md index 7aeee02aa..f408a0ab4 100644 --- a/packages/create-sei/templates/next-template/README.md +++ b/packages/create-sei/templates/next-template/README.md @@ -34,9 +34,9 @@ In addition, this app uses [RainbowKit](https://www.rainbowkit.com/docs/wallet-b To change the list of supported wallets, as well as connection configurations you can modify the `src/app/components/Web3Provider.tsx` component. ### Changing the Chain -This application connects to the arctic-1 devnet by default, using chain configurations uploaded to viem. +This application connects to Sei mainnet by default. -To change the chain the app connects to, edit the `selectedChain` variable in the `chain.ts` file in `src/app/constants` +Set `NEXT_PUBLIC_CHAIN=testnet` in `.env.local` to use Sei testnet. The chain selection logic is in `src/components/providers/providers.tsx`. ## Learn More diff --git a/packages/create-sei/templates/next-template/src/app/development/page.tsx b/packages/create-sei/templates/next-template/src/app/development/page.tsx index 39fa5ecc9..cf9abb622 100644 --- a/packages/create-sei/templates/next-template/src/app/development/page.tsx +++ b/packages/create-sei/templates/next-template/src/app/development/page.tsx @@ -109,8 +109,7 @@ export default function DevelopmentPage() { {`# .env.local NEXT_PUBLIC_CHAIN=mainnet # Default: Sei Pacific-1 -# NEXT_PUBLIC_CHAIN=testnet # Sei Atlantic-2 -# NEXT_PUBLIC_CHAIN=devnet # Sei Devnet`} +# NEXT_PUBLIC_CHAIN=testnet # Sei Atlantic-2`} diff --git a/packages/create-sei/templates/next-template/src/components/providers/providers.tsx b/packages/create-sei/templates/next-template/src/components/providers/providers.tsx index 49c01728a..41033f96b 100644 --- a/packages/create-sei/templates/next-template/src/components/providers/providers.tsx +++ b/packages/create-sei/templates/next-template/src/components/providers/providers.tsx @@ -9,27 +9,8 @@ import { type ReactNode, useMemo } from "react"; import { createConfig, http, WagmiProvider } from "wagmi"; import "@rainbow-me/rainbowkit/styles.css"; -import { defineChain } from "viem"; import { sei, seiTestnet } from "viem/chains"; -const seiDevnet = defineChain({ - id: 713715, - name: "Sei Devnet", - nativeCurrency: { name: "Sei", symbol: "SEI", decimals: 18 }, - rpcUrls: { - default: { - http: ["https://evm-rpc-arctic-1.sei-apis.com"], - }, - }, - blockExplorers: { - default: { - name: "Seitrace", - url: "https://seitrace.com", - }, - }, - testnet: true, -}); - const queryClient = new QueryClient(); const connectors = connectorsForWallets( @@ -56,8 +37,6 @@ export default function Providers({ children }: ProvidersProps) { switch (chainName.toLowerCase()) { case "testnet": return seiTestnet; - case "devnet": - return seiDevnet; default: return sei; } @@ -73,7 +52,6 @@ export default function Providers({ children }: ProvidersProps) { transports: { [sei.id]: http(), [seiTestnet.id]: http(), - [seiDevnet.id]: http(), }, ssr: true, }), diff --git a/packages/mcp-server/.env.example b/packages/mcp-server/.env.example index a768e2d50..159679743 100644 --- a/packages/mcp-server/.env.example +++ b/packages/mcp-server/.env.example @@ -21,4 +21,3 @@ SERVER_PATH=/mcp # Optional, only needed if you want to use a custom RPC URL MAINNET_RPC_URL=your_mainnet_rpc_url_here TESTNET_RPC_URL=your_testnet_rpc_url_here -DEVNET_RPC_URL=your_devnet_rpc_url_here diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index a3a6020ce..b70c59fcf 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -39,7 +39,7 @@ "cors": "^2.8.5", "dotenv": "^16.5.0", "express": "^4.21.2", - "viem": "2.37.2", + "viem": "2.55.16", "zod": "^3.24.2" }, "keywords": [ diff --git a/packages/mcp-server/src/core/chains.ts b/packages/mcp-server/src/core/chains.ts index 117a7d9f8..e368ad273 100644 --- a/packages/mcp-server/src/core/chains.ts +++ b/packages/mcp-server/src/core/chains.ts @@ -1,5 +1,5 @@ import type { Chain } from 'viem'; -import { sei, seiDevnet, seiTestnet } from 'viem/chains'; +import { sei, seiTestnet } from 'viem/chains'; // Default configuration values export const DEFAULT_NETWORK = 'sei'; @@ -9,22 +9,19 @@ export const DEFAULT_CHAIN_ID = 1329; // Map chain IDs to chains export const chainMap: Record = { 1329: sei, - 1328: seiTestnet, - 713715: seiDevnet + 1328: seiTestnet }; // Map network names to chain IDs for easier reference export const networkNameMap: Record = { sei: 1329, - 'sei-testnet': 1328, - 'sei-devnet': 713_715 + 'sei-testnet': 1328 }; // Map chain IDs to RPC URLs export const rpcUrlMap: Record = { 1329: process.env.MAINNET_RPC_URL || 'https://evm-rpc.sei-apis.com', - 1328: process.env.TESTNET_RPC_URL || 'https://evm-rpc-testnet.sei-apis.com', - 713715: process.env.DEVNET_RPC_URL || 'https://evm-rpc-arctic-1.sei-apis.com' + 1328: process.env.TESTNET_RPC_URL || 'https://evm-rpc-testnet.sei-apis.com' }; /** diff --git a/packages/mcp-server/src/core/prompts.ts b/packages/mcp-server/src/core/prompts.ts index 4d4919b34..d85cde671 100644 --- a/packages/mcp-server/src/core/prompts.ts +++ b/packages/mcp-server/src/core/prompts.ts @@ -28,10 +28,7 @@ function registerReadOnlyPrompts(server: McpServer) { 'Explore information about a specific block', { blockNumber: z.string().optional().describe('Block number to explore. If not provided, latest block will be used.'), - network: z - .string() - .optional() - .describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet' etc.) or chain ID. Supports all Sei networks. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, ({ blockNumber, network = DEFAULT_NETWORK }) => ({ messages: [ @@ -54,10 +51,7 @@ function registerReadOnlyPrompts(server: McpServer) { 'Analyze a specific transaction', { txHash: z.string().describe('Transaction hash to analyze'), - network: z - .string() - .optional() - .describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet' etc.) or chain ID. Supports all Sei networks. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, ({ txHash, network = DEFAULT_NETWORK }) => ({ messages: [ @@ -78,10 +72,7 @@ function registerReadOnlyPrompts(server: McpServer) { 'Analyze an EVM address', { address: z.string().describe('Sei 0x address to analyze'), - network: z - .string() - .optional() - .describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet' etc.) or chain ID. Supports all Sei networks. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, ({ address, network = DEFAULT_NETWORK }) => ({ messages: [ @@ -145,7 +136,7 @@ function registerReadOnlyPrompts(server: McpServer) { 'compare_networks', 'Compare Sei networks', { - networkList: z.string().describe("Comma-separated list of networks to compare (e.g., 'sei,sei-testnet,sei-devnet')") + networkList: z.string().describe("Comma-separated list of networks to compare (for example, 'sei,sei-testnet')") }, ({ networkList }) => { const networks = networkList.split(',').map((n) => n.trim()); @@ -171,10 +162,7 @@ function registerReadOnlyPrompts(server: McpServer) { tokenAddress: z.string().describe('Token contract address to analyze'), tokenType: z.string().optional().describe('Type of token to analyze (erc20, erc721/nft, or auto-detect). Defaults to auto.'), tokenId: z.string().optional().describe('Token ID (required for NFT analysis)'), - network: z - .string() - .optional() - .describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet', etc.) or chain ID. Supports all Sei networks. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, ({ tokenAddress, tokenType = 'auto', tokenId, network = DEFAULT_NETWORK }) => { let promptText = ''; diff --git a/packages/mcp-server/src/core/tools.ts b/packages/mcp-server/src/core/tools.ts index 527ce495f..1fc669a50 100644 --- a/packages/mcp-server/src/core/tools.ts +++ b/packages/mcp-server/src/core/tools.ts @@ -32,10 +32,7 @@ function registerReadOnlyTools(server: McpServer) { 'get_chain_info', 'Get information about Sei network', { - network: z - .string() - .optional() - .describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet', etc.) or chain ID. Supports all Sei networks. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, async ({ network = DEFAULT_NETWORK }) => { try { @@ -183,10 +180,7 @@ function registerReadOnlyTools(server: McpServer) { 'Get the native token balance (Sei) for an address', { address: z.string().describe("The wallet address name (e.g., '0x1234...') to check the balance for"), - network: z - .string() - .optional() - .describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet', etc.) or chain ID. Supports all Sei networks. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, async ({ address, network = DEFAULT_NETWORK }) => { try { @@ -278,10 +272,7 @@ function registerReadOnlyTools(server: McpServer) { { tokenAddress: z.string().describe("The contract address name of the ERC20 token (e.g., '0x3894085Ef7Ff0f0aeDf52E2A2704928d1Ec074F1')"), ownerAddress: z.string().describe("The wallet address name to check the balance for (e.g., '0x1234...')"), - network: z - .string() - .optional() - .describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet', etc.) or chain ID. Supports all Sei networks. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, async ({ tokenAddress, ownerAddress, network = DEFAULT_NETWORK }) => { try { @@ -329,7 +320,7 @@ function registerReadOnlyTools(server: McpServer) { 'Get detailed information about a specific transaction by its hash. Includes sender, recipient, value, data, and more.', { txHash: z.string().describe("The transaction hash to look up (e.g., '0x1234...')"), - network: z.string().optional().describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet', etc.) or chain ID. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, async ({ txHash, network = DEFAULT_NETWORK }) => { try { @@ -458,7 +449,7 @@ function registerWalletTools(server: McpServer) { { to: z.string().describe("The recipient address (e.g., '0x1234...'"), amount: z.string().describe("Amount to send in SEI (or the native token of the network), as a string (e.g., '0.1')"), - network: z.string().optional().describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet') or chain ID. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, async ({ to, amount, network = DEFAULT_NETWORK }) => { try { @@ -504,7 +495,7 @@ function registerWalletTools(server: McpServer) { tokenAddress: z.string().describe('The address of the ERC20 token contract'), toAddress: z.string().describe('The recipient address'), amount: z.string().describe("The amount of tokens to send (in token units, e.g., '10' for 10 tokens)"), - network: z.string().optional().describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet') or chain ID. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, async ({ tokenAddress, toAddress, amount, network = DEFAULT_NETWORK }) => { try { @@ -556,7 +547,7 @@ function registerWalletTools(server: McpServer) { .describe( "The amount of tokens to approve in token units, not wei (e.g., '1000' to approve spending 1000 tokens). Use a very large number for unlimited approval." ), - network: z.string().optional().describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet') or chain ID. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, async ({ tokenAddress, spenderAddress, amount, network = DEFAULT_NETWORK }) => { try { @@ -604,10 +595,7 @@ function registerWalletTools(server: McpServer) { tokenAddress: z.string().describe("The contract address of the NFT collection (e.g., '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D')"), tokenId: z.string().describe("The ID of the specific NFT to transfer (e.g., '1234')"), toAddress: z.string().describe('The recipient wallet address that will receive the NFT'), - network: z - .string() - .optional() - .describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet') or chain ID. Most NFTs are on Sei mainnet, which is the default.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Most NFTs are on Sei mainnet, which is the default.") }, async ({ tokenAddress, tokenId, toAddress, network = DEFAULT_NETWORK }) => { try { @@ -657,10 +645,7 @@ function registerWalletTools(server: McpServer) { tokenId: z.string().describe("The ID of the specific token to transfer (e.g., '1234')"), amount: z.string().describe("The quantity of tokens to send (e.g., '1' for a single NFT or '10' for 10 fungible tokens)"), toAddress: z.string().describe('The recipient wallet address that will receive the tokens'), - network: z - .string() - .optional() - .describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet') or chain ID. ERC1155 tokens exist across many networks. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, async ({ tokenAddress, tokenId, amount, toAddress, network = DEFAULT_NETWORK }) => { try { @@ -708,10 +693,7 @@ function registerWalletTools(server: McpServer) { tokenAddress: z.string().describe("The contract address of the ERC20 token to transfer (e.g., '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48')"), toAddress: z.string().describe("The recipient address that will receive the tokens (e.g., '0x1234...')"), amount: z.string().describe("Amount of tokens to send as a string (e.g., '100' for 100 tokens). This will be adjusted for the token's decimals."), - network: z - .string() - .optional() - .describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet') or chain ID. Supports all Sei networks. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, async ({ tokenAddress, toAddress, amount, network = DEFAULT_NETWORK }) => { try { @@ -762,7 +744,7 @@ function registerWalletTools(server: McpServer) { abi: z.array(z.any()).describe('The ABI (Application Binary Interface) of the smart contract function, as a JSON array'), functionName: z.string().describe("The name of the function to call on the contract (e.g., 'balanceOf')"), args: z.array(z.any()).optional().describe("The arguments to pass to the function, as an array (e.g., ['0x1234...'])"), - network: z.string().optional().describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet') or chain ID. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, async ({ contractAddress, abi, functionName, args = [], network = DEFAULT_NETWORK }) => { try { @@ -809,7 +791,7 @@ function registerWalletTools(server: McpServer) { abi: z.array(z.any()).describe('The ABI (Application Binary Interface) of the smart contract function, as a JSON array'), functionName: z.string().describe("The name of the function to call on the contract (e.g., 'transfer')"), args: z.array(z.any()).describe("The arguments to pass to the function, as an array (e.g., ['0x1234...', '1000000000000000000'])"), - network: z.string().optional().describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet') or chain ID. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, async ({ contractAddress, abi, functionName, args, network = DEFAULT_NETWORK }) => { try { @@ -868,7 +850,7 @@ function registerWalletTools(server: McpServer) { .describe( "The constructor arguments to pass during deployment, as an array (e.g., ['param1', 'param2']). Leave empty if constructor has no parameters." ), - network: z.string().optional().describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet') or chain ID. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, async ({ bytecode, abi, args = [], network = DEFAULT_NETWORK }) => { try { @@ -918,10 +900,7 @@ function registerWalletTools(server: McpServer) { 'Check if an address is a smart contract or an externally owned account (EOA)', { address: z.string().describe("The wallet or contract address to check (e.g., '0x1234...')"), - network: z - .string() - .optional() - .describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet', etc.) or chain ID. Supports all Sei networks. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, async ({ address, network = DEFAULT_NETWORK }) => { try { @@ -964,7 +943,7 @@ function registerWalletTools(server: McpServer) { 'Get comprehensive information about an ERC20 token including name, symbol, decimals, total supply, and other metadata. Use this to analyze any token on EVM chains.', { tokenAddress: z.string().describe("The contract address of the ERC20 token (e.g., '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48')"), - network: z.string().optional().describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet') or chain ID. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, async ({ tokenAddress, network = DEFAULT_NETWORK }) => { try { @@ -1123,10 +1102,7 @@ function registerWalletTools(server: McpServer) { tokenAddress: z.string().describe("The contract address of the NFT collection (e.g., '0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D')"), tokenId: z.string().describe("The ID of the NFT to check (e.g., '1234')"), ownerAddress: z.string().describe("The wallet address to check ownership against (e.g., '0x1234...')"), - network: z - .string() - .optional() - .describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet' etc.) or chain ID. Supports all Sei networks. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, async ({ tokenAddress, tokenId, ownerAddress, network = DEFAULT_NETWORK }) => { try { @@ -1172,10 +1148,7 @@ function registerWalletTools(server: McpServer) { { tokenAddress: z.string().describe("The contract address of the ERC1155 token collection (e.g., '0x5B6D32f2B55b62da7a8cd553857EB6Dc26bFDC63')"), tokenId: z.string().describe("The ID of the specific token to query metadata for (e.g., '1234')"), - network: z - .string() - .optional() - .describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet') or chain ID. ERC1155 tokens exist across many networks. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, async ({ tokenAddress, tokenId, network = DEFAULT_NETWORK }) => { try { @@ -1219,10 +1192,7 @@ function registerWalletTools(server: McpServer) { { tokenAddress: z.string().describe("The contract address of the NFT collection (e.g., '0x5B6D32f2B55b62da7a8cd553857EB6Dc26bFDC63')"), ownerAddress: z.string().describe("The wallet address to check the NFT balance for (e.g., '0x1234...')"), - network: z - .string() - .optional() - .describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet') or chain ID. Most NFTs are on Sei mainnet, which is the default.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Most NFTs are on Sei mainnet, which is the default.") }, async ({ tokenAddress, ownerAddress, network = DEFAULT_NETWORK }) => { try { @@ -1267,10 +1237,7 @@ function registerWalletTools(server: McpServer) { tokenAddress: z.string().describe("The contract address of the ERC1155 token collection (e.g., '0x5B6D32f2B55b62da7a8cd553857EB6Dc26bFDC63')"), tokenId: z.string().describe("The ID of the specific token to check the balance for (e.g., '1234')"), ownerAddress: z.string().describe("The wallet address to check the token balance for (e.g., '0x1234...')"), - network: z - .string() - .optional() - .describe("Network name (e.g., 'sei', 'sei-testnet', 'sei-devnet') or chain ID. ERC1155 tokens exist across many networks. Defaults to Sei mainnet.") + network: z.string().optional().describe("Network name ('sei' or 'sei-testnet') or chain ID. Defaults to Sei mainnet.") }, async ({ tokenAddress, tokenId, ownerAddress, network = DEFAULT_NETWORK }) => { try { diff --git a/packages/mcp-server/src/server/args.ts b/packages/mcp-server/src/server/args.ts index fecd42c9b..ac503f61d 100644 --- a/packages/mcp-server/src/server/args.ts +++ b/packages/mcp-server/src/server/args.ts @@ -16,8 +16,7 @@ const DEFAULT_CONFIG = { }, rpc: { mainnet: '', - testnet: '', - devnet: '' + testnet: '' } }; @@ -50,8 +49,7 @@ const loadConfig = () => { }, rpc: { mainnet: getEnvValue('MAINNET_RPC_URL', DEFAULT_CONFIG.rpc.mainnet), - testnet: getEnvValue('TESTNET_RPC_URL', DEFAULT_CONFIG.rpc.testnet), - devnet: getEnvValue('DEVNET_RPC_URL', DEFAULT_CONFIG.rpc.devnet) + testnet: getEnvValue('TESTNET_RPC_URL', DEFAULT_CONFIG.rpc.testnet) } }; @@ -111,7 +109,6 @@ Environment Variables: WALLET_MODE Wallet mode: private-key, disabled (default: disabled) MAINNET_RPC_URL Custom RPC URL for Sei mainnet (optional) TESTNET_RPC_URL Custom RPC URL for Sei testnet (optional) - DEVNET_RPC_URL Custom RPC URL for Sei devnet (optional) Security Note: Wallet mode is only supported with stdio transport. HTTP transports block diff --git a/packages/mcp-server/src/tests/core/chains.test.ts b/packages/mcp-server/src/tests/core/chains.test.ts index 70dd6060d..ee325b935 100644 --- a/packages/mcp-server/src/tests/core/chains.test.ts +++ b/packages/mcp-server/src/tests/core/chains.test.ts @@ -1,6 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, jest, test } from 'bun:test'; import type { Chain } from 'viem'; -import { sei, seiDevnet, seiTestnet } from 'viem/chains'; +import { sei, seiTestnet } from 'viem/chains'; import { chainMap, DEFAULT_CHAIN_ID, @@ -32,19 +32,16 @@ describe('chains module', () => { test('chainMap contains expected chains', () => { expect(chainMap[1329]).toBe(sei); expect(chainMap[1328]).toBe(seiTestnet); - expect(chainMap[713715]).toBe(seiDevnet); }); test('networkNameMap contains expected mappings', () => { expect(networkNameMap.sei).toBe(1329); expect(networkNameMap['sei-testnet']).toBe(1328); - expect(networkNameMap['sei-devnet']).toBe(713715); }); test('rpcUrlMap contains expected URLs', () => { expect(rpcUrlMap[1329]).toBe('https://evm-rpc.sei-apis.com'); expect(rpcUrlMap[1328]).toBe('https://evm-rpc-testnet.sei-apis.com'); - expect(rpcUrlMap[713715]).toBe('https://evm-rpc-arctic-1.sei-apis.com'); }); }); @@ -53,25 +50,21 @@ describe('chains module', () => { test('resolves number chain IDs directly', () => { expect(resolveChainId(1329)).toBe(1329); expect(resolveChainId(1328)).toBe(1328); - expect(resolveChainId(713715)).toBe(713715); }); test('resolves network names to chain IDs', () => { expect(resolveChainId('sei')).toBe(1329); expect(resolveChainId('sei-testnet')).toBe(1328); - expect(resolveChainId('sei-devnet')).toBe(713715); }); test('resolves case-insensitive network names', () => { expect(resolveChainId('SEI')).toBe(1329); expect(resolveChainId('Sei-Testnet')).toBe(1328); - expect(resolveChainId('SEI-DEVNET')).toBe(713715); }); test('resolves string numbers to chain IDs', () => { expect(resolveChainId('1329')).toBe(1329); expect(resolveChainId('1328')).toBe(1328); - expect(resolveChainId('713715')).toBe(713715); }); test('resolves hex-encoded chain IDs', () => { @@ -92,13 +85,11 @@ describe('chains module', () => { test('returns chain for numeric chain ID', () => { expect(getChain(1329)).toBe(sei); expect(getChain(1328)).toBe(seiTestnet); - expect(getChain(713715)).toBe(seiDevnet); }); test('returns chain for network name', () => { expect(getChain('sei')).toBe(sei); expect(getChain('sei-testnet')).toBe(seiTestnet); - expect(getChain('sei-devnet')).toBe(seiDevnet); }); test('returns sei chain when network name exists but chain mapping is missing', () => { @@ -111,7 +102,7 @@ describe('chains module', () => { } finally { // Restore the original map for (const key of Object.keys(networkNameMap)) { - if (key !== 'sei' && key !== 'sei-testnet' && key !== 'sei-devnet') { + if (key !== 'sei' && key !== 'sei-testnet') { delete networkNameMap[key]; } } @@ -121,7 +112,6 @@ describe('chains module', () => { test('returns chain for case-insensitive network name', () => { expect(getChain('SEI')).toBe(sei); expect(getChain('Sei-Testnet')).toBe(seiTestnet); - expect(getChain('SEI-DEVNET')).toBe(seiDevnet); }); test('returns default chain when no parameter is provided', () => { @@ -147,13 +137,11 @@ describe('chains module', () => { test('returns correct RPC URL for numeric chain ID', () => { expect(getRpcUrl(1329)).toBe('https://evm-rpc.sei-apis.com'); expect(getRpcUrl(1328)).toBe('https://evm-rpc-testnet.sei-apis.com'); - expect(getRpcUrl(713715)).toBe('https://evm-rpc-arctic-1.sei-apis.com'); }); test('returns correct RPC URL for network name', () => { expect(getRpcUrl('sei')).toBe('https://evm-rpc.sei-apis.com'); expect(getRpcUrl('sei-testnet')).toBe('https://evm-rpc-testnet.sei-apis.com'); - expect(getRpcUrl('sei-devnet')).toBe('https://evm-rpc-arctic-1.sei-apis.com'); }); test('returns default RPC URL for unknown chain ID', () => { @@ -173,7 +161,6 @@ describe('chains module', () => { // Check that all expected networks are included expect(networks).toContain('sei'); expect(networks).toContain('sei-testnet'); - expect(networks).toContain('sei-devnet'); // Check that the list is sorted expect(networks).toEqual([...networks].sort()); diff --git a/packages/mcp-server/src/tests/server/args.test.ts b/packages/mcp-server/src/tests/server/args.test.ts index 63ead2dd6..3ce9d1302 100644 --- a/packages/mcp-server/src/tests/server/args.test.ts +++ b/packages/mcp-server/src/tests/server/args.test.ts @@ -37,7 +37,6 @@ describe('Args Module', () => { delete process.env.PRIVATE_KEY; delete process.env.MAINNET_RPC_URL; delete process.env.TESTNET_RPC_URL; - delete process.env.DEVNET_RPC_URL; // Import mocked modules const { Command } = await import('commander'); @@ -183,7 +182,6 @@ describe('Args Module', () => { it('should handle all RPC URL environment variables', () => { process.env.MAINNET_RPC_URL = 'https://mainnet.example.com'; process.env.TESTNET_RPC_URL = 'https://testnet.example.com'; - process.env.DEVNET_RPC_URL = 'https://devnet.example.com'; // RPC URLs are loaded but not returned in the final config // This tests that they don't cause errors diff --git a/packages/mcp-server/src/tests/server/server.test.ts b/packages/mcp-server/src/tests/server/server.test.ts index 68b577d9e..e52b5cbb0 100644 --- a/packages/mcp-server/src/tests/server/server.test.ts +++ b/packages/mcp-server/src/tests/server/server.test.ts @@ -79,7 +79,7 @@ describe('Server Module', () => { name: '@sei-js/mcp-server', version: '1.0.0' }); - mockGetSupportedNetworks.mockReturnValue(['sei', 'sei-testnet', 'sei-devnet']); + mockGetSupportedNetworks.mockReturnValue(['sei', 'sei-testnet']); // Spy on console.error and process.exit consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); @@ -117,7 +117,7 @@ describe('Server Module', () => { await getServer(); - expect(consoleErrorSpy).toHaveBeenCalledWith('Supported networks:', 'sei, sei-testnet, sei-devnet'); + expect(consoleErrorSpy).toHaveBeenCalledWith('Supported networks:', 'sei, sei-testnet'); }); it('should handle server initialization error and exit', async () => { diff --git a/packages/precompiles/README.md b/packages/precompiles/README.md index db20ff3ee..36a87175c 100644 --- a/packages/precompiles/README.md +++ b/packages/precompiles/README.md @@ -24,3 +24,12 @@ Works seamlessly with your favorite Ethereum development tools: - **Ethers.js** - Contract factories and utilities - **Wagmi** - React hooks for precompile contracts - **Hardhat/Foundry** - Testing and deployment + +## Sei chain definitions + +Import the canonical Sei mainnet and testnet definitions from the package root or the `viem` entrypoint: + +```ts +import { sei, seiTestnet } from '@sei-js/precompiles'; +// or: import { sei, seiTestnet } from '@sei-js/precompiles/viem'; +``` diff --git a/packages/precompiles/package.json b/packages/precompiles/package.json index 3721c111f..aaa878ba1 100644 --- a/packages/precompiles/package.json +++ b/packages/precompiles/package.json @@ -70,12 +70,12 @@ }, "peerDependencies": { "ethers": "^6.0.0", - "viem": "2.x" + "viem": "^2.55.16" }, "devDependencies": { "esbuild": "^0.28.2", "ethers": "^6.0.0", "typescript": "^5.8.2", - "viem": "2.37.2" + "viem": "2.55.16" } } diff --git a/packages/precompiles/src/viem/__tests__/chain.spec.ts b/packages/precompiles/src/viem/__tests__/chain.spec.ts index 5f4aed376..a372a1dc7 100644 --- a/packages/precompiles/src/viem/__tests__/chain.spec.ts +++ b/packages/precompiles/src/viem/__tests__/chain.spec.ts @@ -1,6 +1,6 @@ import { sei as viemSei, seiTestnet as viemSeiTestnet } from 'viem/chains'; import * as packageRoot from '../../index'; -import { sei, seiDevnet, seiLocal, seiTestnet } from '../chain'; +import { sei, seiLocal, seiTestnet } from '../chain'; import * as viemEntryPoint from '../index'; describe('Sei chain definitions', () => { @@ -14,25 +14,6 @@ describe('Sei chain definitions', () => { expect(seiTestnet.id).toBe(1328); }); - it('defines Sei devnet', () => { - expect(seiDevnet).toMatchObject({ - id: 713715, - name: 'Sei Devnet', - nativeCurrency: { - name: 'Sei', - symbol: 'SEI', - decimals: 18 - }, - rpcUrls: { - default: { - http: ['https://evm-rpc-arctic-1.sei-apis.com'], - webSocket: ['wss://evm-ws-arctic-1.sei-apis.com'] - } - }, - testnet: true - }); - }); - it('defines Sei local', () => { expect(seiLocal).toMatchObject({ id: 713714, @@ -54,7 +35,6 @@ describe('Sei chain definitions', () => { for (const entryPoint of [viemEntryPoint, packageRoot]) { expect(entryPoint.sei).toBe(sei); expect(entryPoint.seiTestnet).toBe(seiTestnet); - expect(entryPoint.seiDevnet).toBe(seiDevnet); expect(entryPoint.seiLocal).toBe(seiLocal); } }); diff --git a/packages/precompiles/src/viem/chain.ts b/packages/precompiles/src/viem/chain.ts index 759c519e4..f68907798 100644 --- a/packages/precompiles/src/viem/chain.ts +++ b/packages/precompiles/src/viem/chain.ts @@ -2,23 +2,6 @@ import { defineChain } from 'viem'; export { sei, seiTestnet } from 'viem/chains'; -/** - * The Viem chain definition for Sei devnet. - * @category Chain - */ -export const seiDevnet = defineChain({ - id: 713715, - name: 'Sei Devnet', - nativeCurrency: { name: 'Sei', symbol: 'SEI', decimals: 18 }, - rpcUrls: { - default: { - http: ['https://evm-rpc-arctic-1.sei-apis.com'], - webSocket: ['wss://evm-ws-arctic-1.sei-apis.com'] - } - }, - testnet: true -}); - /** * The Viem chain definition for the Sei local chain. * @category Chain diff --git a/packages/registry/src/chain-info/__tests__/index.spec.ts b/packages/registry/src/chain-info/__tests__/index.spec.ts index 8d3ca3602..de6dfa8d0 100644 --- a/packages/registry/src/chain-info/__tests__/index.spec.ts +++ b/packages/registry/src/chain-info/__tests__/index.spec.ts @@ -3,7 +3,7 @@ import { CHAIN_INFO } from '../index'; describe('ChainInfo Tests', () => { it('has the required properties with correct types', () => { expect(typeof CHAIN_INFO.chain_name).toBe('string'); - expect(['mainnet', 'testnet', 'devnet']).toContain(CHAIN_INFO.network_type); + expect(['mainnet', 'testnet']).toContain(CHAIN_INFO.network_type); expect(typeof CHAIN_INFO.chain_id).toBe('string'); expect(typeof CHAIN_INFO.daemon_name).toBe('string'); expect(typeof CHAIN_INFO.bech32_prefix).toBe('string'); diff --git a/packages/registry/src/chain-info/index.ts b/packages/registry/src/chain-info/index.ts index f41b68432..2a74ff07d 100644 --- a/packages/registry/src/chain-info/index.ts +++ b/packages/registry/src/chain-info/index.ts @@ -6,9 +6,9 @@ import ChainInfoJSON from '../../chain-registry/chain_info.json'; export interface ChainInfo { /** The name of the chain. (Sei) */ chain_name: string; - /** The type of network, indicating whether it's a mainnet, testnet, or devnet. */ - network_type: 'mainnet' | 'testnet' | 'devnet'; - /** The unique identifier for the Sei network. 'pacific-1' | 'atlantic-2' | 'arctic-1' */ + /** The type of network. */ + network_type: 'mainnet' | 'testnet'; + /** The unique identifier for the Sei network. */ chain_id: string; /** The name of the daemon process that runs the node software for the blockchain. (seid) */ daemon_name: string; diff --git a/packages/registry/src/gas/__tests__/index.spec.ts b/packages/registry/src/gas/__tests__/index.spec.ts index 35e93e4bc..03b62c8e4 100644 --- a/packages/registry/src/gas/__tests__/index.spec.ts +++ b/packages/registry/src/gas/__tests__/index.spec.ts @@ -4,7 +4,9 @@ import { type ChainGasInfo, GAS_INFO, type ModuleAdjustments } from '../index'; describe('GasInfo Tests', () => { // Check if GasInfo contains all expected networks it('contains all required networks', () => { - const expectedNetworks: Network[] = ['pacific-1', 'atlantic-2', 'arctic-1']; + const expectedNetworks: Network[] = ['pacific-1', 'atlantic-2']; + expect(Object.keys(GAS_INFO).sort()).toEqual([...expectedNetworks].sort()); + for (const network of expectedNetworks) { expect(GAS_INFO).toHaveProperty(network); } diff --git a/packages/registry/src/gas/index.ts b/packages/registry/src/gas/index.ts index b61894402..eae747ae9 100644 --- a/packages/registry/src/gas/index.ts +++ b/packages/registry/src/gas/index.ts @@ -50,4 +50,7 @@ type GasInfo = { * console.log(pacific1Info.denom); // 'usei' * ``` */ -export const GAS_INFO: GasInfo = GasInfoJSON as GasInfo; +export const GAS_INFO: GasInfo = { + 'pacific-1': GasInfoJSON['pacific-1'], + 'atlantic-2': GasInfoJSON['atlantic-2'] +} as GasInfo; diff --git a/packages/registry/src/ibc/__tests__/index.spec.ts b/packages/registry/src/ibc/__tests__/index.spec.ts index 4d26f5b9d..78ca04b87 100644 --- a/packages/registry/src/ibc/__tests__/index.spec.ts +++ b/packages/registry/src/ibc/__tests__/index.spec.ts @@ -4,7 +4,9 @@ import { IBC_INFO } from '../index'; describe('IBCInfo Tests', () => { // Check if IBCInfo contains all expected networks it('contains all required networks', () => { - const expectedNetworks: Network[] = ['pacific-1', 'atlantic-2', 'arctic-1']; + const expectedNetworks: Network[] = ['pacific-1', 'atlantic-2']; + expect(Object.keys(IBC_INFO).sort()).toEqual([...expectedNetworks].sort()); + for (const network of expectedNetworks) { expect(IBC_INFO).toHaveProperty(network); } diff --git a/packages/registry/src/ibc/index.ts b/packages/registry/src/ibc/index.ts index aecff28b3..00fb8fec4 100644 --- a/packages/registry/src/ibc/index.ts +++ b/packages/registry/src/ibc/index.ts @@ -50,4 +50,7 @@ type IBCInfo = { * const pacific1 = IBC_INFO['pacific-1'].find((ibcInfo) => ibcInfo.counterparty_chain_name === 'cosmoshub-4'); * ``` */ -export const IBC_INFO: IBCInfo = IBCInfoJSON as IBCInfo; +export const IBC_INFO: IBCInfo = { + 'pacific-1': IBCInfoJSON['pacific-1'], + 'atlantic-2': IBCInfoJSON['atlantic-2'] +} as IBCInfo; diff --git a/packages/registry/src/index.ts b/packages/registry/src/index.ts index 33cff832a..80bb4cb49 100644 --- a/packages/registry/src/index.ts +++ b/packages/registry/src/index.ts @@ -7,13 +7,12 @@ export * from './tokens'; /** * A TypeScript type representing the official Sei network chain identifiers. */ -export type Network = 'pacific-1' | 'atlantic-2' | 'arctic-1'; +export type Network = 'pacific-1' | 'atlantic-2'; /** * An object for referencing the official Sei network chain identifiers. */ export const CHAIN_IDS = { mainnet: 'pacific-1', - testnet: 'atlantic-2', - devnet: 'arctic-1' + testnet: 'atlantic-2' }; diff --git a/packages/registry/src/networks/__tests__/index.spec.ts b/packages/registry/src/networks/__tests__/index.spec.ts index 0cf794290..1a1e4f023 100644 --- a/packages/registry/src/networks/__tests__/index.spec.ts +++ b/packages/registry/src/networks/__tests__/index.spec.ts @@ -2,7 +2,9 @@ import { NETWORKS } from '../index'; describe('Networks configuration', () => { it('should contain configurations for all expected Sei networks', () => { - const expectedNetworkIds = ['pacific-1', 'atlantic-2', 'arctic-1']; + const expectedNetworkIds = ['pacific-1', 'atlantic-2']; + + expect(Object.keys(NETWORKS).sort()).toEqual([...expectedNetworkIds].sort()); for (const id of expectedNetworkIds) { expect(NETWORKS).toHaveProperty(id); diff --git a/packages/registry/src/networks/index.ts b/packages/registry/src/networks/index.ts index 6aaaae6c0..8cc7d9f73 100644 --- a/packages/registry/src/networks/index.ts +++ b/packages/registry/src/networks/index.ts @@ -31,8 +31,8 @@ interface Explorer { export interface NetworkConfig { /** The unique identifier of the Sei network. */ chainId: string; - /** The type of the network, which can be mainnet, testnet, or devnet. */ - network_type: 'mainnet' | 'testnet' | 'devnet'; + /** The type of the network. */ + network_type: 'mainnet' | 'testnet'; /** An array of RPC endpoints available for the network. */ rpc: Endpoint[]; /** An array of REST endpoints for accessing the network's RESTful services. */ @@ -45,7 +45,7 @@ export interface NetworkConfig { evm_ws?: Endpoint[]; /** Optional array of blockchain explorer that support this network. */ explorers?: Explorer[]; - /** An array of faucet endpoints for obtaining test tokens on networks like testnets or devnets. */ + /** An array of faucet endpoints for obtaining test tokens on testnets. */ faucets?: Endpoint[]; } @@ -67,4 +67,7 @@ type NetworksConfig = { * const pacific1 = NETWORKS.find((network) => network.chainId === 'pacific-1'); * ``` */ -export const NETWORKS: NetworksConfig = NetworksJSON as NetworksConfig; +export const NETWORKS: NetworksConfig = { + 'pacific-1': NetworksJSON['pacific-1'], + 'atlantic-2': NetworksJSON['atlantic-2'] +} as NetworksConfig; diff --git a/packages/registry/src/tokens/__tests__/index.spec.ts b/packages/registry/src/tokens/__tests__/index.spec.ts index 50f182954..693ce5615 100644 --- a/packages/registry/src/tokens/__tests__/index.spec.ts +++ b/packages/registry/src/tokens/__tests__/index.spec.ts @@ -3,7 +3,9 @@ import { type DenomUnit, TOKEN_LIST } from '../index'; describe('AssetList Tests', () => { it('should have the correct structure for each network', () => { - const networks: Network[] = ['pacific-1', 'atlantic-2', 'arctic-1']; + const networks: Network[] = ['pacific-1', 'atlantic-2']; + expect(Object.keys(TOKEN_LIST).sort()).toEqual([...networks].sort()); + for (const network of networks) { expect(Array.isArray(TOKEN_LIST[network])).toBeTruthy(); for (const asset of TOKEN_LIST[network]) { diff --git a/packages/registry/src/tokens/index.ts b/packages/registry/src/tokens/index.ts index 580040e39..dbd67beba 100644 --- a/packages/registry/src/tokens/index.ts +++ b/packages/registry/src/tokens/index.ts @@ -69,4 +69,7 @@ type SeiTokens = { * const uSei = TOKEN_LIST['pacific-1'].find((asset) => asset.symbol === 'sei'); * ``` */ -export const TOKEN_LIST: SeiTokens = TokenListJSON as unknown as SeiTokens; +export const TOKEN_LIST: SeiTokens = { + 'pacific-1': TokenListJSON['pacific-1'], + 'atlantic-2': TokenListJSON['atlantic-2'] +} as unknown as SeiTokens; diff --git a/scripts/check-precompile-exports.ts b/scripts/check-precompile-exports.ts index 88311a21b..43ae10d1d 100644 --- a/scripts/check-precompile-exports.ts +++ b/scripts/check-precompile-exports.ts @@ -44,7 +44,6 @@ if (typeof ethersModule.getBankPrecompileEthersV6Contract !== 'function') { for (const [name, chainId] of [ ['sei', 1329], ['seiTestnet', 1328], - ['seiDevnet', 713715], ['seiLocal', 713714] ] as const) { if (viemModule[name]?.id !== chainId) { From 8f47ee2963405dab534cb7c30addde1b3fdcbd3a Mon Sep 17 00:00:00 2001 From: alexander-sei Date: Sat, 15 Aug 2026 14:13:20 +0200 Subject: [PATCH 3/3] fix: harden supported network configuration Mark the Viem peer-floor change as major and derive registry filtering from the shared chain ID map. Co-authored-by: Cursor --- .changeset/restore-sei-chain-exports.md | 2 +- packages/registry/package.json | 2 +- packages/registry/src/gas/index.ts | 7 +-- packages/registry/src/ibc/index.ts | 7 +-- packages/registry/src/index.ts | 15 +----- packages/registry/src/networks/index.ts | 7 +-- packages/registry/src/supported-networks.ts | 18 +++++++ packages/registry/src/tokens/index.ts | 7 +-- scripts/build-registry.ts | 55 +++++++++++++++++++++ 9 files changed, 85 insertions(+), 35 deletions(-) create mode 100644 packages/registry/src/supported-networks.ts create mode 100644 scripts/build-registry.ts diff --git a/.changeset/restore-sei-chain-exports.md b/.changeset/restore-sei-chain-exports.md index 10364b564..1cc7016b7 100644 --- a/.changeset/restore-sei-chain-exports.md +++ b/.changeset/restore-sei-chain-exports.md @@ -1,5 +1,5 @@ --- -'@sei-js/precompiles': minor +'@sei-js/precompiles': major --- Export Viem's canonical Sei mainnet and testnet definitions from the package root and `viem` entrypoint. Viem 2.55.16 or newer is now required. diff --git a/packages/registry/package.json b/packages/registry/package.json index f88501384..4df0b310d 100644 --- a/packages/registry/package.json +++ b/packages/registry/package.json @@ -13,7 +13,7 @@ "scripts": { "build": "rm -rf dist && bun run build:types && bun run build:esm", "build:types": "tsc --project ./tsconfig.json && printf \"export * from './src/index.js';\\n\" > dist/index.d.ts && bun ../../scripts/fix-dts-extensions.ts dist", - "build:esm": "esbuild src/index.ts --bundle --format=esm --platform=neutral --packages=external --target=es2020 --outfile=dist/index.js", + "build:esm": "bun ../../scripts/build-registry.ts", "test": "bun test --isolate src" }, "exports": { diff --git a/packages/registry/src/gas/index.ts b/packages/registry/src/gas/index.ts index eae747ae9..7d2912090 100644 --- a/packages/registry/src/gas/index.ts +++ b/packages/registry/src/gas/index.ts @@ -1,5 +1,5 @@ import GasInfoJSON from '../../chain-registry/gas.json'; -import type { Network } from '../index'; +import { type Network, pickSupportedNetworks } from '../supported-networks'; /** * Defines the gas price adjustments for specific modules within the Sei blockchain, @@ -50,7 +50,4 @@ type GasInfo = { * console.log(pacific1Info.denom); // 'usei' * ``` */ -export const GAS_INFO: GasInfo = { - 'pacific-1': GasInfoJSON['pacific-1'], - 'atlantic-2': GasInfoJSON['atlantic-2'] -} as GasInfo; +export const GAS_INFO: GasInfo = pickSupportedNetworks(GasInfoJSON); diff --git a/packages/registry/src/ibc/index.ts b/packages/registry/src/ibc/index.ts index 00fb8fec4..a33733b67 100644 --- a/packages/registry/src/ibc/index.ts +++ b/packages/registry/src/ibc/index.ts @@ -1,5 +1,5 @@ import IBCInfoJSON from '../../chain-registry/ibc_info.json'; -import type { Network } from '../index'; +import { type Network, pickSupportedNetworks } from '../supported-networks'; /** * Represents information about an IBC channel, facilitating communication @@ -50,7 +50,4 @@ type IBCInfo = { * const pacific1 = IBC_INFO['pacific-1'].find((ibcInfo) => ibcInfo.counterparty_chain_name === 'cosmoshub-4'); * ``` */ -export const IBC_INFO: IBCInfo = { - 'pacific-1': IBCInfoJSON['pacific-1'], - 'atlantic-2': IBCInfoJSON['atlantic-2'] -} as IBCInfo; +export const IBC_INFO: IBCInfo = pickSupportedNetworks(IBCInfoJSON); diff --git a/packages/registry/src/index.ts b/packages/registry/src/index.ts index 80bb4cb49..7315c6fb9 100644 --- a/packages/registry/src/index.ts +++ b/packages/registry/src/index.ts @@ -2,17 +2,6 @@ export * from './chain-info'; export * from './gas'; export * from './ibc'; export * from './networks'; +export type { Network } from './supported-networks'; +export { CHAIN_IDS } from './supported-networks'; export * from './tokens'; - -/** - * A TypeScript type representing the official Sei network chain identifiers. - */ -export type Network = 'pacific-1' | 'atlantic-2'; - -/** - * An object for referencing the official Sei network chain identifiers. - */ -export const CHAIN_IDS = { - mainnet: 'pacific-1', - testnet: 'atlantic-2' -}; diff --git a/packages/registry/src/networks/index.ts b/packages/registry/src/networks/index.ts index 8cc7d9f73..eb03d0aaf 100644 --- a/packages/registry/src/networks/index.ts +++ b/packages/registry/src/networks/index.ts @@ -1,5 +1,5 @@ import NetworksJSON from '../../chain-registry/chains.json'; -import type { Network } from '../index'; +import { type Network, pickSupportedNetworks } from '../supported-networks'; /** * Describes an endpoint with a provider name and its associated URL. @@ -67,7 +67,4 @@ type NetworksConfig = { * const pacific1 = NETWORKS.find((network) => network.chainId === 'pacific-1'); * ``` */ -export const NETWORKS: NetworksConfig = { - 'pacific-1': NetworksJSON['pacific-1'], - 'atlantic-2': NetworksJSON['atlantic-2'] -} as NetworksConfig; +export const NETWORKS: NetworksConfig = pickSupportedNetworks(NetworksJSON) as NetworksConfig; diff --git a/packages/registry/src/supported-networks.ts b/packages/registry/src/supported-networks.ts new file mode 100644 index 000000000..93feb1da8 --- /dev/null +++ b/packages/registry/src/supported-networks.ts @@ -0,0 +1,18 @@ +/** + * Official Sei network chain identifiers supported by this package. + */ +export const CHAIN_IDS = { + mainnet: 'pacific-1', + testnet: 'atlantic-2' +} as const; + +/** + * A supported Sei network chain identifier. + */ +export type Network = (typeof CHAIN_IDS)[keyof typeof CHAIN_IDS]; + +const SUPPORTED_NETWORKS: readonly Network[] = Object.values(CHAIN_IDS); + +export function pickSupportedNetworks>(source: T): Pick { + return Object.fromEntries(SUPPORTED_NETWORKS.map((network) => [network, source[network]])) as Pick; +} diff --git a/packages/registry/src/tokens/index.ts b/packages/registry/src/tokens/index.ts index dbd67beba..fc6a62d64 100644 --- a/packages/registry/src/tokens/index.ts +++ b/packages/registry/src/tokens/index.ts @@ -1,5 +1,5 @@ import TokenListJSON from '../../community-assetlist/assetlist.json'; -import type { Network } from '../index'; +import { type Network, pickSupportedNetworks } from '../supported-networks'; /** * DenomUnit represents a struct that describes a given @@ -69,7 +69,4 @@ type SeiTokens = { * const uSei = TOKEN_LIST['pacific-1'].find((asset) => asset.symbol === 'sei'); * ``` */ -export const TOKEN_LIST: SeiTokens = { - 'pacific-1': TokenListJSON['pacific-1'], - 'atlantic-2': TokenListJSON['atlantic-2'] -} as unknown as SeiTokens; +export const TOKEN_LIST: SeiTokens = pickSupportedNetworks(TokenListJSON) as unknown as SeiTokens; diff --git a/scripts/build-registry.ts b/scripts/build-registry.ts new file mode 100644 index 000000000..fc6ebc9a0 --- /dev/null +++ b/scripts/build-registry.ts @@ -0,0 +1,55 @@ +import { readFile } from 'node:fs/promises'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { build, type Plugin } from 'esbuild'; +import { CHAIN_IDS } from '../packages/registry/src/supported-networks'; + +const root = join(dirname(fileURLToPath(import.meta.url)), '..'); +const packageDir = join(root, 'packages/registry'); +const networkDataFiles = new Set([ + join(packageDir, 'chain-registry/chains.json'), + join(packageDir, 'chain-registry/gas.json'), + join(packageDir, 'chain-registry/ibc_info.json'), + join(packageDir, 'community-assetlist/assetlist.json') +]); +const supportedNetworks = Object.values(CHAIN_IDS); + +const filterNetworkData: Plugin = { + name: 'filter-registry-network-data', + setup(context) { + context.onLoad({ filter: /\.json$/ }, async ({ path }) => { + if (!networkDataFiles.has(path)) { + return; + } + + const source = JSON.parse(await readFile(path, 'utf8')) as Record; + const filtered = Object.fromEntries( + supportedNetworks.map((network) => { + const value = source[network]; + if (value === undefined) { + throw new Error(`${path} is missing supported network ${network}`); + } + return [network, value]; + }) + ); + + return { + contents: JSON.stringify(filtered), + loader: 'json' + }; + }); + } +}; + +await build({ + absWorkingDir: packageDir, + entryPoints: ['src/index.ts'], + bundle: true, + format: 'esm', + platform: 'neutral', + packages: 'external', + target: 'es2020', + outfile: 'dist/index.js', + plugins: [filterNetworkData], + logLevel: 'info' +});